diff --git a/.github/workflows/code_coverage.yml b/.github/workflows/code_coverage.yml index 27f90b37..5cf64923 100644 --- a/.github/workflows/code_coverage.yml +++ b/.github/workflows/code_coverage.yml @@ -29,7 +29,7 @@ jobs: - name: Install GTest run: | dnf config-manager --enable ol8_codeready_builder - dnf install -y gtest-devel + dnf install -y gtest-devel gmock-devel - name: Checkout repository uses: actions/checkout@master diff --git a/.github/workflows/test_linux.yml b/.github/workflows/test_linux.yml index 42637d40..f5e499d1 100644 --- a/.github/workflows/test_linux.yml +++ b/.github/workflows/test_linux.yml @@ -37,6 +37,7 @@ jobs: maven cmake zip + gdb install_gtest: echo gtest already installed conf_pkg: echo package manager already configured install_cmd: install -y @@ -64,7 +65,7 @@ jobs: python3-pip python3-venv install_gtest: | - apt-get install -y libgtest-dev + apt-get install -y libgtest-dev libgmock-dev cd /usr/src/gtest cmake . make @@ -100,7 +101,9 @@ jobs: libX11-devel libXt-devel swig3 - gtest-devel + install_gtest: | + yum install -y http://repo.okay.com.mx/centos/7/x86_64/release/okay-release-1-6.el7.noarch.rpm + yum install -y gtest gtest-devel gmock gmock-devel #-------- RHEL 8-based Only Dependencies ---------------- - cfg: { arch: rhel, arch_ver: 8 } pkg_mgr: dnf @@ -113,12 +116,12 @@ jobs: dnf install -y 'dnf-command(config-manager)' install_gtest: | dnf config-manager --enable powertools - dnf install -y gtest-devel + dnf install -y gtest-devel gmock-devel #-------- OS and Version Specific Dependencies ---------------- - cfg: { os: oraclelinux } install_gtest: | dnf config-manager --enable ol8_codeready_builder - dnf install -y gtest-devel + dnf install -y gtest-devel gmock-devel #-------- Job definition ---------------- runs-on: ubuntu-latest container: docker://${{matrix.cfg.os}}:${{matrix.cfg.tag}} diff --git a/.gitignore b/.gitignore index 2d3bd41f..87af3fb4 100644 --- a/.gitignore +++ b/.gitignore @@ -40,4 +40,6 @@ archive/ trickops_logs/ *.gcda *.gcno -coverage.info \ No newline at end of file +coverage.info +*.dSYM +*.log diff --git a/CMakeLists.txt b/CMakeLists.txt index 99e56a2a..f40375c0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -175,7 +175,7 @@ set( IO_SRC ${CMAKE_BINARY_DIR}/temp_src/io_src/io_JITEvent.cpp ${CMAKE_BINARY_DIR}/temp_src/io_src/io_JITInputFile.cpp ${CMAKE_BINARY_DIR}/temp_src/io_src/io_JSONVariableServer.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_JSONVariableServerThread.cpp + ${CMAKE_BINARY_DIR}/temp_src/io_src/io_JSONVariableServerSessionThread.cpp ${CMAKE_BINARY_DIR}/temp_src/io_src/io_JobData.cpp ${CMAKE_BINARY_DIR}/temp_src/io_src/io_MM4_Integrator.cpp ${CMAKE_BINARY_DIR}/temp_src/io_src/io_MSConnect.cpp @@ -231,7 +231,7 @@ set( IO_SRC ${CMAKE_BINARY_DIR}/temp_src/io_src/io_VariableServer.cpp ${CMAKE_BINARY_DIR}/temp_src/io_src/io_VariableServerListenThread.cpp ${CMAKE_BINARY_DIR}/temp_src/io_src/io_VariableServerReference.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_VariableServerThread.cpp + ${CMAKE_BINARY_DIR}/temp_src/io_src/io_VariableServerSessionThread.cpp ${CMAKE_BINARY_DIR}/temp_src/io_src/io_Zeroconf.cpp ${CMAKE_BINARY_DIR}/temp_src/io_src/io_attributes.cpp ${CMAKE_BINARY_DIR}/temp_src/io_src/io_dllist.cpp diff --git a/Makefile b/Makefile index 48cdf205..0ec17113 100644 --- a/Makefile +++ b/Makefile @@ -95,6 +95,7 @@ UTILS_DIRS := \ ${TRICK_HOME}/trick_source/trick_utils/interpolator \ ${TRICK_HOME}/trick_source/trick_utils/trick_adt \ ${TRICK_HOME}/trick_source/trick_utils/comm \ + ${TRICK_HOME}/trick_source/trick_utils/connection_handlers \ ${TRICK_HOME}/trick_source/trick_utils/shm \ ${TRICK_HOME}/trick_source/trick_utils/math \ ${TRICK_HOME}/trick_source/trick_utils/units \ @@ -105,6 +106,7 @@ UTILS_OBJS := $(addsuffix /object_$(TRICK_HOST_CPU)/*.o ,$(UTILS_DIRS)) # filter out the directories that make their own libraries UTILS_OBJS := $(filter-out ${TRICK_HOME}/trick_source/trick_utils/comm/%, $(UTILS_OBJS)) +UTILS_OBJS := $(filter-out ${TRICK_HOME}/trick_source/trick_utils/connection_handlers/%, $(UTILS_OBJS)) UTILS_OBJS := $(filter-out ${TRICK_HOME}/trick_source/trick_utils/math/%, $(UTILS_OBJS)) UTILS_OBJS := $(filter-out ${TRICK_HOME}/trick_source/trick_utils/units/%, $(UTILS_OBJS)) UTILS_OBJS := $(filter-out ${TRICK_HOME}/trick_source/trick_utils/var_binary_parser/%, $(UTILS_OBJS)) @@ -458,6 +460,7 @@ uninstall: rm -f ${PREFIX}/$(notdir ${TRICK_LIB_DIR})/liber7_utils.a rm -f ${PREFIX}/$(notdir ${TRICK_LIB_DIR})/libtrick.a rm -f ${PREFIX}/$(notdir ${TRICK_LIB_DIR})/libtrick_comm.a + rm -f ${PREFIX}/$(notdir ${TRICK_LIB_DIR})/libtrick_connection_handlers.a rm -f ${PREFIX}/$(notdir ${TRICK_LIB_DIR})/libtrick_math.a rm -f ${PREFIX}/$(notdir ${TRICK_LIB_DIR})/libtrick_mm.a rm -f ${PREFIX}/$(notdir ${TRICK_LIB_DIR})/libtrick_pyip.a diff --git a/docs/developer_docs/Variable-Server.md b/docs/developer_docs/Variable-Server.md new file mode 100644 index 00000000..f2d3e36e --- /dev/null +++ b/docs/developer_docs/Variable-Server.md @@ -0,0 +1,446 @@ +# Variable Server Refactor + +The goals of this refactor were as follows: + +1. Test the Trick Variable Server + - At the start, the variable server was completely uncovered by CI. This can be seen in the earliest builds uploaded to Coveralls (https://coveralls.io/builds/53258598) + - Identified a need for both unit and sim tests + - Significant refactoring was needed to be able to do any unit tests +2. Create a generic representation for a session that can be used in the webserver as well + - Currently, the webserver implementation is completely separate from the variable server, even through they do they same thing. There is a lot of repeated functionality between the two. + - The current implementation of the variable server made it impossible to reuse any of its code. +3. Clarify and document code. + + +## Class Structure + +### VariableServer.hh +The overarching variable server object. It is instantiated in VariableServerSimObject and is intended to be a singleton. + +Responsibilities: +- Initialize a listen thread +- Keep track of all sessions +- Interface with the Trick scheduler through the Trick jobs, and delegate to sessions +- Provide the API for all variable server commands to go through, and delegate to correct session + + +### VariableServerListenThread.hh +Opens a TCP port and listens for new client connections until shut down. Only 1 exists by default as a member of VariableServer, +but additional listen threads can be created through a call to trick.var_server_create_tcp_socket() + +Responsibilities: +- Open a TCP port for listening +- Continuously listen on the port +- Broadcast the port on the multicast channel +- When a client connection comes in, set up the connection, start a thread, and wait for the thread to start the connection + - Delete the thread if the connection fails + +### VariableServerSessionThread.hh +Runs asynchronous parts for the single VariableServerSession. + +Responsibilities: + +- Accept the connection and pass it into the VariableServerSession +- Delete the session and close the connection on shutdown +- Run the parts of VariableServerSession that run asynchronously until user commands shutdown or sim shutdown, including: + - Tell VSSession to read from connection and execute command + - Tell VSSession to copy sim data (if in correct mode) + - Tell VSSession to write data (if in correct mode) + + +### VariableServerSession.hh +Track information and execute commands for a single client session. + +Responsibilities: +- Keep track of the list of variables +- Track desired copy/write mode and timing +- Overrideable behaviors: + - Write out to connection when commanded + - Read in and execute commands from connection when commanded + +### VariableReference.hh +Represent a single variable in a session + +Responsibilities: +- Copy sim data into internal buffer when commanded +- Write sim data into stream when commanded +- Provide functions to write ASCII and binary data +- Track what units the user has requested +- Survive a checkpoint restart + +### TCPClientListener.hh +Represents a listening TCP server + +Responsibilities: +- Encapsulate network code +- Provide an interface to start a server, listen for new connections, and create new connections +- Create a TCPConnection when a client connects + +### ClientConnection.hh + +Responsibilities +Abstract class representing a connection to a client. Inherited classes to handle TCP, UDP, and Multicast connections +- Encapsulate network code +- Provide a generic interface for initializing, starting, shutting down, reading, and writing to network regardless of connection type + +## Threading changes + +### VariableServerListenThread is required to wait for VariableServerSessionThread to notify it when the connection has been accepted. +This was present in old design, but not implemented safely. + +#### Old design: + +``` +VariableServerSessionThread + + wait_for_accept() + - bool connection_accepted = false +``` + +`wait_for_accept` just busy waits on `connection_accepted` with no synch constructs (John Borland would be horrified) +VariableServerListenThread calls `VsThread.wait_for_accept()` + +No error handling if connection failed, which can result in hanging listen thread and extra VSThreads bc of other problems in listen thread + +#### New Design + +``` +VariableServerSessionThread + + enum ConnectionStatus { CONNECTION_PENDING, CONNECTION_SUCCESS, CONNECTION_FAIL }; + + wait_for_accept() + + - ConnectionStatus _connection_status = CONNECTION_PENDING + - pthread_mutex_t _connection_status_mutex + - pthread_cond_t _connection_status_cv + + +VariableServerSessionThread::thread_body + Try to accept connection + if connection fails: + with _connection_status_mutex: + set _connection_status to CONNECTION_FAIL + signal on _connection_status_cv + exit thread + + Do some other setup + with _connection_status_mutex: + set _connection_status to CONNECTION_SUCCESS + signal on _connection_status_cv + + Run loop + + +VariableServerSessionThread::wait_for_accept + with _connection_status_mutex: + while _connection_status == CONNECTION_PENDING: + wait on _connection_status_cv + + return _connection_status + + +VariableServerListenThread::thread_body + When we're trying to set up a connection: + Create new VSThread, set up the connection, start the thread + status = VSThread->wait_for_accept() + if status is CONNECTION_FAIL: + make sure the thread has exited and delete it + +``` + +### VariableServerSessionThread and VariableServerListenThread no longer use pthread_cancel +New termination design: +``` +Additions to Threadbase + + test_shutdown(/* optional exit handler args */) + + thread_shutdown(/* optional exit handler args */) + + - bool cancellable = true + - pthread_mutex_t shutdown_mutex + - bool should_shutdown = false + +ThreadBase::test_shutdown: + with shutdown_mutex + if should_shutdown: + call thread_shutdown + +ThreadBase::thread_shutdown + Call exit handler if one passed + Call pthread_exit(0) + +Addition to function of Threadbase::cancel_thread(): + with shutdown_mutex: Set should_shutdown to true + if cancellable == true, call pthread_cancel +``` + +Threads can set cancellable to false in their constructor, which VariableServerSessionThread and VariableServerListenThread do +Instead, they call test_shutdown at points that would be appropriate to shutdown in their loops +Careful design consideration and testing was taken so that if the threads never start, or various error cases are hit, this still goes ok + + +### VariableServerSessionThread and VariableServerListenThread pause on checkpoint reload + +#### Old design: + +``` +VariableServerSessionThread and VariableServerListenThread + - pthread_mutex_t restart_pause +``` + +This mutex is acquired by the top of the loops while they do their main processing, and released at the bottom of the loop. + +The main thread acquires the mutex in preload_checkpoint jobs and releases in restart jobs. + +This created a few bugs: +- a deadlock in VariableServerSessionThread due to a lock inversion with the VariableServer map mutex +- For some reason, holding the lock while blocked on the select syscall by the VariableServerListenThread could cause some really weird bugs, so the lock was acquired after the select call and only if it returned true. Because of this, select could be run while checkpoint restart was happening, which sometimes resulted in false positives, which created extra VariableServerSessionThreads, and the connection status was never checked so they would never shut down and cause all kinds of problems. + +#### New design + +``` +Addition to SysThread + + void force_thread_to_pause() + + void unpause_thread() + + void test_pause() + + - pthread_mutex_t _restart_pause_mutex + - bool _thread_should_pause + - pthread_cond_t _thread_wakeup_cv + - bool _thread_has_paused + - pthread_cond_t _thread_has_paused_cv +``` + +This design still ensures that thread can be paused, and that the main thread will wait until the threads are paused. It uses condvars to solve the deadlock problem and doesn't cause bugs observed in the old design. + +The main thread can control the SysThread by calling `thread->force_thead_to_pause()`, which will block until the SysThread has paused, and `thread->unpause_thread()`. The SysThread loops must call `test_pause()` at points that would be appropriate to pause. + +SysThread uses a monitor pattern internally to implement this behavior. Pseudocode for the pause managment: + +``` +// Called from main thread +SysThread::force_thread_to_pause() + with _restart_pause_mutex: + _thread_should_pause = true + while !_thread_has_paused: + wait on _thread_has_paused_cv + +// Called from main thread +SysThread::unpause_thread(): + with _restart_pause_mutex: + _thread_should_pause = false + signal on _thread_wakeup_cv + +// Called from SysThread +SysThread::test_pause(): + with _restart_pause_mutex: + if _thread_should_pause: + _thread_has_paused = true + signal on _thread_has_paused_cv + + while _thread_should_pause + wait on _thread_wakeup_cv + + _thread_has_paused = false +``` + +## VariableReference + +The VariableReference class existed in the old version only to hold buffers for copied values, it had no methods. + +In the refactored version, the VariableReference class encapsulates all interaction with the memory manager and sim variables, including copying and formatting the values. VariableReference keeps track of the units that have been requested for this variable in this session. VariableReference provides an interface to write out the value of a variable in either Ascii or binary format. + +It still uses REF2 internally, but the encapsulation will make the (hopefully) impending transition to something else easier to manage. + +The VariableReference class is covered by unit tests. + +``` +VariableReference Interface + + VariableReference(std::string var_name) + + + std::string getName() + + TRICK_TYPE getType() + + + std::string getBaseUnits() + + int setRequestedUnits(std::string new_units) + + + int stageValue () + + int prepareForWrite () + + bool isStaged () + + bool isWriteReady () + + + int writeValueAscii( std::ostream& out ) + + int writeValueBinary( std::ostream& out , bool byteswap = false) + + ((Other methods to write out information about the binary or ascii formats)) +``` + +## VariableServerSession + +The VariableServerSession class is a new class created in this refactor. A lot of the behavior of the old VariableServerSessionThread class is now in this class. + +A VariableServerSession represents a single client session. It tracks the list of variables that are being sent, the copy and write mode, output format mode, cycle, pause state, exit command. + +The VariableServerSession has methods that should be called from Trick job types to implement correct moding - + +``` +// Called from different types of Trick jobs. +// Determines whether this session should be copying and/or writing, and performs those actions if so ++ int copy_and_write_freeze(long long curr_frame); ++ int copy_and_write_freeze_scheduled(long long curr_tics); ++ int copy_and_write_scheduled(long long curr_tics); ++ int copy_and_write_top(long long curr_frame); + +// Called from VariableServerSessionThread ++ virtual int copy_and_write_async(); +``` + +The VariableServerSession has a method that should be called when it is time to handle a message and act on it. This is called from the VariableServerSessionThread loop. +``` ++ virtual int handle_message(); +``` + +The VariableServerSession has the entirety of the documented VariableServer API as methods as well. These are called from the single VariableServer object after looking up the session. + + +The VariableServerSession class is covered by unit tests. + + +## VariableServerSessionThread + +The VariableServerSessionThread class runs and manages the lifetime and synchronization of a VariableServerSessionThread, sets up and closes the connection for the VariableServerSession, and calls any VariableServerSession methods that are designed to be asynchronous with the main thread. This includes `handle_message` and `copy_and_write_async`. It also creates the cycle of the session. + +``` +VariableServerSessionThread_loop.cpp + - void thread_body() + +VariableServerSessionThread.cpp + + void preload_checkpoint() + + void restart() + + + ConnectionStatus wait_for_accept() +``` + +`thread_body()` runs the main loop of the VST. + +`preload_checkpoint()` and `restart()` are called from their respective Trick jobs in the main thread. + + +## VariableServer + +The VariableServer class itself has been the least changed by this refactor. + +Its main job is to delegate between the Trick jobs and the various sessions and threads. The VariableServer is a singleton that is declared as part of a SimObject in `default_trick_sys.sm` +``` +VariableServerSimObject { + VariableServer vs; + + {TRK} ("default_data") vs.default_data() ; + {TRK} P0 ("initialization") trick_ret = vs.init() ; + {TRK} ("preload_checkpoint") vs.suspendPreCheckpointReload(); + {TRK} ("restart") vs.restart(); + {TRK} ("restart") vs.resumePostCheckpointReload(); + {TRK} ("top_of_frame") vs.copy_and_write_top() ; + {TRK} ("automatic_last") vs.copy_and_write_scheduled() ; + + {TRK} ("freeze_init") vs.freeze_init() ; + {TRK} ("freeze_automatic") vs.copy_and_write_freeze_scheduled() ; + {TRK} ("freeze") vs.copy_and_write_freeze() ; + + {TRK} ("shutdown") vs.shutdown() ; +} +``` + +In each job: +- default_data: Initialize the listening socket with any port +- initialization: If the user has requested a different port, set that up. Start the listening thead. +- preload_checkpoint: call methods of all VariableServerListenThreads and VariableServerSessionThreads to pause in preparation for checkpoint +- restart: recreate listening threads if necessary, and call restart methods of all VariableServerListenThreads and VariableServerSessionThreads +- top_of_frame, automatic_last, freeze, freeze_automatic: Loop through all VariableServerSessions and call the appropriate copy_data_ variant +- shutdown: cancel all VariableServerListenThreads and VariableServerSessionThreads + +The VariableServer implements part of the dataflow for commands that come in through the input processor. + +The VariableServer's external API is the entry point of the variable server commands that are passed though the input processor. These commands go to a C-style API, which query the static global singleton VariableServer object. Each of these commands will look up the correct session by pthread_id and call the session's command. + +``` +Client sends `"trick.var_add("my_variable")\n"` +`VariableServerSessionThread::thread_body` calls `VariableServerSession::handle_mesage` +`VariableServerSession::handle_mesage` reads from the client connection and calls `ip_parse("trick.var_add("my_variable")\n")` +Python interpreter runs (on the variable server thread) back through the SWIG layer to invoke global method `var_add("my_variable")` in var_server_ext.cpp +`var_add()` calls the static global variable server object to look up the current session, mapped to the current pthread_id +`var_add()` calls `session->var_add("my_variable")` +session will add my_variable to its list of variables +Return from `ip_parse`, continue `thread_body` processing +``` + +Code snippets (some minor modifications for clarity): + +``` +// var_server_ext.cpp +int var_add(std::string in_name) { + Trick::VariableServerSession * session = get_session(); + + if (session != NULL ) { + session->var_add(in_name) ; + } + return(0) ; +} + +Trick::VariableServerSession * get_session() { + return the_vs->get_session(pthread_self()) ; +} + +// VariableServer.cpp +Trick::VariableServerSession * Trick::VariableServer::get_session(pthread_t thread_id) { + Trick::VariableServerSession * ret = NULL ; + pthread_mutex_lock(&map_mutex) ; + auto it = var_server_sessions.find(thread_id) ; + if ( it != var_server_sessions.end() ) { + ret = (*it).second ; + } + pthread_mutex_unlock(&map_mutex) ; + return ret ; +} + +// VariableServerSession_commands.cpp +int Trick::VariableServerSession::var_add(std::string in_name) { + + VariableReference new_var = new VariableReference(in_name); + _session_variables.push_back(new_var) ; + + return(0) ; +} +``` + +![var_add Sequence Diagram](images/var_add_sequence.png) + +Both var_server_ext.cpp and VariableServerSession_commands.cpp have a method for each command in the user facing variable server API. + +### Special case listen threads and sessions + +The user can request addition TCP, UDP, or multicast servers from the input file. This is handled in the VariableServer class. + +``` +VariableServer { + + int create_tcp_socket(const char * address, unsigned short in_port ) + + int create_udp_socket(const char * address, unsigned short in_port ) + + int create_multicast_socket(const char * mcast_address, const char * address, unsigned short in_port ) +} +``` + +Calling `create_tcp_socket` will create an additional listen thread, which will operate in the same way as the default listen thread. The VariableServer will create a TCPClientListener and start the listen thread, just like it does for the default listen thread. + +`create_udp_socket` will open a UDP socket. Since it does not require a listener, the VariableServer creates a VariableServerSessionThread and a UDPConnection directly, and starts the thread. The output for this session is sent to whatever host a message is received from. + +`create_multicast_socket` will create a UDP socket and add it to a multicast group. It will also create and start a VariableServerSessionThread and MulticastGroup object. The output for this session is broadcast on the multicast address given. + +## ClientConnection and TCPClientListener + +The purpose of these classes are to abstract away all of the network code and the differences between TCP, UDP, and multicast connections for the purpose of the VariableServerSession. + +The VariableServerListenThread uses a TCPClientListener to listen for connections on an open socket, and when one comes in, the TCPClientListener will create a TCPConnection to service the connection. + +In the UDP and Multicast case, the connection is created directly in the VariableServer class and passed into the VariableServerSessionThread. + + +## Class Diagram + +![Variable Server UML](images/vs_uml.png) + diff --git a/docs/developer_docs/images/var_add_sequence.png b/docs/developer_docs/images/var_add_sequence.png new file mode 100644 index 00000000..8ec90ae2 Binary files /dev/null and b/docs/developer_docs/images/var_add_sequence.png differ diff --git a/docs/developer_docs/images/vs_uml.png b/docs/developer_docs/images/vs_uml.png new file mode 100644 index 00000000..f44a799f Binary files /dev/null and b/docs/developer_docs/images/vs_uml.png differ diff --git a/docs/documentation/simulation_capabilities/Status-Message-System.md b/docs/documentation/simulation_capabilities/Status-Message-System.md index 438f0634..5ed2c879 100644 --- a/docs/documentation/simulation_capabilities/Status-Message-System.md +++ b/docs/documentation/simulation_capabilities/Status-Message-System.md @@ -14,9 +14,9 @@ If there are no subscribers, then publishing a message has no effect. ## Message Subscriber There can be any number of Message Subscribers, whose job is to receive (and usually output) published messages. Trick automatically creates three Message Subscribers: -- `Trick::MessageCout` - outputs messages to the standard output stream -- `Trick::MessageFile` - outputs messages to a file named `send_hs` in the RUN directory -- `Trick::MessageTCDevice` - outputs messages to a socket at port 7200, used by the Simulation Control Panel for its Status Messages display +- `Trick::MessageCout` - outputs messages with level < 100 to the standard output stream +- `Trick::MessageHSFile` - outputs messages with level < 100 to a file named `send_hs` in the RUN directory +- `Trick::MessageTCDevice` - outputs messages with level < 100 to a socket at port 7200, used by the Simulation Control Panel for its Status Messages display When you publish a message, it will be output by the three subscribers above. A subscriber can be enabled / disabled at any time during simulation execution to output / ignore messages as desired. @@ -33,16 +33,18 @@ trick_message.mtcout.init() trick.message_subscribe(trick_message.mtcout) ``` -## User accessible routines +## Publish a message To publish a message: ```cpp -int ::message_publish(int level, const char * format_msg, ...) ; -int ::send_hs(FILE * fp, const char * format_msg, ...) ; +#include "trick/message_proto.h" +#include "trick/message_level.h" + +int message_publish(int level, const char * format_msg, ...) ; ``` -The level number can be any integer from 0 to 99. Trick has a few predefined levels (`Trick::MessagePublisher::MESSAGE_TYPE`) that it uses for publishing messages. +The level number can be any number greater than or equal to 0. Levels 0-99 are captured by Trick's default message subscribers. Trick has a few predefined levels (`Trick::MessagePublisher::MESSAGE_TYPE`) that it uses for publishing messages. If the message subscriber's color is enabled (see below), then a particular colored message will be displayed for each of these levels: - 0 - normal message, default color - 1 - informational message, green @@ -50,6 +52,31 @@ If the message subscriber's color is enabled (see below), then a particular colo - 3 - error message, red - 10 - debug message, cyan +## Open a custom log file + +To open a custom message file: + +```cpp +#include "trick/Message_proto.hh" + +int open_custom_message_file(std::string file_name, std::string subscriber_name, int level = -1); +``` + +This function opens a new file, adds it to the list of message subscribers, and returns the level that can be used to write to the file. + +A user can specify a level >= 0. If `open_custom_message_file` is called without a level argument, the function will assign a unique level to the file that is >= 100. If a user wants the messages written to this file to also be captured by default Trick message subscribers, they should specify a level from 0-99. + +Example: +```cpp +// Open the logfile +int my_level = open_custom_message_file("my_log_dir/logfile", "custom_log"); + +// Write to it by publishing a message with the given level +message_publish(my_level, "This message will be written to my custom logfile"); +``` + +## User accessible routines + To subscribe / unsubscribe Trick's default subscribers (these call `::message_subscribe` / `::message_unsubscribe` mentioned above): By default these are all subscribed. You can use subscribe/unsubscribe throughout a simulation to turn on/off messages at will. diff --git a/docs/documentation/simulation_capabilities/Threads.md b/docs/documentation/simulation_capabilities/Threads.md index c032840b..7fb3daf5 100644 --- a/docs/documentation/simulation_capabilities/Threads.md +++ b/docs/documentation/simulation_capabilities/Threads.md @@ -12,7 +12,7 @@ A trick sim is a multi-threaded process, and all of the threads that are created | MessageTCDeviceListenThread | `trick_message.mdevice.get_listen_thread()` | | MessageThreadedCout | `trick_message.mtcout` | | DRDWriterThread | `trick_data_record.drd.drd_writer_thread` | -| VariableServerThread | `trick_vs.vs.get_vst(pthread_t thread_id)` | +| VariableServerSessionThread | `trick_vs.vs.get_vst(pthread_t thread_id)` | ## ThreadBase Methods diff --git a/docs/documentation/simulation_capabilities/Variable-Server.md b/docs/documentation/simulation_capabilities/Variable-Server.md index ef6d112c..ba739093 100644 --- a/docs/documentation/simulation_capabilities/Variable-Server.md +++ b/docs/documentation/simulation_capabilities/Variable-Server.md @@ -39,7 +39,7 @@ int set_var_server_info_msg_on(); ``` These commands are also for toggling information messages from the variable server (i.e., commands received from ALL clients). -The messages only go to a dedicated "varserver_log" file in the RUN directory. +The messages only go to a dedicated `varserver_log` file in the RUN directory. The variable server log capability is off by default. ```c @@ -47,6 +47,16 @@ int set_var_server_log_off(); int set_var_server_log_on(); ``` +These commands are also for toggling individual variable server session logs. +Each log records the IP and port number of the client that connected and every message received. +These logs go into a subdirectory under the RUN direcory called `sesssion_logs`, and the files are named `VSSession.log` +The variable server session log capability is off by default. + +```c +int set_var_server_session_log_off(); +int set_var_server_session_log_on(); +``` + ### Getting and Setting the Variable Server Port Information To set the variable server port to a fixed number in the input file use var_server_set_port() diff --git a/include/trick/ClientConnection.hh b/include/trick/ClientConnection.hh new file mode 100644 index 00000000..2eb5dcf7 --- /dev/null +++ b/include/trick/ClientConnection.hh @@ -0,0 +1,51 @@ +/************************************************************************ +PURPOSE: (Abstract base class for a connection to a client. Should +be inherited by variable server and web server connections. ) +LIBRARY DEPENDENCIES: + () ) +**************************************************************************/ + +#ifndef CLIENT_CONNECTION_HH +#define CLIENT_CONNECTION_HH + +#include + +namespace Trick { + class ClientConnection { + public: + + virtual ~ClientConnection() { } + + static const unsigned int MAX_CMD_LEN = 200000 ; + + enum ConnectionType { TCP, UDP, MCAST, WS } ; + + // Pure virtual methods + virtual int start() = 0; + + virtual int write (const std::string& message) = 0; + virtual int write (char * message, int size) = 0; + + virtual int read (std::string& message, int max_len = MAX_CMD_LEN) = 0; + + virtual int setBlockMode (bool blocking) = 0; + virtual int disconnect () = 0; + + virtual bool isInitialized() = 0; + + virtual std::string getClientTag () = 0; + virtual int setClientTag (std::string tag) = 0; + + virtual int restart() = 0; + + virtual std::string getClientHostname() = 0; + virtual int getClientPort() = 0; + + protected: + ConnectionType _connection_type; + std::string _client_tag; + + }; +} + +#endif \ No newline at end of file diff --git a/include/trick/CommandLineArguments.hh b/include/trick/CommandLineArguments.hh index fad9fd12..ab250ef6 100644 --- a/include/trick/CommandLineArguments.hh +++ b/include/trick/CommandLineArguments.hh @@ -139,6 +139,7 @@ namespace Trick { */ void set_output_dir(std::string output_directory) ; + // TODO: Move elsewhere // Helper method to create full path static int create_path(const std::string& dirname); diff --git a/include/trick/MessageCustomFile.hh b/include/trick/MessageCustomFile.hh new file mode 100644 index 00000000..e62a4299 --- /dev/null +++ b/include/trick/MessageCustomFile.hh @@ -0,0 +1,60 @@ +/* + PURPOSE: + (Custom log file writable by message_publish.) + ASSUMPTIONS AND LIMITATIONS: + (()) + PROGRAMMERS: + (((Jackie Deans) (LinCom) (4/23) (--) (CACI)) +*/ + +#ifndef MESSAGE_CUSTOM_FILE_HH +#define MESSAGE_CUSTOM_FILE_HH + +#include +#include +#include +#include +#include "trick/MessageFile.hh" + +namespace Trick { + + /** + * MessageCustomFile writes all messages its level to the given file + */ + class MessageCustomFile : public MessageFile { + + public: + + /** + @brief The constructor. + */ + MessageCustomFile() ; + + /** + @brief Output message to the file. + */ + virtual void update( unsigned int level , std::string header , std::string message ); + + /** + * @brief Get the level + * + */ + virtual int get_level() const; + virtual void set_level(int level); + + virtual void set_name(std::string name); + virtual void set_file_name(std::string file_name); + + + protected: + int _this_level; + + static int level_counter; + + + } ; + +} + +#endif + diff --git a/include/trick/MessageCustomManager.hh b/include/trick/MessageCustomManager.hh new file mode 100644 index 00000000..df16ad72 --- /dev/null +++ b/include/trick/MessageCustomManager.hh @@ -0,0 +1,53 @@ +/* + PURPOSE: + (Track custom log files.) + ASSUMPTIONS AND LIMITATIONS: + (()) + PROGRAMMERS: + (((Jackie Deans) (CACI) (4/23) (--) ()) +*/ + +#ifndef MESSAGE_CUSTOM_MANAGER_HH +#define MESSAGE_CUSTOM_MANAGER_HH + +#include +#include +#include +#include +#include "trick/MessageCustomFile.hh" +#include "trick/MessageSubscriber.hh" + + +namespace Trick { + + /** + * MessageCustomFile writes all messages its level to the given file + */ + class MessageCustomManager : public MessageSubscriber { + + public: + + /** + @brief The constructor. + */ + MessageCustomManager() ; + virtual ~MessageCustomManager() ; + + + virtual int open_custom_message_file(std::string file_name, std::string subscriber_name, int level = -1); + + /** + @brief Output message to the file. + */ + virtual void update( unsigned int level , std::string header , std::string message ); + + int restart(); + + protected: + std::vector _custom_message_files; + } ; + +} + +#endif + diff --git a/include/trick/MessageFile.hh b/include/trick/MessageFile.hh index de21a88b..35fd8c71 100644 --- a/include/trick/MessageFile.hh +++ b/include/trick/MessageFile.hh @@ -32,7 +32,7 @@ namespace Trick { public: /** The file name of a file which the messages goes to. \n*/ - std::string file_name ; /**< trick_units(--) trick_io(*i) */ + std::string file_name ; /**< trick_units(--) trick_io(io) */ /** @brief The constructor. @@ -47,7 +47,7 @@ namespace Trick { /** @brief Output message to the file. */ - virtual void update( unsigned int level , std::string header , std::string message ) ; + virtual void update( unsigned int level , std::string header , std::string message ); /** @brief Set a file name for a file which the messages received by this subscriber goes to. @@ -61,6 +61,9 @@ namespace Trick { */ virtual int init() ; + virtual int restart() ; + + protected: /** The output file stream. \n */ std::fstream out_stream ; /**< trick_io(**) */ diff --git a/include/trick/MessageHSFile.hh b/include/trick/MessageHSFile.hh new file mode 100644 index 00000000..e9ea4e7b --- /dev/null +++ b/include/trick/MessageHSFile.hh @@ -0,0 +1,47 @@ +/* + PURPOSE: + (Trick runtime simulation executive parameter definition.) + REFERENCE: + (((Bailey, R.W, and Paddock, E.J.) (Trick Simulation Environment) + (NASA:JSC #37943) + (JSC / Engineering Directorate / Automation and Robotics Division) + (June 1994) (--))) + ASSUMPTIONS AND LIMITATIONS: + ((Only 64 levels of nested input data file inclusion.)) + PROGRAMMERS: + (((Robert W. Bailey) (LinCom) (4/92) (--) (Realtime)) + ((Robert W. Bailey) (LinCom) (6/1/91) (Trick-CR-00000) (Initial Release))) +*/ + +#ifndef MESSAGE_HS_FILE_HH +#define MESSAGE_HS_FILE_HH + +#include +#include +#include +#include "trick/MessageFile.hh" + +namespace Trick { + + /** + * MessageHSFile writes all messages of levels 0-99 to the send_hs file + */ + class MessageHSFile : public MessageFile { + + public: + /** + @brief The constructor. + */ + MessageHSFile() ; + + /** + @brief Output message to the file. + */ + virtual void update( unsigned int level , std::string header , std::string message ); + + } ; + +} + +#endif + diff --git a/include/trick/MessagePublisher.hh b/include/trick/MessagePublisher.hh index 70d61430..f22d364f 100644 --- a/include/trick/MessagePublisher.hh +++ b/include/trick/MessagePublisher.hh @@ -41,6 +41,7 @@ namespace Trick { @brief The constructor. */ MessagePublisher() ; + virtual ~MessagePublisher(); /** @brief Initialization job. Sets tics_per_sec and print format. @@ -66,7 +67,7 @@ namespace Trick { @param message - the text of the message @return always 0 */ - int publish(int level, std::string message) ; + virtual int publish(int level, std::string message) ; /** @brief gets the subscriber from the list diff --git a/include/trick/Message_proto.hh b/include/trick/Message_proto.hh index 95b57a7c..194901c4 100644 --- a/include/trick/Message_proto.hh +++ b/include/trick/Message_proto.hh @@ -22,5 +22,7 @@ int message_subscribe( Trick::MessageSubscriber * in_ms ) ; int message_unsubscribe( Trick::MessageSubscriber * in_ms ) ; +int open_custom_message_file(std::string file_name, std::string subscriber_name, int level = -1); + #endif diff --git a/include/trick/Mock/MockClientConnection.hh b/include/trick/Mock/MockClientConnection.hh new file mode 100644 index 00000000..83b8d0c0 --- /dev/null +++ b/include/trick/Mock/MockClientConnection.hh @@ -0,0 +1,24 @@ +#ifndef MOCK_CLIENT_CONNECTION_HH +#define MOCK_CLIENT_CONNECTION_HH + +#include "trick/ClientConnection.hh" +#include + +class MockClientConnection : public Trick::ClientConnection { + public: + MOCK_METHOD0(start, int()); + MOCK_METHOD1(write, int(const std::string& message)); + MOCK_METHOD2(write, int(char * message, int size)); + MOCK_METHOD2(read, int(std::string& message, int max_len)); + MOCK_METHOD0(disconnect, int()); + MOCK_METHOD1(setBlockMode, int(bool blocking)); + MOCK_METHOD0(isInitialized, bool()); + MOCK_METHOD0(restart, int()); + MOCK_METHOD0(getClientTag, std::string()); + MOCK_METHOD1(setClientTag, int(std::string tag)); + MOCK_METHOD0(getClientHostname, std::string()); + MOCK_METHOD0(getClientPort, int()); + +}; + +#endif \ No newline at end of file diff --git a/include/trick/Mock/MockExecutive.hh b/include/trick/Mock/MockExecutive.hh new file mode 100644 index 00000000..703aa215 --- /dev/null +++ b/include/trick/Mock/MockExecutive.hh @@ -0,0 +1,13 @@ +#ifndef MOCK_EXECUTIVE_HH +#define MOCK_EXECUTIVE_HH + +#include "trick/Executive.hh" +#include + +class MockExecutive : public Trick::Executive { + public: + MOCK_METHOD0(get_time_tics, long long()); + MOCK_METHOD0(get_sim_time, double()); +}; + +#endif \ No newline at end of file diff --git a/include/trick/Mock/MockInputProcessor.hh b/include/trick/Mock/MockInputProcessor.hh new file mode 100644 index 00000000..a678176e --- /dev/null +++ b/include/trick/Mock/MockInputProcessor.hh @@ -0,0 +1,13 @@ +#ifndef MOCK_INPUT_PROCESSOR_HH +#define MOCK_INPUT_PROCESSOR_HH + +#include "trick/InputProcessor.hh" +#include + + +class MockInputProcessor : public Trick::InputProcessor { + public: + MOCK_METHOD1(parse, int(std::string in_string)); +}; + +#endif \ No newline at end of file diff --git a/include/trick/Mock/MockMessageCustomManager.hh b/include/trick/Mock/MockMessageCustomManager.hh new file mode 100644 index 00000000..8c730543 --- /dev/null +++ b/include/trick/Mock/MockMessageCustomManager.hh @@ -0,0 +1,13 @@ +#ifndef MOCK_MESSAGE_CUSTOM_MANAGER_HH +#define MOCK_MESSAGE_CUSTOM_MANAGER_HH + +#include "trick/MessageCustomManager.hh" +#include + +class MockMessageCustomManager : public Trick::MessageCustomManager { + public: + MOCK_METHOD3(update, void(int level, std::string header, std::string message)); + MOCK_METHOD3(open_custom_message_file, int(std::string file_name, std::string subscriber_name, int level)); +}; + +#endif \ No newline at end of file diff --git a/include/trick/Mock/MockMessagePublisher.hh b/include/trick/Mock/MockMessagePublisher.hh new file mode 100644 index 00000000..3cda026d --- /dev/null +++ b/include/trick/Mock/MockMessagePublisher.hh @@ -0,0 +1,12 @@ +#ifndef MOCK_MESSAGE_PUBLISHER_HH +#define MOCK_MESSAGE_PUBLISHER_HH + +#include "trick/MessagePublisher.hh" +#include + +class MockMessagePublisher : public Trick::MessagePublisher { + public: + MOCK_METHOD2(publish, int(int level, std::string message)); +}; + +#endif \ No newline at end of file diff --git a/include/trick/Mock/MockMulticastGroup.hh b/include/trick/Mock/MockMulticastGroup.hh new file mode 100644 index 00000000..275eefa3 --- /dev/null +++ b/include/trick/Mock/MockMulticastGroup.hh @@ -0,0 +1,18 @@ +#ifndef MOCK_MULTICAST_GROUP_HH +#define MOCK_MULTICAST_GROUP_HH + +#include "trick/MulticastGroup.hh" + +#include + +class MockMulticastGroup : public Trick::MulticastGroup { + public: + MOCK_METHOD1(broadcast, int(std::string)); + MOCK_METHOD2(addAddress, int(std::string, int)); + MOCK_METHOD0(restart, int()); + MOCK_METHOD0(isInitialized, bool()); + MOCK_METHOD0(initialize, int()); + MOCK_METHOD0(disconnect, int()); +}; + +#endif \ No newline at end of file diff --git a/include/trick/Mock/MockRealtimeSync.hh b/include/trick/Mock/MockRealtimeSync.hh new file mode 100644 index 00000000..6b841634 --- /dev/null +++ b/include/trick/Mock/MockRealtimeSync.hh @@ -0,0 +1,21 @@ +#ifndef MOCK_REALTIME_SYNC_HH +#define MOCK_REALTIME_SYNC_HH + +#include "trick/GetTimeOfDayClock.hh" +#include "trick/RealtimeSync.hh" +#include + +// This sucks but refactoring this is out of the scope for now +Trick::GetTimeOfDayClock& my_static_clock() { + static Trick::GetTimeOfDayClock clock; + return clock; +} + +class MockRealtimeSync : public Trick::RealtimeSync { + public: + MockRealtimeSync() : RealtimeSync(&(my_static_clock())) {} + + MOCK_METHOD0(is_active, bool()); +}; + +#endif \ No newline at end of file diff --git a/include/trick/Mock/MockTCPClientListener.hh b/include/trick/Mock/MockTCPClientListener.hh new file mode 100644 index 00000000..cf457d06 --- /dev/null +++ b/include/trick/Mock/MockTCPClientListener.hh @@ -0,0 +1,24 @@ +#ifndef MOCK_CLIENT_LISTENER_HH +#define MOCK_CLIENT_LISTENER_HH + +#include "trick/TCPClientListener.hh" + +#include + +class MockTCPClientListener : public Trick::TCPClientListener { + public: + MOCK_METHOD2(initialize, int(std::string, int)); + MOCK_METHOD0(initialize, int()); + MOCK_METHOD1(setBlockMode, int(bool)); + MOCK_METHOD0(checkForNewConnections, bool()); + MOCK_METHOD0(setUpNewConnection, Trick::TCPConnection*()); + MOCK_METHOD0(disconnect, int()); + MOCK_METHOD0(checkSocket, int()); + MOCK_METHOD1(validateSourceAddress, bool(std::string)); + MOCK_METHOD0(isInitialized, bool()); + MOCK_METHOD0(restart, int()); + MOCK_METHOD0(getHostname, std::string()); + MOCK_METHOD0(getPort, int()); +}; + +#endif \ No newline at end of file diff --git a/include/trick/Mock/MockTCPConnection.hh b/include/trick/Mock/MockTCPConnection.hh new file mode 100644 index 00000000..37a28456 --- /dev/null +++ b/include/trick/Mock/MockTCPConnection.hh @@ -0,0 +1,21 @@ +#ifndef MOCK_TCP_CONNECTION_HH +#define MOCK_TCP_CONNECTION_HH + +#include "trick/TCPConnection.hh" +#include + +class MockTCPConnection : public Trick::TCPConnection { + public: + MOCK_METHOD0(start, int()); + MOCK_METHOD1(write, int(const std::string& message)); + MOCK_METHOD2(write, int(char * message, int size)); + MOCK_METHOD2(read, int(std::string& message, int max_len)); + MOCK_METHOD0(disconnect, int()); + MOCK_METHOD1(setBlockMode, int(bool blocking)); + MOCK_METHOD0(isInitialized, bool()); + MOCK_METHOD0(restart, int()); + MOCK_METHOD0(getClientTag, std::string()); + MOCK_METHOD1(setClientTag, int(std::string tag)); +}; + +#endif \ No newline at end of file diff --git a/include/trick/Mock/MockVariableServerSession.hh b/include/trick/Mock/MockVariableServerSession.hh new file mode 100644 index 00000000..08e7c61b --- /dev/null +++ b/include/trick/Mock/MockVariableServerSession.hh @@ -0,0 +1,34 @@ +#ifndef MOCK_VARIABLE_SERVER_SESSION_HH +#define MOCK_VARIABLE_SERVER_SESSION_HH + +#include "trick/VariableServerSession.hh" +#include + +class MockVariableServerSession : public Trick::VariableServerSession { + public: + MOCK_METHOD0(handle_message, int()); + MOCK_METHOD0(write_data, int()); + MOCK_METHOD0(get_exit_cmd, bool()); + MOCK_METHOD0(get_pause, bool()); + MOCK_CONST_METHOD0(get_enabled, bool()); + MOCK_CONST_METHOD0(get_write_mode, VS_WRITE_MODE()); + MOCK_CONST_METHOD0(get_copy_mode, VS_COPY_MODE()); + MOCK_METHOD1(set_log, int(bool on_off)); + MOCK_METHOD1(set_session_log, int(bool on_off)); + MOCK_METHOD0(copy_sim_data, int()); + MOCK_CONST_METHOD0(get_update_rate, double()); + MOCK_CONST_METHOD0(get_frame_multiple, int()); + MOCK_CONST_METHOD0(get_frame_offset, int()); + MOCK_CONST_METHOD0(get_freeze_frame_multiple, int()); + MOCK_CONST_METHOD0(get_freeze_frame_offset, int()); + MOCK_CONST_METHOD0(get_next_tics, long long ()); + MOCK_CONST_METHOD0(get_freeze_next_tics, long long()); + MOCK_METHOD0(set_exit_cmd, void()); + + MOCK_METHOD0(copy_and_write_async, int()); + + // Accessor for the concrete version + int copy_and_write_async_concrete() { return Trick::VariableServerSession::copy_and_write_async(); } +}; + +#endif \ No newline at end of file diff --git a/include/trick/MulticastGroup.hh b/include/trick/MulticastGroup.hh new file mode 100644 index 00000000..c6095b3e --- /dev/null +++ b/include/trick/MulticastGroup.hh @@ -0,0 +1,55 @@ +/* + PURPOSE: ( Encapsulate multicast functionality. ) +*/ +#ifndef MULTICAST_GROUP_HH +#define MULTICAST_GROUP_HH + +#include +#include +#include + +#include +#include + +namespace Trick { + class MulticastGroup : public UDPConnection { + public: + MulticastGroup(); + MulticastGroup (SystemInterface * system_interface); + + virtual ~MulticastGroup(); + + // Multicast specific functions + int initialize_with_receiving(std::string local_addr, std::string mcast_addr, int port); + virtual int initialize(); + + virtual int broadcast (std::string message); + virtual int addAddress (std::string addr, int port); + + // ClientConnection interface + + virtual int write (const std::string& message) override; + virtual int write (char * message, int size) override; + + virtual int read (std::string& message, int max_len = MAX_CMD_LEN) override; + + virtual int disconnect () override; + + virtual bool isInitialized() override; + + virtual int restart() override; + + virtual std::string getClientHostname() override; + virtual int getClientPort() override; + + private: + std::vector _addresses; /**< trick_io(**) Addresses to multicast to. */ + bool _initialized; /**< trick_io(**) Whether this object is ready */ + + struct sockaddr_in _self_info ; /**< trick_io(**) Save self information so we don't process our own messages */ + + SystemInterface * _system_interface; + }; +} + +#endif \ No newline at end of file diff --git a/include/trick/RealtimeSync.hh b/include/trick/RealtimeSync.hh index e828882f..de0b2826 100644 --- a/include/trick/RealtimeSync.hh +++ b/include/trick/RealtimeSync.hh @@ -256,6 +256,12 @@ namespace Trick { */ virtual int start_sleep_timer(); + /** + @brief Return whether realtime is currently active + @return True if active, false otherwise + */ + virtual bool is_active(); + } ; } ; diff --git a/include/trick/SysThread.hh b/include/trick/SysThread.hh index a565b8c5..e5aab8e4 100644 --- a/include/trick/SysThread.hh +++ b/include/trick/SysThread.hh @@ -33,12 +33,34 @@ namespace Trick { **/ class SysThread : public Trick::ThreadBase { public: - SysThread(std::string in_name, bool self_deleting = false); + SysThread(std::string in_name); ~SysThread(); static int ensureAllShutdown(); + protected: + // Called from the main thread + void force_thread_to_pause(); + // Called from the main thread + void unpause_thread(); + + // Called from the sys_thread at a point that would be appropriate to pause + void test_pause(); + private: + /** Synchronization to safely pause and restart processing during a checkpoint reload */ + pthread_mutex_t _restart_pause_mutex ; /**< trick_io(**) */ + + // For the main thread to tell the sys_thread to pause + bool _thread_should_pause; /**< trick_io(**) */ + // For the main thread to tell the sys_thread to wake up + pthread_cond_t _thread_wakeup_cv; /**< trick_io(**) */ + + // For the main thread to wait for the sys_thread to pause + pthread_cond_t _thread_has_paused_cv; /**< trick_io(**) */ + bool _thread_has_paused; /**< trick_io(**) */ + + // Had to use Construct On First Use here to avoid the static initialziation fiasco static pthread_mutex_t& list_mutex(); static pthread_cond_t& list_empty_cv(); @@ -47,7 +69,6 @@ namespace Trick { static bool shutdown_finished; - bool self_deleting; } ; } diff --git a/include/trick/SystemInterface.hh b/include/trick/SystemInterface.hh new file mode 100644 index 00000000..e05ced9e --- /dev/null +++ b/include/trick/SystemInterface.hh @@ -0,0 +1,57 @@ +/* + PURPOSE: (A wrapper for system calls to facilitate testing.) +*/ + + +#ifndef __SYSTEM_INTERFACE__ +#define __SYSTEM_INTERFACE__ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class SystemInterface { + public: + + virtual int socket (int domain, int type, int protocol) { return ::socket ( domain, type, protocol); } + + virtual int setsockopt (int socket, int level, int option_name, const void * option_value, socklen_t option_len) { return ::setsockopt ( socket, level, option_name, option_value, option_len); } + + virtual int bind (int socket, const struct sockaddr * address, socklen_t address_len) { return ::bind ( socket, address, address_len); } + + virtual int getsockname (int socket, struct sockaddr * address, socklen_t * address_len) { return ::getsockname ( socket, address, address_len); } + + virtual int listen (int socket, int backlog) { return ::listen ( socket, backlog); } + + virtual int select (int nfds, fd_set * readfds, fd_set * writefds, fd_set * errorfds, struct timeval * timeout) { return ::select ( nfds, readfds, writefds, errorfds, timeout); } + + virtual int close (int fildes) { return ::close ( fildes); } + + virtual int getaddrinfo (const char * hostname, const char * servname, const struct addrinfo * hints, struct addrinfo ** res) { return ::getaddrinfo ( hostname, servname, hints, res); } + + virtual int fcntl (int fildes, int cmd, int arg) { return ::fcntl ( fildes, cmd, arg); } + + virtual int shutdown (int socket, int how) { return ::shutdown ( socket, how); } + + virtual int accept (int socket, struct sockaddr * address, socklen_t * address_len) { return ::accept ( socket, address, address_len); } + + virtual ssize_t send (int socket, const void * buffer, size_t length, int flags) { return ::send ( socket, buffer, length, flags); } + + virtual ssize_t sendto (int socket, const void * buffer, size_t length, int flags, const struct sockaddr * dest_addr, socklen_t dest_len) { return ::sendto ( socket, buffer, length, flags, dest_addr, dest_len); } + + virtual ssize_t recv (int socket, void * buffer, size_t length, int flags) { return ::recv ( socket, buffer, length, flags); } + + virtual ssize_t recvfrom (int socket, void * buffer, size_t length, int flags, struct sockaddr * address, socklen_t * address_len) { return ::recvfrom ( socket, buffer, length, flags, address, address_len); } + + virtual in_addr_t inet_addr (const char * cp) { return ::inet_addr ( cp); } + +}; + +#endif diff --git a/include/trick/TCPClientListener.hh b/include/trick/TCPClientListener.hh new file mode 100644 index 00000000..5f6dea05 --- /dev/null +++ b/include/trick/TCPClientListener.hh @@ -0,0 +1,64 @@ +#ifndef CLIENT_LISTENER_HH +#define CLIENT_LISTENER_HH + +/* + PURPOSE: ( Encapsulate a TCP server. ) +*/ + +#include +#include "trick/SystemInterface.hh" +#include "trick/TCPConnection.hh" + +#define LISTENER_ERROR -1 + + +namespace Trick { + + class TCPConnection; + + class TCPClientListener { + public: + + TCPClientListener (); + TCPClientListener (SystemInterface * system_interface); + + virtual ~TCPClientListener (); + + virtual int initialize(std::string hostname, int port); + virtual int initialize(); + + virtual int setBlockMode(bool blocking); + + virtual int setSocketPriority(unsigned short priority); + + virtual bool checkForNewConnections(); + + virtual TCPConnection * setUpNewConnection (); + + virtual int disconnect(); + + virtual int checkSocket(); + + virtual bool validateSourceAddress(std::string source_address); + + virtual bool isInitialized(); + + virtual int restart(); + + virtual std::string getHostname (); + virtual int getPort(); + + protected: + + int _listen_socket; + std::string _hostname; + int _port; + std::string _client_tag; + + bool _initialized; + + SystemInterface * _system_interface; /* ** */ + }; +} + +#endif \ No newline at end of file diff --git a/include/trick/TCPConnection.hh b/include/trick/TCPConnection.hh new file mode 100644 index 00000000..6b34dedd --- /dev/null +++ b/include/trick/TCPConnection.hh @@ -0,0 +1,53 @@ +#ifndef TCP_CONNECTION_HH +#define TCP_CONNECTION_HH + +/* + PURPOSE: ( Encapsulate a TCP connection. ) +*/ + +#include "trick/ClientConnection.hh" +#include "trick/SystemInterface.hh" + +namespace Trick { + + class TCPConnection : public ClientConnection { + public: + + TCPConnection (); + TCPConnection (int listen_socket); + TCPConnection (SystemInterface * system_interface); + TCPConnection (int listen_socket, SystemInterface * system_interface); + + virtual int start() override; + + virtual int write (const std::string& message) override; + virtual int write (char * message, int size) override; + + virtual int read (std::string& message, int max_len = MAX_CMD_LEN) override; + + virtual int disconnect () override; + virtual bool isInitialized() override; + + virtual int setBlockMode(bool blocking) override; + + virtual int restart() override; + + virtual std::string getClientTag () override; + virtual int setClientTag (std::string tag) override; + + virtual std::string getClientHostname() override; + virtual int getClientPort() override; + + private: + int _socket; + bool _connected; + + // This is needed to be able to accept an incoming connection, after being set up by the listener + int _listen_socket; + + SystemInterface * _system_interface; /* ** */ + + }; +} + +#endif \ No newline at end of file diff --git a/include/trick/ThreadBase.hh b/include/trick/ThreadBase.hh index 41ec3273..ffe51ceb 100644 --- a/include/trick/ThreadBase.hh +++ b/include/trick/ThreadBase.hh @@ -131,7 +131,7 @@ namespace Trick { virtual int cancel_thread() ; /** - * Cancels thread. + * Joins thread. * @return always 0 */ virtual int join_thread() ; @@ -153,6 +153,12 @@ namespace Trick { */ virtual void dump( std::ostream & oss = std::cout ) ; + virtual void test_shutdown(); + virtual void test_shutdown(void (*exit_handler) (void *), void * exit_arg); + + virtual void thread_shutdown(); + virtual void thread_shutdown(void (*exit_handler) (void *), void * exit_arg); + protected: /** optional name of thread */ @@ -170,6 +176,14 @@ namespace Trick { /** Set of cpus to use with thread */ unsigned int max_cpu ; /**< trick_io(**) */ + /** Track whether the thread has been created */ + bool created ; /**< trick_io(**) */ + + /** Manage thread shutdown */ + bool should_shutdown; /**< trick_io(**) */ + bool cancellable; /**< trick_io(**) */ + pthread_mutex_t shutdown_mutex; /**< trick_io(**) */ + #if __linux #ifndef SWIG /** Set of cpus to use with thread */ diff --git a/include/trick/UDPConnection.hh b/include/trick/UDPConnection.hh new file mode 100644 index 00000000..c64539d3 --- /dev/null +++ b/include/trick/UDPConnection.hh @@ -0,0 +1,60 @@ +#ifndef UDP_CONNECTION_HH +#define UDP_CONNECTION_HH + +/* + PURPOSE: ( Encapsulate a UDP socket. ) +*/ + +#include "trick/ClientConnection.hh" +#include "trick/SystemInterface.hh" + + +namespace Trick { + + class UDPConnection : public ClientConnection { + public: + + UDPConnection (); + UDPConnection (SystemInterface * system_interface); + + int start() override; + + int write (const std::string& message) override; + int write (char * message, int size) override; + + int read (std::string& message, int max_len = MAX_CMD_LEN) override; + + int disconnect () override; + bool isInitialized() override; + + int setBlockMode(bool block_mode) override; + int restart() override; + + virtual std::string getClientTag () override; + virtual int setClientTag (std::string tag) override; + + // Non-override functions + int initialize(const std::string& hostname, int port); + + int getPort(); + std::string getHostname(); + + virtual std::string getClientHostname() override; + virtual int getClientPort() override; + + protected: + bool _initialized; + bool _started; + int _socket; + + int _port; + std::string _hostname; + + struct sockaddr_in _remote_serv_addr; + + SystemInterface * _system_interface; /* ** */ + }; + +} + +#endif \ No newline at end of file diff --git a/include/trick/VariableReference.hh b/include/trick/VariableReference.hh new file mode 100644 index 00000000..5de953e8 --- /dev/null +++ b/include/trick/VariableReference.hh @@ -0,0 +1,94 @@ +/************************************************************************* +PURPOSE: (A variable server variable reference. Refactor of VariableReference + based on the VariableServerVariable class previously in the webserver.) +**************************************************************************/ +#ifndef VARIABLE_REFERENCE_HH +#define VARIABLE_REFERENCE_HH + +#include +#include + +#include +#include + +#define MAX_ARRAY_LENGTH 4096 + +union cv_converter ; +namespace Trick { + class VariableReference { + + public: + friend std::ostream& operator<< (std::ostream& s, const Trick::VariableReference& ref); + + VariableReference(std::string var_name); + + // Special constructor to deal with time + VariableReference(std::string var_name, double* time); + + ~VariableReference(); + + std::string getName() const; + TRICK_TYPE getType() const; + + std::string getBaseUnits() const; + int setRequestedUnits(std::string new_units); + + // These variables have 2 staging buffers that we can swap between to allow for different copy and write-out modes + // stageValue copies data from the simulation into one of the buffers (stage_buffer) + // prepareForWrite swaps the pointers for stage_buffer and write_buffer + // This way we can have data ready for writing, but also be copying out from the sim at the same time + // stageValue must be called first, and then prepare for write, and then writeValue* can be called. + int stageValue(bool validate_address = false); + int prepareForWrite(); + bool isStaged() const; + bool isWriteReady() const; + + // Write out the value to the given outstream. + // write_ready must be true + int getSizeAscii() const; + int getSizeBinary() const; + int writeValueAscii( std::ostream& out ) const; + int writeValueBinary( std::ostream& out , bool byteswap = false) const; + int writeNameBinary( std::ostream& out, bool byteswap = false) const; + int writeNameLengthBinary( std::ostream& out, bool byteswap = false) const; + int writeSizeBinary( std::ostream& out, bool byteswap = false) const; + int writeTypeBinary( std::ostream& out, bool byteswap = false) const; + + bool validate(); + void tagAsInvalid(); + + // Helper method for byteswapping + static void byteswap_var (char * out, char * in, const VariableReference& ref); + + private: + VariableReference(); + void byteswap_var(char * out, char * in) const; + + // Error refs + static REF2* make_error_ref(std::string in_name); + static REF2* make_do_not_resolve_ref(std::string in_name); + + static int _bad_ref_int; + static int _do_not_resolve_bad_ref_int; + + REF2 * _var_info; + void * _address; // -- address of data copied to buffer + int _size; // -- size of data copied to buffer + bool _deref; // -- indicates whether variable is pointer that needs to be dereferenced + cv_converter * _conversion_factor ; // ** udunits conversion factor + TRICK_TYPE _trick_type ; // -- Trick type of this variable + + bool _staged; + bool _write_ready; + + void *_stage_buffer; + void *_write_buffer; + + std::string _base_units; + std::string _requested_units; + std::string _name; + }; + + std::ostream& operator<< (std::ostream& s, const Trick::VariableReference& ref); +} +#endif diff --git a/include/trick/VariableServer.hh b/include/trick/VariableServer.hh index 10cf4ee2..3f84d685 100644 --- a/include/trick/VariableServer.hh +++ b/include/trick/VariableServer.hh @@ -16,7 +16,7 @@ #include "trick/reference.h" #include "trick/JobData.hh" #include "trick/variable_server_sync_types.h" -#include "trick/VariableServerThread.hh" +#include "trick/VariableServerSessionThread.hh" #include "trick/VariableServerListenThread.hh" #include "trick/SysThread.hh" @@ -85,39 +85,55 @@ namespace Trick { /** @brief Copies client variable values at the top of the frame. */ - int copy_data_top() ; + int copy_and_write_top() ; /** @brief The function to copy client variable values to their output buffers when in sync mode. */ - int copy_data_scheduled() ; + int copy_and_write_scheduled() ; /** @brief The function to copy client variable values to their output buffers when in sync mode. */ - int copy_data_freeze_scheduled() ; + int copy_and_write_freeze_scheduled() ; /** @brief Copies client variable values at the top of the frame. */ - int copy_data_freeze() ; + int copy_and_write_freeze() ; /** @brief Adds a vst to the map. */ - void add_vst(pthread_t thread_id, VariableServerThread * in_vst ) ; + void add_vst(pthread_t thread_id, VariableServerSessionThread * in_vst ) ; + + /** + @brief Adds a vst to the map. + */ + void add_session(pthread_t thread_id, VariableServerSession * in_session ) ; /** @brief Get a vst mapped by thread id - @return the VariableServerThread mapped to the thread id if found, or NULL if not found. + @return the VariableServerSessionThread mapped to the thread id if found, or NULL if not found. */ - Trick::VariableServerThread * get_vst(pthread_t thread_id) ; + Trick::VariableServerSessionThread * get_vst(pthread_t thread_id) ; + + /** + @brief Get a session mapped by thread id + @return the VariableServerSession mapped to the thread id if found, or NULL if not found. + */ + Trick::VariableServerSession * get_session(pthread_t thread_id) ; /** @brief Delete a vst in the map */ void delete_vst(pthread_t thread_id) ; + /** + @brief Delete a session in the map + */ + void delete_session(pthread_t thread_id) ; + /** @brief @userdesc Return host name from the listen device. @par Python Usage: @@ -158,6 +174,11 @@ namespace Trick { */ bool get_log() ; + /** + @brief @userdesc Test if the variable server logging is on. + */ + bool get_session_log() ; + /** @brief @userdesc Command to turn on variable server info messages (e.g., get a message when a command is received from any client). @@ -197,6 +218,24 @@ namespace Trick { */ void set_var_server_log_off() ; + /** + @brief @userdesc Command to turn on variable server session logging for all clients. + Each new session will create a file under RUN_<>/session_logs/ containg the client's IP and port and all messages received. + @par Python Usage: + @code trick.set_var_server_log_on() @endcode + @return always 0 + */ + void set_var_server_session_log_on() ; + + /** + @brief @userdesc Command to turn off variable server session logging for all clients. + No session log will be creeated. + @par Python Usage: + @code trick.set_var_server_log_off() @endcode + @return always 0 + */ + void set_var_server_session_log_off() ; + /** @brief @userdesc Command to open additional variable server listen port. @param source_address - the name or numeric IP of the machine to bind listen socket. NULL or empty @@ -250,9 +289,9 @@ namespace Trick { void set_copy_data_job( Trick::JobData * ) ; /** - @brief Called from the S_define to set the copy_data_freeze_job ptr. + @brief Called from the S_define to set the copy_and_write_freeze_job ptr. */ - void set_copy_data_freeze_job( Trick::JobData * ) ; + void set_copy_and_write_freeze_job( Trick::JobData * ) ; protected: @@ -266,6 +305,9 @@ namespace Trick { to a varserver_log file in the RUN directory.\n */ bool log ; /**< trick_units(--) */ + /** Toggle to turn on/off variable server individual session logs. */ + bool session_log ; /**< trick_units(--) */ + /** Default listen port thread object */ VariableServerListenThread listen_thread ; @@ -273,16 +315,11 @@ namespace Trick { Trick::JobData * copy_data_job ; /**< trick_io(**) trick_units(--) */ /** Pointer to freeze_automatic job that copies requested variable values to their output buffers in sync mode.\n */ - Trick::JobData * copy_data_freeze_job ; /**< trick_io(**) trick_units(--) */ + Trick::JobData * copy_and_write_freeze_job ; /**< trick_io(**) trick_units(--) */ - /** Storage for saved thread pause state. The pause state of each thread is saved - to this map by suspendPreCheckpointReload(). resumePostCheckpointReload() restores - the pause state from this map. - */ - std::map thread_pause_state_store; // ** ignore this - - /** Map thread id to the VariableServerThread object.\n */ - std::map < pthread_t , VariableServerThread * > var_server_threads ; /**< trick_io(**) */ + /** Map thread id to the VariableServerSessionThread object.\n */ + std::map < pthread_t , VariableServerSessionThread * > var_server_threads ; /**< trick_io(**) */ + std::map < pthread_t , VariableServerSession * > var_server_sessions ; /**< trick_io(**) */ /** Mutex to ensure only one thread manipulates the map of var_server_threads\n */ pthread_mutex_t map_mutex ; /**< trick_io(**) */ @@ -295,8 +332,6 @@ namespace Trick { } -int vs_format_ascii(Trick::VariableReference * var, char *value, size_t value_size); - Trick::VariableServer * var_server_get_var_server() ; // external calls to be made available from input processor @@ -331,8 +366,6 @@ int var_set_freeze_frame_multiple(unsigned int mult) ; int var_set_freeze_frame_offset(unsigned int offset) ; int var_byteswap(bool on_off) ; -int var_signal() ; -int var_multicast(bool on_off) ; int var_send_list_size() ; diff --git a/include/trick/VariableServerListenThread.hh b/include/trick/VariableServerListenThread.hh index 29242675..512c6162 100644 --- a/include/trick/VariableServerListenThread.hh +++ b/include/trick/VariableServerListenThread.hh @@ -8,19 +8,24 @@ #include #include -#include "trick/tc.h" +#include "trick/TCPClientListener.hh" #include "trick/SysThread.hh" +#include "trick/MulticastGroup.hh" + namespace Trick { /** This class runs the variable server listen loop. @author Alex Lin + @author Jackie Deans (2023) */ class VariableServerListenThread : public Trick::SysThread { public: VariableServerListenThread() ; + VariableServerListenThread(TCPClientListener * listener); + virtual ~VariableServerListenThread() ; const char * get_hostname() ; @@ -28,10 +33,8 @@ namespace Trick { unsigned short get_port() ; void set_port(unsigned short in_port) ; -#ifndef __APPLE__ void set_socket_priority(unsigned short in_socket_priority) ; - void apply_socket_priority(TCDevice *listen_device, TCDevice *device); -#endif + void apply_socket_priority(); std::string get_user_tag() ; const std::string& get_user_tag_ref() ; @@ -46,8 +49,6 @@ namespace Trick { virtual int init_listen_device() ; virtual int check_and_move_listen_device() ; - void create_tcp_socket(const char * address, unsigned short in_port ) ; - virtual void * thread_body() ; int restart() ; @@ -56,37 +57,39 @@ namespace Trick { void pause_listening() ; void restart_listening() ; + void set_multicast_group (MulticastGroup * group); + virtual void dump( std::ostream & oss = std::cout ) ; protected: + void initializeMulticast(); + /** Requested variable server source address\n */ - std::string source_address ; /**< trick_units(--) */ + std::string _requested_source_address ; /**< trick_units(--) */ /** Requested variable server port number.\n */ - unsigned short port ; /**< trick_units(--) */ + unsigned short _requested_port ; /**< trick_units(--) */ /** User requested specific port number\n */ - bool user_port_requested ; /**< trick_units(--) */ + bool _user_requested_address ; /**< trick_units(--) */ /** User defined unique tag to easily identify this variable server port.\n */ - std::string user_tag; /**< trick_units(--) */ + std::string _user_tag; /**< trick_units(--) */ /** Turn on/off broadcasting of variable server port.\n */ - bool broadcast ; /**< trick_units(--) */ + bool _broadcast ; /**< trick_units(--) */ - /** The listen device\n */ - TCDevice listen_dev; /**< trick_io(**) */ + /** The listen device */ + TCPClientListener * _listener; /**< trick_io(**) trick_units(--) */ - /** The mutex to stop accepting new connections during restart\n */ - pthread_mutex_t restart_pause ; /**< trick_io(**) */ + /* Multicast broadcaster */ + MulticastGroup * _multicast; /**< trick_io(**) trick_units(--) */ -#ifndef __APPLE__ /** Requested variable server socket priority.\n */ - unsigned short socket_priority ; /**< trick_units(--) */ + unsigned short _socket_priority ; /**< trick_units(--) */ /** User requested specific socket priority\n */ - bool user_socket_priority_requested ; /**< trick_units(--) */ -#endif + bool _user_socket_priority_requested ; /**< trick_units(--) */ } ; diff --git a/include/trick/VariableServerReference.hh b/include/trick/VariableServerReference.hh deleted file mode 100644 index 6becfd27..00000000 --- a/include/trick/VariableServerReference.hh +++ /dev/null @@ -1,44 +0,0 @@ -/* - PURPOSE: - (VariableServerReference) -*/ - -#ifndef VARIABLESERVERREFERENCE_HH -#define VARIABLESERVERREFERENCE_HH - -#include -#include "trick/reference.h" - -union cv_converter ; - -#define MAX_ARRAY_LENGTH 4096 - -namespace Trick { - -/** - This class provides reference information for variables requested from the variable server by the client. - @author Alex Lin - */ - - class VariableReference { - public: - VariableReference(REF2 * in_ref) ; - ~VariableReference() ; - - friend std::ostream& operator<< (std::ostream& s, const Trick::VariableReference& vref); - - /** Pointer to trick variable reference structure.\n */ - REF2 * ref ; - cv_converter * conversion_factor ; // ** udunits conversion factor - void * buffer_in ; - void * buffer_out ; - void * address ; // -- address of data copied to buffer - int size ; // -- size of data copied to buffer - TRICK_TYPE string_type ; // -- indicate if this is a string or wstring - bool need_deref ; // -- inidicate this is a painter to be dereferenced - } ; - -} - -#endif - diff --git a/include/trick/VariableServerSession.hh b/include/trick/VariableServerSession.hh new file mode 100644 index 00000000..88c5fdea --- /dev/null +++ b/include/trick/VariableServerSession.hh @@ -0,0 +1,541 @@ +/************************************************************************* +PURPOSE: (Represent the state of a variable server connection.) +**************************************************************************/ + +#ifndef VSSESSION_HH +#define VSSESSION_HH + +#include +#include + +#include "trick/VariableReference.hh" +#include "trick/ClientConnection.hh" +#include "trick/variable_server_sync_types.h" +#include "trick/tc.h" +#include "trick/variable_server_message_types.h" + + +namespace Trick { + class VariableServerSession { + public: + VariableServerSession(); + + virtual ~VariableServerSession(); + + friend std::ostream& operator<< (std::ostream& s, const Trick::VariableServerSession& session); + + /** + * @brief Set the connection object + * + * @param connection - a pointer to a ClientConnection that is initialized and ready to go + */ + virtual void set_connection(ClientConnection * connection); + + /** + @brief Read a message from the connection and act on it + */ + virtual int handle_message(); + + /** + @brief Get the pause state of this thread. + */ + virtual bool get_pause() ; + + /** + @brief Set the pause state of this thread. + */ + virtual void set_pause(bool on_off) ; + + /** + @brief Get the exit command of this session. + */ + virtual bool get_exit_cmd() ; + + /** + @brief Command session to exit + */ + virtual void set_exit_cmd() ; + + /** + @brief gets the send_stdio flag. + */ + virtual bool get_send_stdio() ; + + /** + @brief sets the send_stdio flag. + */ + virtual int set_send_stdio(bool on_off) ; + + // Called from different types of Trick jobs. + // Determines whether this session should be copying at that time, and calls internal copy methods if so + int copy_and_write_freeze(long long curr_frame); + int copy_and_write_freeze_scheduled(long long curr_tics); + int copy_and_write_scheduled(long long curr_tics); + int copy_and_write_top(long long curr_frame); + + // Called from VariableServerSessionThread + virtual int copy_and_write_async(); + + /** + @brief Copy given variable values from Trick memory to each variable's output buffer. + cyclical indicated whether it is a normal cyclical copy or a send_once copy + */ + virtual int copy_sim_data(std::vector& given_vars, bool cyclical); + virtual int copy_sim_data(); + + /** + @brief Write data from the given var only to the appropriate format (var_ascii or var_binary) from variable output buffers to socket. + */ + virtual int write_data(std::vector& var, VS_MESSAGE_TYPE message_type) ; + virtual int write_data(); + + int write_stdio(int stream, std::string text); + + void disconnect_references(); + + virtual long long get_next_tics() const; + + virtual long long get_freeze_next_tics() const; + + int freeze_init(); + + virtual double get_update_rate() const; + + void pause_copy(); + void unpause_copy(); + + virtual VS_WRITE_MODE get_write_mode () const; + virtual VS_COPY_MODE get_copy_mode () const; + + + /************************************************************************************************/ + /* Variable Server Interface */ + /************************************************************************************************/ + + /** + @brief @userdesc Command to add a variable to a list of registered variables for value retrieval. + The variable server will immediately begin returning the variable values to the client at a + frequency according to var_cycle. + @par Python Usage: + @code trick.var_add("") @endcode + @param in_name - the variable name to retrieve + @return always 0 + */ + virtual int var_add( std::string in_name ) ; + + /** + @brief @userdesc Command to add a variable to a list of registered variables for value retrieval, + and also instructs the variable server to return the value in the specified measurement units. + The unit specification will be returned after the variable's value (var_ascii mode only). + Specifying units as "{xx}" will use the unit specification from the variable's model code declaration, + which is essentially a method of querying a variable's unit specification. + The variable server will immediately begin returning the variable values/units to the client at + a frequency according to var_cycle. + @par Python Usage: + @code trick.var_add("", "") @endcode + @param in_name - the variable name to retrieve + @param units_name - the desired units, specified within curly braces + @return always 0 + */ + virtual int var_add( std::string in_name, std::string units_name ) ; + + /** + @brief @userdesc Command to remove a variable (previously registered with var_add) + from the list of registered variables for value retrieval. + @par Python Usage: + @code trick.var_remove("") @endcode + @param in_name - the variable name to remove + @return always 0 + */ + virtual int var_remove( std::string in_name ) ; + + /** + @brief @userdesc Command to instruct the variable server to return the value of a variable + in the specified units (var_ascii mode only). + The variable must have been previously registered with the var_add command. + The unit specification will be returned after the variable's value. + @par Python Usage: + @code trick.var_units("", "") @endcode + @param var_name - the variable name previously registered with var_add + @param units_name - the desired units, specified within curly braces + @return always 0 + @note trick.var_add("my_object.my_variable"); trick.var_units("my_object.my_variable","{m}") + is the same as trick.var_add("my_object.my_variable","{m}") + */ + virtual int var_units(std::string var_name , std::string units_name) ; + + /** + @brief @userdesc Command to instruct the variable server to send a Boolean value indicating + whether the specified variable exists + (is a Trick processed variable in the current simulation). + - var_binary mode: the message indicator is 1, and the returned value is a binary 0 or 1. + - var_ascii mode: the message indicator is "1" followed by a tab, then an ASCII "0" or "1" returned value. + . + @par Python Usage: + @code trick.var_exists("") @endcode + @param in_name - the variable name to query + @return always 0 + */ + virtual int var_exists( std::string in_name ) ; + + /** + @brief @userdesc Command to immediately send the value of a comma separated list of variables + @par Python Usage: + @code trick.var_send_once("", ) @endcode + @param in_name_list - the variables name to retrieve, comma separated + @param num_vars - number of vars in in_name_list + @return always 0 + */ + virtual int var_send_once(std::string in_name_list, int num_vars); + + /** + @brief @userdesc Command to instruct the variable server to immediately send back the values of + variables that are registered with the var_add command + (typically used when var_pause is in effect). Each var_send command sends back all current values + once instead of cyclically. + @par Python Usage: + @code trick.var_send() @endcode + @return always 0 + */ + virtual int var_send() ; + + /** + @brief @userdesc Command to remove all variables from the list of variables currently + registered with the var_add command, + so the variable server will no longer send cyclic values until new var_add commands are issued. + @par Python Usage: + @code trick.var_clear() @endcode + @return always 0 + */ + virtual int var_clear() ; + + /** + @brief @userdesc Turns on validating addresses before they are referenced + @par Python Usage: + @code trick.var_validate_address() @endcode + @return always 0 + */ + virtual int var_validate_address(bool on_off) ; + + /** + @brief @userdesc Command to instruct the variable server to output debug information. + @par Python Usage: + @code trick.var_debug() @endcode + @return always 0 + @param level - 1,2,or 3, higher number increases amount of info output + */ + virtual int var_debug(int level) ; + + /** + @brief @userdesc Command to instruct the variable server to return values in ASCII format (this is the default). + @par Python Usage: + @code trick.var_ascii() @endcode + @return always 0 + */ + virtual int var_ascii() ; + + /** + @brief @userdesc Command to instruct the variable server to return values in binary format. + @par Python Usage: + @code trick.var_binary() @endcode + @return always 0 + */ + virtual int var_binary() ; + + /** + @brief @userdesc Command to instruct the variable server to return values in binary format, + but saves space in the returned messages + by omitting the variable names. + @par Python Usage: + @code trick.var_binary_nonames() @endcode + @return always 0 + */ + virtual int var_binary_nonames() ; + + /** + @brief @userdesc Command to tell the server when to copy data + - VS_COPY_ASYNC = copies data asynchronously. (default) + - VS_COPY_SCHEDULED = copies data as an automatic_last job in main thread + - VS_COPY_TOP_OF_FRAME = copies data at top of frame + @par Python Usage: + @code trick.var_set_copy_mode() @endcode + @param mode - One of the above enumerations + @return 0 if successful, -1 if error + */ + virtual int var_set_copy_mode(int mode) ; + + /** + @brief @userdesc Command to tell the server when to copy data + - VS_WRITE_ASYNC = writes data asynchronously. (default) + - VS_WRITE_WHEN_COPIED = writes data as soon as it's copied from sim + @par Python Usage: + @code trick.var_set_write_mode() @endcode + @param mode - One of the above enumerations + @return 0 if successful, -1 if error + */ + virtual int var_set_write_mode(int mode) ; + + /** + @brief @userdesc Command to put the current variable server/client connection in sync mode, + so that values to be sent to the client + are guaranteed to be homogenous, that is from the same execution frame (the default is asynchronous). + - async/async mode: the variable server thread itself copies the client data, + independent of the execution frame so may not be homogenous. + - sync/async mode: a variable server automatic_last job retrieves client requested data. + Data is sent asynchronously on separate thread. + - sync/sync mode: a variable server automatic_last job retrieves client requested data. + Data is sent in same automatic_last job. + . + @par Python Usage: + @code trick.var_sync() @endcode + @param on_off - 0 = fully asynchronous. 1 = sync data gather, async socket write. + 2 = sync data gather, sync socket write + @return always 0 + */ + virtual int var_sync(int on_off) ; + + /** + @brief @userdesc Set the frame multiple + @par Python Usage: + @code trick.var_set_frame_multiple() @endcode + @param mult - The requested multiple + @return 0 + */ + virtual int var_set_frame_multiple(unsigned int mult) ; + + /** + @brief @userdesc Set the frame offset + @par Python Usage: + @code trick.var_set_frame_offset() @endcode + @param offset - The requested offset + @return 0 + */ + virtual int var_set_frame_offset(unsigned int offset) ; + + /** + @brief @userdesc Set the frame multiple + @par Python Usage: + @code trick.var_set_freeze_frame_multiple() @endcode + @param mult - The requested multiple + @return 0 + */ + virtual int var_set_freeze_frame_multiple(unsigned int mult) ; + + /** + @brief @userdesc Set the frame offset + @par Python Usage: + @code trick.var_set_freeze_frame_offset() @endcode + @param offset - The requested offset + @return 0 + */ + virtual int var_set_freeze_frame_offset(unsigned int offset) ; + + /** + @brief @userdesc Command to instruct the variable server to byteswap the return values + (only has an effect in var_binary mode). + The default is no byteswap - it is assumed server and client are same endianness. + If not, the user must issue the var_byteswap command. + @par Python Usage: + @code trick.var_byteswap() @endcode + @param on_off - true (or 1) to byteswap the return data, false (or 0) to return data as is + @return always 0 + */ + virtual int var_byteswap(bool on_off) ; + + /** + @brief @userdesc Command to toggle variable server logged messages to a playback file. + All messages received from all clients will be saved to file named "playback" in the RUN directory. + @par Python Usage: + @code trick.set_log() @endcode + @return always 0 + */ + virtual int set_log(bool on_off) ; + + /** + @brief @userdesc Command to toggle variable server session log. + Each session will create a file containing the IP and port of client and all messages. + The file will be created in a session_log/ subdirectory under the RUN directory. + @par Python Usage: + @code trick.set_session_log() @endcode + @return always 0 + */ + virtual int set_session_log (bool on_off) ; + + /** + @brief @userdesc Command to set info messages. + @return always 0 + */ + virtual int set_info_message(bool on) ; + + /** + @brief Command to send the number of items in the var_add list. + The variable server sends a message indicator of "3", followed by the total number of variables being sent. + */ + virtual int send_list_size(); + + /** + @brief Special command to instruct the variable server to send the contents of the S_sie.resource file; used by TV. + The variable server sends a message indicator of "2", followed by a tab, followed by the file contents + which are then sent as sequential ASCII messages with a maximum size of 4096 bytes each. + */ + virtual int send_sie_resource(); + + /** + @brief Special command to only send the class sie class information + */ + virtual int send_sie_class(); + + /** + @brief Special command to only send the enumeration sie class information + */ + virtual int send_sie_enum(); + + /** + @brief Special command to only send the top level objects sie class information + */ + virtual int send_sie_top_level_objects(); + + /** + @brief Special command to send an arbitrary file through the variable server. + */ + virtual int send_file(std::string file_name); + + /** + @brief @userdesc Command to set the frequencty at which the variable server will send values + of variables that have been registered using + the var_add command. If var_cycle is not specified, the default cycle is 0.1 seconds. + @par Python Usage: + @code trick.var_cycle() @endcode + @param in_cycle - the desired frequency in seconds + @return always 0 + */ + virtual int var_cycle(double in_cycle) ; + + /** + @brief @userdesc Command exit this variable server session. + @par Python Usage: + @code trick.var_exit() @endcode + @return always 0 + */ + virtual int var_exit(); + + private: + static int instance_counter; + static std::string log_subdir; + + pthread_mutex_t _copy_mutex; /**< trick_io(**) */ + + ClientConnection * _connection; /**< trick_io(**) */ + + // Helper method to send a file to connection + virtual int transmit_file(std::string sie_file); + + // Helper methods to write out formatted data + virtual int write_binary_data(const std::vector& given_vars, VS_MESSAGE_TYPE message_type); + virtual int write_ascii_data(const std::vector& given_vars, VS_MESSAGE_TYPE message_type ); + + virtual VariableReference * find_session_variable(std::string name) const; + + std::vector _session_variables; /**< trick_io(**) */ + + // Getters and setters for internal variables + virtual long long get_cycle_tics() const; + + virtual void set_freeze_next_tics(long long tics); + virtual void set_next_tics(long long tics); + + virtual int get_frame_multiple () const; + virtual int get_frame_offset () const; + virtual int get_freeze_frame_multiple () const; + virtual int get_freeze_frame_offset () const; + virtual bool get_enabled () const; + + // Check settings and log to appropriate places + void log_received_message(const std::string& msg); + void log_connection_opened (); + + bool is_session_log_open(); + + void open_session_log(); + + void write_to_session_log(const std::string& msg); + + /** Value set in var_cycle command.\n */ + double _update_rate ; /**< trick_io(**) */ + + /** The update rate in integer tics.\n */ + long long _cycle_tics ; /**< trick_io(**) */ + + /** The next call time in integer tics of the job to copy client data (sync mode).\n */ + long long _next_tics ; /**< trick_io(**) */ + + /** The next call time in integer tics of the job to copy client data (sync mode).\n */ + long long _freeze_next_tics ; /**< trick_io(**) */ + + /** The simulation time converted to seconds\n */ + double _time ; /**< trick_units(s) */ + + /** Toggle to set variable server copy as top_of_frame, scheduled, async \n */ + VS_COPY_MODE _copy_mode ; /**< trick_io(**) */ + + /** Toggle to set variable server writes as when copied or async.\n */ + VS_WRITE_MODE _write_mode ; /**< trick_io(**) */ + + /** multiples of frame_count to copy data. Only used at top_of_frame\n */ + int _frame_multiple ; /**< trick_io(**) */ + + /** multiples of frame_count to copy data. Only used at top_of_frame\n */ + int _frame_offset ; /**< trick_io(**) */ + + /** multiples of frame_count to copy data. Only used at top_of_frame\n */ + int _freeze_frame_multiple ; /**< trick_io(**) */ + + /** multiples of frame_count to copy data. Only used at top_of_frame\n */ + int _freeze_frame_offset ; /**< trick_io(**) */ + + /** Toggle to tell variable server to byteswap returned values.\n */ + bool _byteswap ; /**< trick_io(**) */ + + /** Toggle to tell variable server return data in binary format.\n */ + bool _binary_data ; /**< trick_io(**) */ + + /** Toggle to tell variable server return data in binary format without the variable names.\n */ + bool _binary_data_nonames ; /**< trick_io(**) */ + + /** Value (1,2,or 3) that causes the variable server to output increasing amounts of debug information.\n */ + int _debug ; /**< trick_io(**) */ + + /** Toggle to enable/disable this variable server thread.\n */ + bool _enabled ; /**< trick_io(**) */ + + /** Toggle to turn on/off variable server logged messages to a playback file.\n */ + bool _log ; /**< trick_io(**) */ + + /** Toggle to turn on/off individual variable server session logs.\n */ + bool _session_log; /**< trick_io(**) */ + + /** Toggle to turn on/off debug info messages.\n */ + bool _info_msg ; + + /** Message stream number for the session log file */ + int _session_log_msg_stream; + + /** Toggle to indicate var_pause commanded.\n */ + bool _pause_cmd ; /**< trick_io(**) */ + + /** Save pause state while reloading a checkpoint.\n */ + bool _saved_pause_cmd ; /**< trick_io(**) */ + + bool _send_stdio; + + bool _validate_address; + + /** Toggle to indicate var_exit commanded.\n */ + bool _exit_cmd ; /**< trick_io(**) */ + + int _instance_num; + + }; +} + +#endif diff --git a/include/trick/VariableServerSessionThread.hh b/include/trick/VariableServerSessionThread.hh new file mode 100644 index 00000000..8037da31 --- /dev/null +++ b/include/trick/VariableServerSessionThread.hh @@ -0,0 +1,106 @@ +/* + PURPOSE: + (VariableServerSessionThread) +*/ + +#ifndef VariableServerSessionThread_HH +#define VariableServerSessionThread_HH + +#include +#include +#include +#include "trick/SysThread.hh" +#include "trick/VariableServerSession.hh" +#include "trick/variable_server_sync_types.h" +#include "trick/variable_server_message_types.h" + +#include "trick/ClientConnection.hh" +#include "trick/TCPClientListener.hh" + +namespace Trick { + + class VariableServer ; + + /** Flag to indicate the connection has been made */ + enum ConnectionStatus { CONNECTION_PENDING, CONNECTION_SUCCESS, CONNECTION_FAIL }; + + +/** + The purpose of this class is to run a VariableServerSession. + - Manages creation and cleanup of the network connection + - Manages thread startup and shutdown + - Invokes parts of the VariableServerSession that run asynchronously (reading from client, copying and writing to client if in applicable mode) + + @author Alex Lin + @author Jackie Deans (2023) + */ + class VariableServerSessionThread : public Trick::SysThread { + + public: + friend std::ostream& operator<< (std::ostream& s, Trick::VariableServerSessionThread& vst); + + /** + @brief Constructor. + */ + VariableServerSessionThread() ; + VariableServerSessionThread(VariableServerSession * session) ; + + virtual ~VariableServerSessionThread() ; + /** + @brief static routine called from S_define to set the VariableServer pointer for all threads. + @param in_vs - the master variable server object + */ + static void set_vs_ptr(Trick::VariableServer * in_vs) ; + + void set_client_tag(std::string tag); + + /** + @brief Set the connection pointer for this thread + */ + void set_connection(ClientConnection * in_connection); + + /** + @brief Block until thread has accepted connection + */ + ConnectionStatus wait_for_accept() ; + + /** + @brief The main loop of the variable server thread that reads and processes client commands. + @return always 0 + */ + virtual void * thread_body() ; + + VariableServer * get_vs() ; + + void preload_checkpoint() ; + + void restart() ; + + void cleanup(); + + protected: + /** The Master variable server object. */ + static VariableServer * _vs ; + + /** Manages the variable list */ + VariableServerSession * _session; /**< trick_io(**) */ + + /** Connection to the client */ + ClientConnection * _connection; /**< trick_io(**) */ + + /** Value (1,2,or 3) that causes the variable server to output increasing amounts of debug information.\n */ + int _debug ; /**< trick_io(**) */ + + ConnectionStatus _connection_status ; /**< trick_io(**) */ + pthread_mutex_t _connection_status_mutex; /**< trick_io(**) */ + pthread_cond_t _connection_status_cv; /**< trick_io(**) */ + + bool _saved_pause_cmd; + } ; + + std::ostream& operator<< (std::ostream& s, VariableServerSessionThread& vst); + +} + +#endif + diff --git a/include/trick/VariableServerThread.hh b/include/trick/VariableServerThread.hh deleted file mode 100644 index fed81f59..00000000 --- a/include/trick/VariableServerThread.hh +++ /dev/null @@ -1,593 +0,0 @@ -/* - PURPOSE: - (VariableServerThread) -*/ - -#ifndef VARIABLESERVERTHREAD_HH -#define VARIABLESERVERTHREAD_HH - -#include -#include -#include -#include -#include "trick/tc.h" -#include "trick/SysThread.hh" -#include "trick/VariableServerReference.hh" -#include "trick/variable_server_sync_types.h" -#include "trick/variable_server_message_types.h" - - -namespace Trick { - - class VariableServer ; - -/** - This class provides variable server command processing on a separate thread for each client. - @author Alex Lin - */ - class VariableServerThread : public Trick::SysThread { - - public: - enum ConnectionType { TCP, UDP, MCAST } ; - - friend std::ostream& operator<< (std::ostream& s, Trick::VariableServerThread& vst); - - /** - @brief Constructor. - @param listen_dev - the TCDevice set up in listen() - */ - VariableServerThread(TCDevice * in_listen_dev ) ; - - virtual ~VariableServerThread() ; - /** - @brief static routine called from S_define to set the VariableServer pointer for all threads. - @param in_vs - the master variable server object - */ - static void set_vs_ptr(Trick::VariableServer * in_vs) ; - - /** - @brief Wait for the connection_accepted flag to be set. - */ - void wait_for_accept() ; - - /** - @brief The main loop of the variable server thread that reads and processes client commands. - @return always 0 - */ - virtual void * thread_body() ; - - /** - @brief @userdesc Command to add a variable to a list of registered variables for value retrieval. - The variable server will immediately begin returning the variable values to the client at a - frequency according to var_cycle. - @par Python Usage: - @code trick.var_add("") @endcode - @param in_name - the variable name to retrieve - @return always 0 - */ - int var_add( std::string in_name ) ; - - /** - @brief @userdesc Command to add a variable to a list of registered variables for value retrieval, - and also instructs the variable server to return the value in the specified measurement units. - The unit specification will be returned after the variable's value (var_ascii mode only). - Specifying units as "{xx}" will use the unit specification from the variable's model code declaration, - which is essentially a method of querying a variable's unit specification. - The variable server will immediately begin returning the variable values/units to the client at - a frequency according to var_cycle. - @par Python Usage: - @code trick.var_add("", "") @endcode - @param in_name - the variable name to retrieve - @param units_name - the desired units, specified within curly braces - @return always 0 - */ - int var_add( std::string in_name, std::string units_name ) ; - - /** - @brief @userdesc Command to remove a variable (previously registered with var_add) - from the list of registered variables for value retrieval. - @par Python Usage: - @code trick.var_remove("") @endcode - @param in_name - the variable name to remove - @return always 0 - */ - int var_remove( std::string in_name ) ; - - /** - @brief @userdesc Command to instruct the variable server to return the value of a variable - in the specified units (var_ascii mode only). - The variable must have been previously registered with the var_add command. - The unit specification will be returned after the variable's value. - @par Python Usage: - @code trick.var_units("", "") @endcode - @param var_name - the variable name previously registered with var_add - @param units_name - the desired units, specified within curly braces - @return always 0 - @note trick.var_add("my_object.my_variable"); trick.var_units("my_object.my_variable","{m}") - is the same as trick.var_add("my_object.my_variable","{m}") - */ - int var_units(std::string var_name , std::string units_name) ; - - /** - @brief @userdesc Command to instruct the variable server to send a Boolean value indicating - whether the specified variable exists - (is a Trick processed variable in the current simulation). - - var_binary mode: the message indicator is 1, and the returned value is a binary 0 or 1. - - var_ascii mode: the message indicator is "1" followed by a tab, then an ASCII "0" or "1" returned value. - . - @par Python Usage: - @code trick.var_exists("") @endcode - @param in_name - the variable name to query - @return always 0 - */ - int var_exists( std::string in_name ) ; - - /** - @brief @userdesc Command to immediately send the value of a comma separated list of variables - @par Python Usage: - @code trick.var_send_once("", ) @endcode - @param in_name_list - the variables name to retrieve, comma separated - @param num_vars - number of vars in in_name_list - @return always 0 - */ - int var_send_once(std::string in_name_list, int num_vars); - - /** - @brief @userdesc Command to instruct the variable server to immediately send back the values of - variables that are registered with the var_add command - (typically used when var_pause is in effect). Each var_send command sends back all current values - once instead of cyclically. - @par Python Usage: - @code trick.var_send() @endcode - @return always 0 - */ - int var_send() ; - - /** - @brief @userdesc Command to remove all variables from the list of variables currently - registered with the var_add command, - so the variable server will no longer send cyclic values until new var_add commands are issued. - @par Python Usage: - @code trick.var_clear() @endcode - @return always 0 - */ - int var_clear() ; - - /** - @brief @userdesc Command to set the frequencty at which the variable server will send values - of variables that have been registered using - the var_add command. If var_cycle is not specified, the default cycle is 0.1 seconds. - @par Python Usage: - @code trick.var_cycle() @endcode - @param in_cycle - the desired frequency in seconds - @return always 0 - */ - int var_cycle(double in_cycle) ; - - /** - @brief Get the pause state of this thread. - */ - bool get_pause() ; - - /** - @brief Set the pause state of this thread. - */ - void set_pause(bool on_off) ; - - /** - @brief @userdesc Command to terminate the current variable server/client connection. - @par Python Usage: - @code trick.var_exit() @endcode - @return always 0 - */ - int var_exit() ; - - /** - @brief @userdesc Turns on validating addresses before they are referenced - @par Python Usage: - @code trick.var_validate_address() @endcode - @return always 0 - */ - int var_validate_address(bool on_off) ; - - /** - @brief @userdesc Command to instruct the variable server to output debug information. - @par Python Usage: - @code trick.var_debug() @endcode - @return always 0 - @param level - 1,2,or 3, higher number increases amount of info output - */ - int var_debug(int level) ; - - /** - @brief @userdesc Command to instruct the variable server to return values in ASCII format (this is the default). - @par Python Usage: - @code trick.var_ascii() @endcode - @return always 0 - */ - int var_ascii() ; - - /** - @brief @userdesc Command to instruct the variable server to return values in binary format. - @par Python Usage: - @code trick.var_binary() @endcode - @return always 0 - */ - int var_binary() ; - - /** - @brief @userdesc Command to instruct the variable server to return values in binary format, - but saves space in the returned messages - by omitting the variable names. - @par Python Usage: - @code trick.var_binary_nonames() @endcode - @return always 0 - */ - int var_binary_nonames() ; - - /** - @brief @userdesc Command to tell the server when to copy data - - VS_COPY_ASYNC = copies data asynchronously. (default) - - VS_COPY_SCHEDULED = copies data as an automatic_last job in main thread - - VS_COPY_TOP_OF_FRAME = copies data at top of frame - @par Python Usage: - @code trick.var_set_copy_mode() @endcode - @param mode - One of the above enumerations - @return 0 if successful, -1 if error - */ - int var_set_copy_mode(int on_off) ; - - /** - @brief @userdesc Command to tell the server when to copy data - - VS_WRITE_ASYNC = writes data asynchronously. (default) - - VS_WRITE_WHEN_COPIED = writes data as soon as it's copied from sim - @par Python Usage: - @code trick.var_set_write_mode() @endcode - @param mode - One of the above enumerations - @return 0 if successful, -1 if error - */ - int var_set_write_mode(int on_off) ; - - /** - @brief @userdesc Command to put the current variable server/client connection in sync mode, - so that values to be sent to the client - are guaranteed to be homogenous, that is from the same execution frame (the default is asynchronous). - - async/async mode: the variable server thread itself copies the client data, - independent of the execution frame so may not be homogenous. - - sync/async mode: a variable server automatic_last job retrieves client requested data. - Data is sent asynchronously on separate thread. - - sync/sync mode: a variable server automatic_last job retrieves client requested data. - Data is sent in same automatic_last job. - . - @par Python Usage: - @code trick.var_sync() @endcode - @param on_off - 0 = fully asynchronous. 1 = sync data gather, async socket write. - 2 = sync data gather, sync socket write - @return always 0 - */ - int var_sync(int on_off) ; - - /** - @brief @userdesc Set the frame multiple - @par Python Usage: - @code trick.var_set_frame_multiple() @endcode - @param mult - The requested multiple - @return 0 - */ - int var_set_frame_multiple(unsigned int mult) ; - - /** - @brief @userdesc Set the frame offset - @par Python Usage: - @code trick.var_set_frame_offset() @endcode - @param offset - The requested offset - @return 0 - */ - int var_set_frame_offset(unsigned int offset) ; - - /** - @brief @userdesc Set the frame multiple - @par Python Usage: - @code trick.var_set_freeze_frame_multiple() @endcode - @param mult - The requested multiple - @return 0 - */ - int var_set_freeze_frame_multiple(unsigned int mult) ; - - /** - @brief @userdesc Set the frame offset - @par Python Usage: - @code trick.var_set_freeze_frame_offset() @endcode - @param offset - The requested offset - @return 0 - */ - int var_set_freeze_frame_offset(unsigned int offset) ; - - /** - @brief @userdesc Command to instruct the variable server to byteswap the return values - (only has an effect in var_binary mode). - The default is no byteswap - it is assumed server and client are same endianness. - If not, the user must issue the var_byteswap command. - @par Python Usage: - @code trick.var_byteswap() @endcode - @param on_off - true (or 1) to byteswap the return data, false (or 0) to return data as is - @return always 0 - */ - int var_byteswap(bool on_off) ; - - /** - @brief CURRENTLY NOT IMPLEMENTED - */ - int var_signal() ; - - /** - @brief @userdesc Command to turn on variable server logged messages to a playback file. - All messages received from all clients will be saved to file named "playback" in the RUN directory. - @par Python Usage: - @code trick.set_log_on() @endcode - @return always 0 - */ - int set_log_on() ; - - /** - @brief @userdesc Command to turn off variable server logged messages to a playback file. - @par Python Usage: - @code trick.set_log_off() @endcode - @return always 0 - */ - int set_log_off() ; - - /** - @brief CURRENTLY NOT IMPLEMENTED\n - Command to instruct the variable server to send values via multicast socket. - */ - int var_multicast(bool on_off) ; - - /** - @brief Command to send the number of items in the var_add list. - The variable server sends a message indicator of "3", followed by the total number of variables being sent. - */ - int send_list_size(); - - /** - @brief Special command to instruct the variable server to send the contents of the S_sie.resource file; used by TV. - The variable server sends a message indicator of "2", followed by a tab, followed by the file contents - which are then sent as sequential ASCII messages with a maximum size of 4096 bytes each. - */ - int send_sie_resource(); - - /** - @brief Special command to only send the class sie class information - */ - int send_sie_class(); - - /** - @brief Special command to only send the enumeration sie class information - */ - int send_sie_enum(); - - /** - @brief Special command to only send the top level objects sie class information - */ - int send_sie_top_level_objects(); - - /** - @brief Special command to send an arbitrary file through the variable server. - */ - int send_file(std::string file_name); - - /** - @brief Copy client variable values from Trick memory to each variable's output buffer. - */ - int copy_sim_data(); - - /** - @brief Copy given variable values from Trick memory to each variable's output buffer. - cyclical indicated whether it is a normal cyclical copy or a send_once copy - */ - int copy_sim_data(std::vector given_vars, bool cyclical); - - /** - @brief Write data in the appropriate format (var_ascii or var_binary) from variable output buffers to socket. - */ - int write_data(); - - /** - @brief Write data from the given var only to the appropriate format (var_ascii or var_binary) from variable output buffers to socket. - */ - int write_data(std::vector var) ; - - /** - @brief gets the send_stdio flag. - */ - bool get_send_stdio() ; - - /** - @brief sets the send_stdio flag. - */ - int set_send_stdio(bool on_off) ; - - VariableServer * get_vs() ; - TCDevice & get_connection() ; - - /** - @brief Internal function used by input processor to send stdout and stderr to the client. - @return always 0 - */ - int write_stdio(int stream , std::string text ) ; - - int freeze_init() ; - - int copy_data_freeze() ; - int copy_data_freeze_scheduled(long long curr_tics) ; - int copy_data_scheduled(long long curr_tics) ; - int copy_data_top() ; - - void preload_checkpoint() ; - void restart() ; - - long long get_next_tics() ; - long long get_freeze_next_tics() ; - - /** - @brief creates a udp socket with the specified address and port. UDP ports do not go through - the listen and accept states - */ - int create_udp_socket(const char * address, unsigned short in_port) ; - - /** - @brief creates a multicast socket with the specified address and port. mcast ports do not go through - the listen and accept states - */ - int create_mcast_socket(const char * mcast_address, const char * address, unsigned short in_port) ; - - protected: - - /** - @brief Called by send_sie commands to transmit files through the socket. - */ - int transmit_file(std::string file_name); - - /** - @brief Called by write_data to write given variables to socket in var_binary format. - */ - int write_binary_data( int Start, char *buf1, const std::vector& given_vars, VS_MESSAGE_TYPE message_type); - - /** - @brief Called by write_data to write given variables to socket in var_ascii format. - */ - int write_ascii_data(char * dest_buf, size_t dest_buf_size, const std::vector& given_vars, VS_MESSAGE_TYPE message_type ); - - /** - @brief Construct a variable reference from the string in_name and handle error checking - */ - VariableReference* create_var_reference(std::string in_name); - - /** - @brief Make a time reference. - */ - REF2* make_time_ref(); - - /** - @brief Make a "bad-reference" reference. - */ - REF2* make_error_ref(std::string in_name); - - /** The Master variable server object. */ - static VariableServer * vs ; - - /** The listen device from the variable server\n */ - TCDevice * listen_dev; /**< trick_io(**) */ - - /** The trickcomm device used for the connection to the client.\n */ - TCDevice connection ; /**< trick_io(**) */ - - /** The type of connection we have.\n */ - ConnectionType conn_type ; /**< trick_io(**) */ - - /** Value (1,2,or 3) that causes the variable server to output increasing amounts of debug information.\n */ - int debug ; /**< trick_io(**) */ - - /** Toggle to enable/disable this variable server thread.\n */ - bool enabled ; /**< trick_io(**) */ - - /** Toggle to turn on/off variable server logged messages to a playback file.\n */ - bool log ; /**< trick_io(**) */ - - /** Toggle to indicate var_pause commanded.\n */ - bool pause_cmd ; /**< trick_io(**) */ - - /** Save pause state while reloading a checkpoint.\n */ - bool saved_pause_cmd ; /**< trick_io(**) */ - - /** Toggle to indicate var_exit commanded.\n */ - bool exit_cmd ; /**< trick_io(**) */ - - /** Set to true to validate all addresses before copying.\n */ - bool validate_address ; /**< trick_io(**) */ - - /** The mutex to protect variable output buffers when copying variable values to them from Trick memory.\n */ - pthread_mutex_t copy_mutex ; /**< trick_io(**) */ - - /** The mutex pauses all processing during checkpoint restart */ - pthread_mutex_t restart_pause ; /**< trick_io(**) */ - - /** Dummy integer for bad references.\n */ - static int bad_ref_int ; /**< trick_io(**) */ - - /** Dummy integer for bad references. If a variable points here, do not try to re-resolve address\n */ - static int do_not_resolve_bad_ref_int ; /**< trick_io(**) */ - - /** The simulation time converted to seconds\n */ - double time ; /**< trick_units(s) */ - - /** List of client requested variables.\n */ - std::vector vars; /**< trick_io(**) */ - - /** Toggle to set variable server copy as top_of_frame, scheduled, async \n */ - VS_COPY_MODE copy_mode ; /**< trick_io(**) */ - - /** Toggle to set variable server writes as when copied or async.\n */ - VS_WRITE_MODE write_mode ; /**< trick_io(**) */ - - /** multiples of frame_count to copy data. Only used at top_of_frame\n */ - int frame_multiple ; /**< trick_io(**) */ - - /** multiples of frame_count to copy data. Only used at top_of_frame\n */ - int frame_offset ; /**< trick_io(**) */ - - /** multiples of frame_count to copy data. Only used at top_of_frame\n */ - int freeze_frame_multiple ; /**< trick_io(**) */ - - /** multiples of frame_count to copy data. Only used at top_of_frame\n */ - int freeze_frame_offset ; /**< trick_io(**) */ - - /** Toggle to tell variable server to byteswap returned values.\n */ - bool byteswap ; /**< trick_io(**) */ - - /** Toggle to tell variable server return data in binary format.\n */ - bool binary_data ; /**< trick_io(**) */ - - /** Toggle to tell variable server return data in binary format without the variable names.\n */ - bool binary_data_nonames ; /**< trick_io(**) */ - - /** Toggle to tell variable server to send data multicast or point to point.\n */ - bool multicast ; /**< trick_io(**) */ - - /** Flag to indicate the connection has been made\n */ - bool connection_accepted ; /**< trick_io(**) */ - - /** Value set in var_cycle command.\n */ - double update_rate ; /**< trick_io(**) */ - - /** The update rate in integer tics.\n */ - long long cycle_tics ; /**< trick_io(**) */ - - /** The next call time in integer tics of the job to copy client data (sync mode).\n */ - long long next_tics ; /**< trick_io(**) */ - - /** The next call time in integer tics of the job to copy client data (sync mode).\n */ - long long freeze_next_tics ; /**< trick_io(**) */ - - /** Indicate whether variable data has been written into buffer_in.\n */ - bool var_data_staged; /**< trick_io(**) */ - - /** number of packets copied to client \n */ - unsigned int packets_copied ; /**< trick_io(**) */ - - /** Toggle to indicate sending python stdout and stderr to client\n */ - bool send_stdio ; /**< trick_io(**) */ - - /** Holding area for the incoming message\n */ - char *incoming_msg; /**< trick_io(**) */ - - /** Message with '\r' characters removed\n */ - char *stripped_msg; /**< trick_io(**) */ - - /** Maximum size of incoming message\n */ - static const unsigned int MAX_CMD_LEN = 200000 ; - } ; -} - -#endif - diff --git a/include/trick/command_line_protos.h b/include/trick/command_line_protos.h index a3e56ab3..93218787 100644 --- a/include/trick/command_line_protos.h +++ b/include/trick/command_line_protos.h @@ -45,5 +45,8 @@ void set_output_dir(const char* output_directory); std::string & command_line_args_get_input_file_ref(void) ; #endif +void create_path(const char* directory); + + #endif diff --git a/include/trick/files_to_ICG.hh b/include/trick/files_to_ICG.hh index 524e5241..19dd0071 100644 --- a/include/trick/files_to_ICG.hh +++ b/include/trick/files_to_ICG.hh @@ -36,6 +36,9 @@ #include "trick/MessageCout.hh" #include "trick/MessageThreadedCout.hh" #include "trick/MessageFile.hh" +#include "trick/MessageHSFile.hh" +#include "trick/MessageCustomFile.hh" +#include "trick/MessageCustomManager.hh" #include "trick/MessageLCout.hh" #include "trick/MessagePublisher.hh" #include "trick/MessageTCDevice.hh" diff --git a/include/trick/tc.h b/include/trick/tc.h index c22b8fcb..04596c04 100644 --- a/include/trick/tc.h +++ b/include/trick/tc.h @@ -48,8 +48,6 @@ PROGRAMMERS: #define TC_READWRITE_TIMEOUT -2 #define TC_BROKEN_PIPE -3 -#define TC_COULD_NOT_SET_SOCKET_PRIORITY 16 - /* Set default for blocking timeout limit high */ #define MAX_BLOCKIO_TIMEOUT_LIMIT 1.0e20 diff --git a/include/trick/tc_proto.h b/include/trick/tc_proto.h index 774012bd..0abf0e79 100644 --- a/include/trick/tc_proto.h +++ b/include/trick/tc_proto.h @@ -111,12 +111,6 @@ int tc_error(TCDevice * device, int on_off); /* copy a device */ int tc_dev_copy(TCDevice * dest, TCDevice * src); -#ifndef __APPLE__ -/* set priority of socket for a device */ -#define tc_set_socket_priority(listen_device, device, priority) tc_set_socket_priority_(listen_device, device, priority, __FILE__, __LINE__) -int tc_set_socket_priority_(TCDevice * listen_device, TCDevice * device, int priority, const char *file, int line) ; -#endif - void *trick_bswap_buffer(void *out, void *in, ATTRIBUTES * attr, int tofrom) ; void *trick_bswap_single_parameter(void *out, void *in, ATTRIBUTES * attr, int tofrom) ; #ifdef __cplusplus diff --git a/include/trick/var_binary_parser.hh b/include/trick/var_binary_parser.hh index 723166b5..1203c849 100644 --- a/include/trick/var_binary_parser.hh +++ b/include/trick/var_binary_parser.hh @@ -112,7 +112,6 @@ class ParsedBinaryMessage { void combine (const ParsedBinaryMessage& message); int parse (const std::vector& bytes); - int parse (char * raw_bytes); int getMessageType() const; unsigned int getMessageSize() const; @@ -139,4 +138,4 @@ class ParsedBinaryMessage { const static size_t variable_name_length_size; const static size_t variable_type_size; const static size_t variable_size_size; -}; \ No newline at end of file +}; diff --git a/include/trick/variable_server.h b/include/trick/variable_server.h deleted file mode 100644 index 163b2d3e..00000000 --- a/include/trick/variable_server.h +++ /dev/null @@ -1,68 +0,0 @@ -/* -PURPOSE: - (Allows clients to get and set Trick parameters) -PROGRAMMERS: - (((Keith Vetter) (LinCom) (September 2001) (--))) -*/ - -#ifndef VARIABLE_SERVER_H -#define VARIABLE_SERVER_H - -#include - -#include "trick/reference.h" -#include "trick/tc.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct { - REF2 ref; - double scale_factor; - double bias; -} VARIABLE_NODE; - -typedef struct { - int socket ; /* ** multicast socket file descriptor */ - struct sockaddr_in addr ; /* ** multicast socket info */ -} MCAST_INFO; - -typedef struct VARIABLE_SERVER_struct { - int enabled ; - int debug; /* ** Variable_server debugging */ - int synchronous ; - double update_rate; - double next; - int binary_data; - int no_handshake; - int num_vars; - int clear_cmd; - int send_cmd; - int pause_cmd; - int exists_cmd; - int exit_cmd; - VARIABLE_NODE *vars; - - int multicast_on ; /* -- flag to switch between TCdevice and multcast socket */ - TCDevice * connection ; - MCAST_INFO mcast_info ; - char local_byteorder ; - - pthread_t thread_id ; -} VARIABLE_SERVER; - -//void *var_server(void *); /* Server thread */ -//void *var_serve_init(void *); /* Initialize server */ -//void exit_var_server(void *); /* Exit handler for var init server */ -//void exit_var_serv_threads( void ); /* Exit handler for var init server */ - -int vs_mcast_init(MCAST_INFO * M, char * address , int port ) ; -//int vs_write_data(VARIABLE_SERVER * V) ; -//int vs_write_error(VARIABLE_SERVER * V, char * incoming_str , char * error_str ) ; -//int vs_format_ascii(REF2 * ref, double scale_factor, double bias, char *value) ; - -#ifdef __cplusplus -} -#endif -#endif diff --git a/share/trick/makefiles/Makefile.common b/share/trick/makefiles/Makefile.common index 104d5aad..154621fa 100644 --- a/share/trick/makefiles/Makefile.common +++ b/share/trick/makefiles/Makefile.common @@ -66,7 +66,7 @@ export TRICK_PYTHON_PATH := $(TRICK_PYTHON_PATH) export TRICK_GTE_EXT := $(TRICK_GTE_EXT) export TRICK_HOST_CPU := $(shell TRICK_FORCE_32BIT=$(TRICK_FORCE_32BIT) $(TRICK_HOME)/bin/trick-gte TRICK_HOST_CPU) export TRICK_EXEC_LINK_LIBS := ${PTHREAD_LIBS} $(PYTHON_LIB) $(UDUNITS_LDFLAGS) $(PLATFORM_LIBS) -lm -ldl -export TRICK_LIBS := ${RPATH} -L${TRICK_LIB_DIR} -ltrick -ltrick_pyip -ltrick_comm -ltrick_math -ltrick_units -ltrick_mm +export TRICK_LIBS := ${RPATH} -L${TRICK_LIB_DIR} -ltrick -ltrick_pyip -ltrick_comm -ltrick_math -ltrick_units -ltrick_mm -ltrick_connection_handlers export TRICK_SYSTEM_LDFLAGS := $(TRICK_SYSTEM_LDFLAGS) export TRICK_SYSTEM_ICG_EXCLUDE := $(TRICK_SYSTEM_ICG_EXCLUDE) export TRICK_SWIG_FLAGS := $(TRICK_SWIG_FLAGS) diff --git a/share/trick/pymods/trick/tests/test_variable_server.py b/share/trick/pymods/trick/tests/test_variable_server.py index 9ac8dc68..ef72c47d 100644 --- a/share/trick/pymods/trick/tests/test_variable_server.py +++ b/share/trick/pymods/trick/tests/test_variable_server.py @@ -110,7 +110,7 @@ class TestVariableServer(unittest.TestCase): def test_set_period(self): self.variable_server.set_period(10) - # We would like to verify that VariableServerThread::update_rate + # We would like to verify that VariableServerSessionThread::update_rate # was modified, but variable server threads are not registered # with the memory manager, so we can't. @@ -137,38 +137,38 @@ class TestVariableServer(unittest.TestCase): def test_pause(self): self.variable_server.pause(True) self.variable_server.pause(False) - # We would like to verify that VariableServerThread::pause_cmd + # We would like to verify that VariableServerSessionThread::pause_cmd # was modified, but variable server threads are not registered # with the memory manager, so we can't. def test_set_debug(self): self.variable_server.set_debug(3) - # We would like to verify that VariableServerThread::debug + # We would like to verify that VariableServerSessionThread::debug # was modified, but variable server threads are not registered # with the memory manager, so we can't. def test_set_tag(self): self.variable_server.set_tag('test') # We would like to verify that - # VariableServerThread::connection.client_tag was modified, but + # VariableServerSessionThread::connection.client_tag was modified, but # variable server threads are not registered with the memory # manager, so we can't. def test_set_copy_mode(self): self.variable_server.set_copy_mode() - # We would like to verify that VariableServerThread::copy_mode + # We would like to verify that VariableServerSessionThread::copy_mode # was modified, but variable server threads are not registered # with the memory manager, so we can't. def test_send_on_copy(self): self.variable_server.send_on_copy() - # We would like to verify that VariableServerThread::write_mode + # We would like to verify that VariableServerSessionThread::write_mode # was modified, but variable server threads are not registered # with the memory manager, so we can't. def test_validate_addresses(self): self.variable_server.validate_addresses() - # We would like to verify that VariableServerThread::validate_addresses + # We would like to verify that VariableServerSessionThread::validate_addresses # was modified, but variable server threads are not registered # with the memory manager, so we can't. diff --git a/share/trick/sim_objects/default_trick_sys.sm b/share/trick/sim_objects/default_trick_sys.sm index 9b7a871e..c04946b2 100644 --- a/share/trick/sim_objects/default_trick_sys.sm +++ b/share/trick/sim_objects/default_trick_sys.sm @@ -48,6 +48,9 @@ a replacement SimObject will create an uncompilable sim. ##include "trick/MessageThreadedCout.hh" ##include "trick/MessageLCout.hh" ##include "trick/MessageFile.hh" +##include "trick/MessageHSFile.hh" +##include "trick/MessageCustomFile.hh" +##include "trick/MessageCustomManager.hh" ##include "trick/MessageTCDevice.hh" ##include "trick/PlaybackFile.hh" ##include "trick/MemoryManager.hh" @@ -258,9 +261,10 @@ class MessageSimObject : public Trick::SimObject { Trick::MessagePublisher mpublisher ; Trick::MessageCout mcout ; Trick::MessageThreadedCout mtcout ; - Trick::MessageFile mfile ; + Trick::MessageHSFile mfile ; Trick::MessageTCDevice mdevice ; Trick::PlaybackFile pfile ; + Trick::MessageCustomManager message_file_manager ; MessageSimObject() { @@ -269,6 +273,7 @@ class MessageSimObject : public Trick::SimObject { {TRK} ("default_data") mpublisher.subscribe(&mfile) ; {TRK} ("default_data") mpublisher.subscribe(&mdevice) ; {TRK} ("default_data") mpublisher.subscribe(&pfile) ; + {TRK} ("default_data") mpublisher.subscribe(&message_file_manager) ; {TRK} ("default_data") mdevice.default_data() ; {TRK} P1 ("initialization") mpublisher.init() ; //{TRK} P1 ("initialization") mtcout.init() ; @@ -278,9 +283,9 @@ class MessageSimObject : public Trick::SimObject { {TRK} ("exec_time_tic_changed") mpublisher.init() ; {TRK} P1 ("restart") mdevice.restart() ; + {TRK} P1 ("restart") message_file_manager.restart() ; {TRK} ("shutdown") mtcout.shutdown() ; {TRK} ("shutdown") mdevice.shutdown() ; - } private: @@ -456,22 +461,22 @@ class VariableServerSimObject : public Trick::SimObject { {TRK} ("preload_checkpoint") vs.suspendPreCheckpointReload(); {TRK} ("restart") vs.restart(); {TRK} ("restart") vs.resumePostCheckpointReload(); - {TRK} ("top_of_frame") vs.copy_data_top() ; - {TRK} ("automatic_last") vs.copy_data_scheduled() ; + {TRK} ("top_of_frame") vs.copy_and_write_top() ; + {TRK} ("automatic_last") vs.copy_and_write_scheduled() ; {TRK} ("freeze_init") vs.freeze_init() ; - {TRK} ("freeze_automatic") vs.copy_data_freeze_scheduled() ; - {TRK} ("freeze") vs.copy_data_freeze() ; + {TRK} ("freeze_automatic") vs.copy_and_write_freeze_scheduled() ; + {TRK} ("freeze") vs.copy_and_write_freeze() ; {TRK} ("shutdown") vs.shutdown() ; - // Set the static VariableServer pointer in the VariableServerThread class. - Trick::VariableServerThread::set_vs_ptr(&vs) ; + // Set the static VariableServer pointer in the VariableServerSessionThread class. + Trick::VariableServerSessionThread::set_vs_ptr(&vs) ; // get the process_event job and set it in the event processor. - vs.set_copy_data_job(get_job("vs.copy_data_scheduled")) ; - vs.set_copy_data_freeze_job(get_job("vs.copy_data_freeze_scheduled")) ; + vs.set_copy_data_job(get_job("vs.copy_and_write_scheduled")) ; + vs.set_copy_and_write_freeze_job(get_job("vs.copy_and_write_freeze_scheduled")) ; } private: diff --git a/share/trick/trickops/WorkflowCommon.py b/share/trick/trickops/WorkflowCommon.py index 0846313b..8b8e0e86 100644 --- a/share/trick/trickops/WorkflowCommon.py +++ b/share/trick/trickops/WorkflowCommon.py @@ -205,7 +205,7 @@ class Job(object): for getting status information. More specific types of Jobs should inherit from this base clasee. """ - enums = ['NOT_STARTED', 'RUNNING', 'SUCCESS', 'FAILED'] + enums = ['NOT_STARTED', 'RUNNING', 'SUCCESS', 'FAILED', 'TIMEOUT'] Status = collections.namedtuple('Status', enums)(*(range(len(enums)))) _success_progress_bar = create_progress_bar(1, 'Success') @@ -220,7 +220,9 @@ class Job(object): Job.Status.NOT_STARTED: ('NOT RUN', 'DARK_YELLOW'), Job.Status.SUCCESS: ('OK', 'DARK_GREEN'), Job.Status.FAILED: ('FAIL:' + str(self._expected_exit_status) + '/' + - str(self._exit_status), 'DARK_RED') }[self.get_status()] + str(self._exit_status), 'DARK_RED'), + Job.Status.TIMEOUT: ('TIMEOUT after ' + str(self._timeout) + ' seconds', 'YELLOW') + }[self.get_status()] return printer.colorstr(text, color) def __init__(self, name, command, log_file, expected_exit_status=0): @@ -245,6 +247,7 @@ class Job(object): self._stop_time = None self._exit_status = None self._expected_exit_status = expected_exit_status + self._timeout = None def start(self): """ @@ -275,6 +278,8 @@ class Job(object): This job completed with an exit status of zero. Status.FAILED This job completed with a non-zero exit status. + Status.TIMEOUT + This job ran longer than the allotted time given by execute_job """ if self._process is None: return self.Status.NOT_STARTED @@ -283,6 +288,9 @@ class Job(object): if self._exit_status is None: return self.Status.RUNNING + if self._timeout is not None: + return self.Status.TIMEOUT + if self._stop_time is None: self._stop_time = time.time() @@ -368,6 +376,17 @@ class Job(object): """ return self._done_string() + def _timeout_string(self): + """ + Get a string to display when this Job has failed. + + Returns + ------- + str + A string to be displayed when in the FAILED state. + """ + return self._done_string() + def _done_string(self): """ This class uses the same string for SUCCESS and FAILED, but @@ -408,7 +427,7 @@ class Job(object): by sending them the SIGKILL signal. """ try: - os.killpg(os.getpgid(self._process.pid), signal.SIGKILL) + os.killpg(os.getpgid(self._process.pid), signal.SIGABRT) self._process.wait() except: pass @@ -573,7 +592,7 @@ class WorkflowCommon: else: return 'unknown' - def execute_jobs(self, jobs, max_concurrent=None, header=None): + def execute_jobs(self, jobs, max_concurrent=None, header=None, job_timeout=None): """ Run jobs, blocking until all have returned. @@ -694,6 +713,15 @@ class WorkflowCommon: for i in range(min(len(waitingJobs), available_cpus)): waitingJobs[i].start() + if job_timeout is not None: + # Check if any jobs have timed out + runningJobs = [job for job in jobs if job.get_status() is job.Status.RUNNING] + for runningJob in runningJobs: + if time.time() - runningJob._start_time > job_timeout: + runningJob._timeout = job_timeout + runningJob._stop_time = time.time() + runningJob.die() + # display the status if enabled if stdscr: status_pad.erase() @@ -802,8 +830,8 @@ class WorkflowCommon: text, color = { job.Status.NOT_STARTED: ('was not run', 'GREY40'), job.Status.SUCCESS: ('succeeded', 'DARK_GREEN'), - job.Status.FAILED: ('failed', 'DARK_RED') - }[job.get_status()] + job.Status.FAILED: ('failed', 'DARK_RED'), + job.Status.TIMEOUT: ('timed out', 'YELLOW') }[job.get_status()] text = job.name + ' ' + text diff --git a/test/.gitignore b/test/.gitignore index f3d79c4a..2cb1c8bc 100644 --- a/test/.gitignore +++ b/test/.gitignore @@ -27,3 +27,4 @@ build S_sie.json *.ckpnt MonteCarlo_Meta_data_output +*.xml \ No newline at end of file diff --git a/test/SIM_test_varserv/RUN_test/err1_test.py b/test/SIM_test_varserv/RUN_test/err1_test.py new file mode 100644 index 00000000..ca34b4e7 --- /dev/null +++ b/test/SIM_test_varserv/RUN_test/err1_test.py @@ -0,0 +1,24 @@ +import trick + +from trick.unit_test import * + +def main(): + + trick.var_server_set_port(40000) + trick.var_ascii() + trick.real_time_enable() + trick.exec_set_software_frame(0.01) + + trick.exec_set_terminate_time(1000.0) + trick.var_server_set_source_address('localhost') + + varServerPort = trick.var_server_get_port() + test_output = ( os.getenv("TRICK_HOME") + "/trick_test/SIM_test_varserv.xml" ) + command = 'os.system("./models/test_client/test_client_err1 ' + str(varServerPort) + ' --gtest_output=xml:' + test_output + ' &")' + + # Start the test client after everything has been initialized (hopefully) + trick.add_read(1.0, command) + +if __name__ == "__main__": + main() + diff --git a/test/SIM_test_varserv/RUN_test/err2_test.py b/test/SIM_test_varserv/RUN_test/err2_test.py new file mode 100644 index 00000000..e5ce5927 --- /dev/null +++ b/test/SIM_test_varserv/RUN_test/err2_test.py @@ -0,0 +1,24 @@ +import trick + +from trick.unit_test import * + +def main(): + + trick.var_server_set_port(40000) + trick.var_ascii() + trick.real_time_enable() + trick.exec_set_software_frame(0.01) + + trick.exec_set_terminate_time(1000.0) + trick.var_server_set_source_address('localhost') + + varServerPort = trick.var_server_get_port() + test_output = ( os.getenv("TRICK_HOME") + "/trick_test/SIM_test_varserv.xml" ) + command = 'os.system("./models/test_client/test_client_err2 ' + str(varServerPort) + ' --gtest_output=xml:' + test_output + ' &")' + + # Start the test client after everything has been initialized (hopefully) + trick.add_read(1.0, command) + +if __name__ == "__main__": + main() + diff --git a/test/SIM_test_varserv/RUN_test/manual_test.py b/test/SIM_test_varserv/RUN_test/manual_test.py index 982de3ce..ac7013fa 100644 --- a/test/SIM_test_varserv/RUN_test/manual_test.py +++ b/test/SIM_test_varserv/RUN_test/manual_test.py @@ -5,7 +5,7 @@ from trick.unit_test import * def main(): - trick.var_server_set_port(4000) + trick.var_server_set_port(40000) trick.var_ascii() trick.real_time_enable() trick.exec_set_software_frame(0.01) diff --git a/test/SIM_test_varserv/RUN_test/unit_test.py b/test/SIM_test_varserv/RUN_test/unit_test.py index bfdd413a..d9148751 100644 --- a/test/SIM_test_varserv/RUN_test/unit_test.py +++ b/test/SIM_test_varserv/RUN_test/unit_test.py @@ -1,5 +1,4 @@ import trick -import socket from trick.unit_test import * @@ -10,12 +9,17 @@ def main(): trick.real_time_enable() trick.exec_set_software_frame(0.01) # trick.set_var_server_info_msg_on() + trick.set_var_server_log_on() - trick.var_server_create_tcp_socket('localhost', 49000) - trick.var_server_create_udp_socket('', 48000) - trick.var_server_create_multicast_socket('224.10.10.10','', 47000) + hostname = trick.var_server_get_hostname() - trick.exec_set_terminate_time(1000.0) + trick.var_server_set_socket_priority(7) + + trick.var_server_create_tcp_socket(hostname, 49000) + trick.var_server_create_udp_socket(hostname, 48000) + trick.var_server_create_multicast_socket('224.10.10.10',hostname, 47000) + + trick.exec_set_terminate_time(100.0) varServerPort = trick.var_server_get_port() test_output = ( os.getenv("TRICK_HOME") + "/trick_test/SIM_test_varserv.xml" ) diff --git a/test/SIM_test_varserv/S_overrides.mk b/test/SIM_test_varserv/S_overrides.mk index 36707008..96897111 100644 --- a/test/SIM_test_varserv/S_overrides.mk +++ b/test/SIM_test_varserv/S_overrides.mk @@ -2,7 +2,7 @@ TRICK_CFLAGS += -I./models TRICK_CXXFLAGS += -I./models -all: test_client +all: test_client test_client_err1 test_client_err2 clean: clean_test_client TEST_CLIENT_LIBS += -L${GTEST_HOME}/lib64 -L${GTEST_HOME}/lib -lgtest -lgtest_main -lpthread -L${TRICK_LIB_DIR} -ltrick_var_binary_parser @@ -10,6 +10,12 @@ TEST_CLIENT_LIBS += -L${GTEST_HOME}/lib64 -L${GTEST_HOME}/lib -lgtest -lgtest_ma test_client: models/test_client/test_client.cpp cd models/test_client; $(TRICK_CXX) test_client.cpp -o test_client $(TRICK_CXXFLAGS) $(TRICK_SYSTEM_CXXFLAGS) $(TRICK_TEST_FLAGS) -Wno-write-strings -Wno-sign-compare -I$(TRICK_HOME)/include $(TEST_CLIENT_LIBS) +test_client_err1: models/test_client/test_client_err1.cpp + cd models/test_client; $(TRICK_CXX) test_client_err1.cpp -o test_client_err1 $(TRICK_CXXFLAGS) $(TRICK_SYSTEM_CXXFLAGS) $(TRICK_TEST_FLAGS) -Wno-write-strings -Wno-sign-compare -I$(TRICK_HOME)/include $(TEST_CLIENT_LIBS) + +test_client_err2: models/test_client/test_client_err2.cpp + cd models/test_client; $(TRICK_CXX) test_client_err2.cpp -o test_client_err2 $(TRICK_CXXFLAGS) $(TRICK_SYSTEM_CXXFLAGS) $(TRICK_TEST_FLAGS) -Wno-write-strings -Wno-sign-compare -I$(TRICK_HOME)/include $(TEST_CLIENT_LIBS) + clean_test_client: - rm -f models/test_client/test_client - \ No newline at end of file + rm -f models/test_client/test_client models/test_client/test_client_err1 models/test_client/test_client_err2 + diff --git a/test/SIM_test_varserv/file_to_send.txt b/test/SIM_test_varserv/file_to_send.txt new file mode 100644 index 00000000..b34b754c --- /dev/null +++ b/test/SIM_test_varserv/file_to_send.txt @@ -0,0 +1 @@ +hi hello world \ No newline at end of file diff --git a/test/SIM_test_varserv/models/test_client/.gitignore b/test/SIM_test_varserv/models/test_client/.gitignore index 837a5b2a..25864a77 100644 --- a/test/SIM_test_varserv/models/test_client/.gitignore +++ b/test/SIM_test_varserv/models/test_client/.gitignore @@ -1 +1,3 @@ -test_client \ No newline at end of file +test_client +test_client_err1 +test_client_err2 \ No newline at end of file diff --git a/test/SIM_test_varserv/models/test_client/test_client.cpp b/test/SIM_test_varserv/models/test_client/test_client.cpp index 070606ed..fc36f713 100644 --- a/test/SIM_test_varserv/models/test_client/test_client.cpp +++ b/test/SIM_test_varserv/models/test_client/test_client.cpp @@ -1,240 +1,7 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - +#include "test_client.hh" #include "trick/var_binary_parser.hh" +#include -#define DOUBLE_TOL 1e-5 -#define SOCKET_BUF_SIZE 204800 - -// Pretend that gtest was kind enough to provide an EXPECT_FEQ operator with a tolerance -#define EXPECT_FEQ(a,b) EXPECT_LE(fabs(a - b), DOUBLE_TOL) - -class Socket { - - public: - - int max_retries = 10; - - Socket() : _initialized(false) {} - int init(std::string hostname, int port, int mode = SOCK_STREAM) { - _multicast_socket = false; - - _hostname = hostname; - _port = port; - int tries = 0; - - while ((_socket_fd = socket(AF_INET, mode, 0)) < 0 && tries < max_retries) tries++; - - if (_socket_fd < 0) { - std::cout << "Socket connection failed" << std::endl; - return -1; - } - - int value = 1; - if (setsockopt(_socket_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &value, (socklen_t) sizeof(value)) < 0) { - std::cout << "init_multicast: Socket option failed" << std::endl; - return -1; - } - - if (setsockopt(_socket_fd, SOL_SOCKET, SO_REUSEPORT, (char *) &value, sizeof(value)) < 0) { - perror("setsockopt: reuseport"); - } - - struct sockaddr_in serv_addr; - serv_addr.sin_family = AF_INET; - serv_addr.sin_port = htons(port); // convert to weird network byte format - serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); - - tries = 0; - int connection_status; - - while ((connection_status = connect(_socket_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr))) < 0 && tries < max_retries) tries++; - - if (connection_status < 0) { - std::cout << "Connection failed" << std::endl; - return -1; - } - - _initialized = true; - - return 0; - } - - #ifndef __APPLE__ - int init_multicast (std::string hostname, int port) { - _multicast_socket = true; - _hostname = hostname; - _port = port; - int tries = 0; - - _socket_fd = socket(AF_INET, SOCK_DGRAM, 0); - if (_socket_fd < 0) { - std::cout << "init_multicast: Socket open failed" << std::endl; - return -1; - } - - int value = 1; - if (setsockopt(_socket_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &value, (socklen_t) sizeof(value)) < 0) { - std::cout << "init_multicast: Socket option failed" << std::endl; - return -1; - } - - if (setsockopt(_socket_fd, SOL_SOCKET, SO_REUSEPORT, (char *) &value, sizeof(value)) < 0) { - perror("setsockopt: reuseport"); - } - - struct ip_mreq mreq; - // Use setsockopt() to request that the kernel join a multicast group - mreq.imr_multiaddr.s_addr = inet_addr(_hostname.c_str()); - mreq.imr_interface.s_addr = htonl(INADDR_ANY); - if (setsockopt(_socket_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreq, (socklen_t) sizeof(mreq)) < 0) { - std::cout << "init_multicast: setsockopt: ip_add_membership failed" << std::endl; - return -1; - } - - - struct sockaddr_in sockin ; - - // Set up local interface - // We must bind to the multicast address - sockin.sin_family = AF_INET; - sockin.sin_addr.s_addr = inet_addr(_hostname.c_str()); - sockin.sin_port = htons(_port); - - if ( bind(_socket_fd, (struct sockaddr *) &sockin, (socklen_t) sizeof(sockin)) < 0 ) { - std::cout << "init_multicast: bind failed" << std::endl; - return -1; - } - - char loopch = 1; - - if(setsockopt(_socket_fd, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loopch, sizeof(loopch)) < 0) - { - perror("Setting IP_MULTICAST_LOOP error"); - return -1; - } - - _initialized = true; - return 0; - } - - #endif - - int send (std::string message) { - // weird syntax I've never used before - since the send syscall that i'm trying to use is overloaded in this class, - // I have to append :: to the front of it so that the compiler knows to look in the global namespace - int success = ::send(_socket_fd, message.c_str(), message.size(), 0); - if (success < message.size()) { - std::cout << "init_multicast: Failed to send message" << std::endl; - } - return success; - } - - int operator<< (std::string message) { - return send(message); - } - - std::string receive () { - char buffer[SOCKET_BUF_SIZE]; - - int numBytes = recv(_socket_fd, buffer, SOCKET_BUF_SIZE, 0); - if (numBytes < 0) { - } else if (numBytes < SOCKET_BUF_SIZE) { - buffer[numBytes] = '\0'; - } - - return std::string(buffer); - } - - void operator>> (std::string& ret) { - ret = receive(); - } - - std::vector receive_bytes() { - unsigned char buffer[SOCKET_BUF_SIZE]; - int numBytes = recv(_socket_fd, buffer, SOCKET_BUF_SIZE, 0); - if (numBytes < 0) { - std::cout << "init_multicast: Failed to read from socket" << std::endl; - } - - std::vector bytes; - for (int i = 0; i < numBytes; i++) { - bytes.push_back(buffer[i]); - } - - return bytes; - } - - bool check_for_message_availible(long timeout_sec = 2) { - fd_set read_fd_set; - struct timeval timeout = { .tv_sec = timeout_sec, .tv_usec = 0 }; - FD_ZERO(&read_fd_set); - FD_SET(_socket_fd, &read_fd_set); - - // I have one question for the designers of the interface for this syscall: why - select(_socket_fd+1, &read_fd_set, NULL, NULL, &timeout); - - return FD_ISSET(_socket_fd, &read_fd_set); - } - - void clear_buffered_data() { - // Poll the socket - if (check_for_message_availible(0)) { - // Receive into the void if there is a message - receive(); - } - // otherwise no worries - } - - void close() { - ::close(_socket_fd); - } - - private: - int _port; - std::string _hostname; - int _socket_fd; - bool _initialized; - bool _multicast_socket; -}; - -class VariableServerTest : public ::testing::Test { - protected: - VariableServerTest() { - socket_status = socket.init("localhost", 40000); - if (socket_status == 0) { - std::stringstream request; - request << "trick.var_set_client_tag(\"VSTest"; - request << numSession++; - request << "\") \n"; - - socket << request.str(); - } - } - ~VariableServerTest() { - socket << "trick.var_exit()\n"; - socket.close(); - } - - Socket socket; - int socket_status; - - static int numSession; -}; class VariableServerUDPTest : public ::testing::Test { protected: @@ -285,12 +52,16 @@ class VariableServerTestAltListener : public ::testing::Test { static int numSession; }; +int VariableServerTest::numSession = 0; +int VariableServerUDPTest::numSession = 0; +int VariableServerTestAltListener::numSession = 0; + #ifndef __APPLE__ class VariableServerTestMulticast : public ::testing::Test { protected: VariableServerTestMulticast() { socket_status = socket.init("", 47000, SOCK_DGRAM); - multicast_listener.init_multicast("224.10.10.10", 47000); + socket_status = multicast_listener.init_multicast("224.10.10.10", 47000); if (socket_status == 0) { std::stringstream request; @@ -316,11 +87,6 @@ class VariableServerTestMulticast : public ::testing::Test { int VariableServerTestMulticast::numSession = 0; #endif -int VariableServerTest::numSession = 0; -int VariableServerUDPTest::numSession = 0; -int VariableServerTestAltListener::numSession = 0; - - /**********************************************************/ /* Helpful constants and functions */ /**********************************************************/ @@ -640,7 +406,7 @@ TEST_F (VariableServerTestAltListener, RestartAndSet) { load_checkpoint(socket, "RUN_test/reload_file.ckpnt"); socket >> reply; - expected = std::string("0\t-1234\t-123456\n"); + expected = std::string("0\t-1234\t-123456 {m}\n"); EXPECT_EQ(reply, expected); } @@ -797,6 +563,18 @@ TEST_F (VariableServerTest, Units) { EXPECT_EQ(strcmp_IgnoringWhiteSpace(reply, expected), 0); } +TEST_F (VariableServerTest, dump_info) { + if (socket_status != 0) { + FAIL(); + } + socket << "trick.var_add(\"vsx.vst.c\")\n"; + + std::string command = "trick.var_server_list_connections()\n"; + socket << command; + sleep(1); +} + + TEST_F (VariableServerTest, SendOnce) { if (socket_status != 0) { FAIL(); @@ -903,6 +681,153 @@ TEST_F (VariableServerTest, ListSize) { } +TEST_F (VariableServerTest, bitfields) { + if (socket_status != 0) { + FAIL(); + } + + socket << "trick.var_send_once(\"vsx.vst.my_bitfield.var1,vsx.vst.my_bitfield.var2,vsx.vst.my_bitfield.var3,vsx.vst.my_bitfield.var4\", 4)\n"; + std::string reply_str; + socket >> reply_str; + + EXPECT_EQ(strcmp_IgnoringWhiteSpace(reply_str, "5 3 -128 0 2112"), 0); + + socket << "trick.var_binary()\ntrick.var_send_once(\"vsx.vst.my_bitfield.var1,vsx.vst.my_bitfield.var2,vsx.vst.my_bitfield.var3,vsx.vst.my_bitfield.var4\", 4)\n"; + + std::vector reply = socket.receive_bytes(); + + ParsedBinaryMessage message; + try { + message.parse(reply); + } catch (const MalformedMessageException& ex) { + FAIL() << "Parser threw an exception: " << ex.what(); + } + + for (int i = 0; i < message.getNumVars(); i++) { + EXPECT_TRUE(message.variables[i].getType() == 12 || message.variables[i].getType() == 13); + } +} + +TEST_F (VariableServerTest, transmit_file) { + if (socket_status != 0) { + FAIL(); + } + + socket << "trick.send_file(\"file_to_send.txt\")\n"; + + std::stringstream file_contents; + + while (socket.check_for_message_availible()) { + socket >> file_contents; + } + + std::ifstream actual_file("file_to_send.txt"); + std::string expected_contents; + getline(actual_file, expected_contents); + + std::string expected_header = "2\t" + std::to_string(expected_contents.size()); + + std::string test_line; + + std::getline(file_contents, test_line); + EXPECT_EQ(test_line, expected_header); + + std::getline(file_contents, test_line); + EXPECT_EQ(test_line, expected_contents); +} + + +TEST_F (VariableServerTest, SendSieResource) { + if (socket_status != 0) { + FAIL(); + } + + socket << "trick.send_sie_resource()\n"; + + std::stringstream received_file; + + while (socket.check_for_message_availible()) { + socket >> received_file; + } + + std::string first_line; + std::getline(received_file, first_line); + + // We're not gonna bother comparing the contents + // Just check to see that the message type is correct + ASSERT_EQ(first_line.at(0), '2'); +} + +TEST_F (VariableServerTest, SendSieClass) { + if (socket_status != 0) { + FAIL(); + } + + socket << "trick.send_sie_class()\n"; + + std::stringstream received_file; + std::string file_temp; + + while (socket.check_for_message_availible()) { + socket >> file_temp; + received_file << file_temp; + } + + std::string first_line; + std::getline(received_file, first_line); + + // We're not gonna bother comparing the contents + // Just check to see that the message type is correct + ASSERT_EQ(first_line.at(0), '2'); +} + +TEST_F (VariableServerTest, SendSieEnum) { + if (socket_status != 0) { + FAIL(); + } + + socket << "trick.send_sie_enum()\n"; + + std::stringstream received_file; + std::string file_temp; + + while (socket.check_for_message_availible()) { + socket >> file_temp; + received_file << file_temp; + } + + std::string first_line; + std::getline(received_file, first_line); + + // We're not gonna bother comparing the contents + // Just check to see that the message type is correct + ASSERT_EQ(first_line.at(0), '2'); +} + + +TEST_F (VariableServerTest, SendSieClassTopLevelObjects) { + if (socket_status != 0) { + FAIL(); + } + + socket << "trick.send_sie_top_level_objects()\n"; + + std::stringstream received_file; + std::string file_temp; + + while (socket.check_for_message_availible()) { + socket >> file_temp; + received_file << file_temp; + } + + std::string first_line; + std::getline(received_file, first_line); + + // We're not gonna bother comparing the contents + // Just check to see that the message type is correct + ASSERT_EQ(first_line.at(0), '2'); +} + TEST_F (VariableServerTest, RestartAndSet) { if (socket_status != 0) { FAIL(); @@ -932,7 +857,7 @@ TEST_F (VariableServerTest, RestartAndSet) { load_checkpoint(socket, "RUN_test/reload_file.ckpnt"); socket >> reply; - expected = std::string("0\t-1234\t-123456\n"); + expected = std::string("0\t-1234\t-123456 {m}\n"); EXPECT_EQ(reply, expected); } @@ -979,6 +904,7 @@ TEST_F (VariableServerTest, Cycle) { socket << command; // Give it a cycle to update socket.receive(); + socket.receive(); double sim_time = parse_message_for_sim_time(socket.receive()); compare_cycle(num_cycles, sim_time); }; @@ -1042,7 +968,7 @@ TEST_F (VariableServerTest, Multicast) { FAIL() << "Multicast Socket failed to initialize."; } - int max_multicast_tries = 100; + int max_multicast_tries = 10000; int tries = 0; bool found = false; @@ -1156,7 +1082,28 @@ TEST_F (VariableServerTest, Freeze) { // Sim mode should be MODE_FREEZE == 1 ASSERT_EQ(mode, MODE_FREEZE); - // Not sure what else to test in copy mode VS_COPY_ASYNC + // Set to back to run mode + socket << "trick.exec_run()\n"; + + // The mode takes a little bit of time to change + wait_for_mode_change(MODE_RUN); + + // Sim mode should be MODE_RUN + ASSERT_EQ(mode, MODE_RUN); + + // Test VS_COPY_SCHEDULED - should see 1 message per frame + socket << "trick.var_set_copy_mode(1)\n"; + + + // Change back to freeze mode + // Set to Freeze mode + socket << "trick.exec_freeze()\n"; + + // The mode takes a little bit of time to change + wait_for_mode_change(MODE_FREEZE); + + // Sim mode should be MODE_FREEZE == 1 + ASSERT_EQ(mode, MODE_FREEZE); // Test VS_COPY_SCHEDULED - should see 1 message per frame socket << "trick.var_set_copy_mode(1)\n"; @@ -1243,24 +1190,26 @@ TEST_F (VariableServerTest, CopyAndWriteModes) { socket.clear_buffered_data(); // Copy mode 1 (VS_COPY_SCHEDULED) write mode 0 (VS_WRITE_ASYNC) - command = "trick.var_clear()\n" + test_vars_command + "trick.var_set_copy_mode(1)\ntrick.var_add(\"vsx.vst.c\")\ntrick.var_add(\"vsx.vst.d\")\ntrick.var_unpause()\n"; + command = "trick.var_clear()\n" + test_vars_command + "trick.var_sync(1)\ntrick.var_add(\"vsx.vst.c\")\ntrick.var_add(\"vsx.vst.d\")\ntrick.var_unpause()\n"; socket << command; // With copy mode VS_COPY_SCHEDULED and write mode VS_WRITE_ASYNC, the first reply will be all 0 since the main time to copy has not occurred yet. // Is this what we want? Maybe we should have more strict communication on whether the data has been staged so the first message isn't incorrect - // spin(); + + spin(socket); expected = "-1234 1234"; parse_message(socket.receive()); EXPECT_EQ(strcmp_IgnoringWhiteSpace(vars, expected), 0) << "Received: " << vars << " Expected: " << expected; + // Test that we see a difference of exactly expected_cycle (with a tolerance for floating point issues) int prev_frame = 0; double prev_time = 0; for (int i = 0; i < num_tests; i++) { prev_time = sim_time; parse_message(socket.receive()); - EXPECT_FEQ(sim_time - prev_time, expected_cycle); + EXPECT_LT(fmod(sim_time - prev_time, expected_cycle), DOUBLE_TOL); EXPECT_EQ(strcmp_IgnoringWhiteSpace(vars, expected), 0) << "Received: " << vars << " Expected: " << expected; } @@ -1273,6 +1222,8 @@ TEST_F (VariableServerTest, CopyAndWriteModes) { command = "trick.var_clear()\n" + test_vars_command + "trick.var_set_write_mode(1)\ntrick.var_add(\"vsx.vst.e\")\ntrick.var_add(\"vsx.vst.f\")\ntrick.var_unpause()\n"; socket << command; + spin(socket); + parse_message(socket.receive()); expected = "-123456 123456"; EXPECT_EQ(strcmp_IgnoringWhiteSpace(vars, expected), 0) << "Received: " << vars << " Expected: " << expected; @@ -1283,7 +1234,7 @@ TEST_F (VariableServerTest, CopyAndWriteModes) { for (int i = 0; i < num_tests; i++) { prev_time = sim_time; parse_message(socket.receive()); - EXPECT_FEQ(sim_time - prev_time, expected_cycle); + EXPECT_LT(fmod(sim_time - prev_time, expected_cycle), DOUBLE_TOL); EXPECT_EQ(strcmp_IgnoringWhiteSpace(vars, expected), 0) << "Received: " << vars << " Expected: " << expected; } @@ -1300,7 +1251,7 @@ TEST_F (VariableServerTest, CopyAndWriteModes) { socket << command; // Same issue as copy mode 1 write mode 0 - // spin(); + spin(socket); parse_message(socket.receive()); expected = "-1234567 123456789"; EXPECT_EQ(strcmp_IgnoringWhiteSpace(vars, expected), 0) << "Received: " << vars << " Expected: " << expected; @@ -1326,6 +1277,8 @@ TEST_F (VariableServerTest, CopyAndWriteModes) { command = "trick.var_clear()\n" + test_vars_command + "trick.var_set_copy_mode(2)\ntrick.var_set_write_mode(1)\ntrick.var_set_frame_multiple(" + std::to_string(frame_multiple) + ")\ntrick.var_set_frame_offset(" + std::to_string(frame_offset) + ")\ntrick.var_add(\"vsx.vst.i\")\ntrick.var_add(\"vsx.vst.j\")\ntrick.var_unpause()\n"; socket << command; + spin(socket); + expected = "1234.5677 -1234.56789"; parse_message(socket.receive()); EXPECT_EQ(strcmp_IgnoringWhiteSpace(vars, expected), 0) << "Received: " << vars << " Expected: " << expected; @@ -1345,6 +1298,27 @@ TEST_F (VariableServerTest, CopyAndWriteModes) { socket.clear_buffered_data(); } +TEST_F (VariableServerTest, var_set) { + if (socket_status != 0) { + FAIL(); + } + + std::string reply; + std::string expected; + + socket << "trick.var_add(\"vsx.vst.c\")\ntrick.var_add(\"vsx.vst.blocked_from_output\")\ntrick.var_add(\"vsx.vst.blocked_from_input\")\n"; + + socket >> reply; + expected = std::string("0\t-1234\t0\t500\n"); + + EXPECT_EQ(reply, expected); + + socket << "trick.var_set(\"vsx.vst.blocked_from_input\", 0)\n"; + socket << "trick.var_set(\"vsx.vst.blocked_from_output\", 0)\n"; + + +} + bool getCompleteBinaryMessage(ParsedBinaryMessage& message, Socket& socket, bool print = false) { static const int max_retries = 5; @@ -1412,84 +1386,6 @@ TEST_F (VariableServerTest, send_stdio) { } -#ifndef __APPLE__ - -TEST_F (VariableServerTest, MulticastAfterRestart) { - if (socket_status != 0) { - FAIL(); - } - - socket << "trick.var_server_set_user_tag(\"VSTestServer\")\n"; - - Socket multicast_socket; - if (multicast_socket.init_multicast("224.3.14.15", 9265) != 0) { - FAIL() << "Multicast Socket failed to initialize."; - } - - int max_multicast_tries = 100; - int tries = 0; - bool found = false; - - char expected_hostname[80]; - gethostname(expected_hostname, 80); - int expected_port = 40000; - - // get expected username - struct passwd *passp = getpwuid(getuid()) ; - char * expected_username; - if ( passp == NULL ) { - expected_username = strdup("unknown") ; - } else { - expected_username = strdup(passp->pw_name) ; - } - - // Don't care about PID, just check that it's > 0 - char * expected_sim_dir = "trick/test/SIM_test_varserv"; // Compare against the end of the string for this one - // Don't care about cmdline name - char * expected_input_file = "RUN_test/unit_test.py"; - // Don't care about trick_version - char * expected_tag = "VSTestServer"; - - // Variables to be populated by the multicast message - char actual_hostname[80]; - unsigned short actual_port = 0; - char actual_username[80]; - int actual_pid = 0; - char actual_sim_dir[80]; - char actual_cmdline_name[80]; - char actual_input_file[80]; - char actual_trick_version[80]; - char actual_tag[80]; - unsigned short actual_duplicate_port = 0; - - while (!found && tries++ < max_multicast_tries) { - std::string broadcast_data = multicast_socket.receive(); - sscanf(broadcast_data.c_str(), "%s\t%hu\t%s\t%d\t%s\t%s\t%s\t%s\t%s\t%hu\n" , actual_hostname, &actual_port , - actual_username , &actual_pid , actual_sim_dir , actual_cmdline_name , - actual_input_file , actual_trick_version , actual_tag, &actual_duplicate_port) ; - - if (strcmp(actual_hostname, expected_hostname) == 0 && strcmp(expected_tag, actual_tag) == 0) { - found = true; - EXPECT_STREQ(actual_hostname, expected_hostname); - EXPECT_EQ(actual_port, expected_port); - EXPECT_STREQ(actual_username, expected_username); - EXPECT_GT(actual_pid, 0); - std::string expected_sim_dir_str(expected_sim_dir); - std::string actual_sim_dir_str(actual_sim_dir); - std::string end_of_actual = actual_sim_dir_str.substr(actual_sim_dir_str.length() - expected_sim_dir_str.length(), actual_sim_dir_str.length()); - EXPECT_EQ(expected_sim_dir_str, end_of_actual); - EXPECT_STREQ(actual_input_file, expected_input_file); - EXPECT_STREQ(actual_tag, expected_tag); - EXPECT_EQ(actual_duplicate_port, expected_port); - } - } - - if (!found) - FAIL() << "Multicast message never received"; -} - -#endif - TEST_F (VariableServerTest, Binary) { if (socket_status != 0) { FAIL(); @@ -1666,4 +1562,4 @@ int main(int argc, char **argv) { socket << "trick.stop() \n"; return result; -} +} \ No newline at end of file diff --git a/test/SIM_test_varserv/models/test_client/test_client.hh b/test/SIM_test_varserv/models/test_client/test_client.hh new file mode 100644 index 00000000..010b7399 --- /dev/null +++ b/test/SIM_test_varserv/models/test_client/test_client.hh @@ -0,0 +1,235 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +#define DOUBLE_TOL 1e-5 +#define SOCKET_BUF_SIZE 204800 + +// Pretend that gtest was kind enough to provide an EXPECT_FEQ operator with a tolerance +#define EXPECT_FEQ(a,b) EXPECT_LE(fabs(a - b), DOUBLE_TOL) + +class Socket { + + public: + + int max_retries = 10; + + Socket() : _initialized(false) {} + int init(std::string hostname, int port, int mode = SOCK_STREAM) { + _multicast_socket = false; + + _hostname = hostname; + _port = port; + + _socket_fd = socket(AF_INET, mode, 0); + if (_socket_fd < 0) { + std::cout << "Socket connection failed" << std::endl; + return -1; + } + + int value = 1; + if (setsockopt(_socket_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &value, (socklen_t) sizeof(value)) < 0) { + std::cout << "init_multicast: Socket option failed" << std::endl; + return -1; + } + + struct sockaddr_in serv_addr; + serv_addr.sin_family = AF_INET; + serv_addr.sin_port = htons(port); // convert to weird network byte format + + if(inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr)<=0) { + std::cout << "Invalid address/ Address not supported" << std::endl; + return -1; + } + + int connection_status; + + connection_status = connect(_socket_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); + if (connection_status < 0) { + std::cout << "Connection failed" << std::endl; + return -1; + } + + _initialized = true; + + return 0; + } + #ifndef __APPLE__ + int init_multicast (std::string hostname, int port) { + _multicast_socket = true; + _hostname = hostname; + _port = port; + + _socket_fd = socket(AF_INET, SOCK_DGRAM, 0); + if (_socket_fd < 0) { + std::cout << "init_multicast: Socket open failed" << std::endl; + return -1; + } + + int value = 1; + if (setsockopt(_socket_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &value, (socklen_t) sizeof(value)) < 0) { + std::cout << "init_multicast: Socket option failed" << std::endl; + return -1; + } + + if (setsockopt(_socket_fd, SOL_SOCKET, SO_REUSEPORT, (char *) &value, sizeof(value)) < 0) { + perror("setsockopt: reuseport"); + } + + struct ip_mreq mreq; + // Use setsockopt() to request that the kernel join a multicast group + mreq.imr_multiaddr.s_addr = inet_addr(_hostname.c_str()); + mreq.imr_interface.s_addr = htonl(INADDR_ANY); + if (setsockopt(_socket_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreq, (socklen_t) sizeof(mreq)) < 0) { + std::cout << "init_multicast: setsockopt: ip_add_membership failed" << std::endl; + return -1; + } + + + struct sockaddr_in sockin ; + + // Set up local interface + // We must bind to the multicast address + sockin.sin_family = AF_INET; + sockin.sin_addr.s_addr = inet_addr(_hostname.c_str()); + sockin.sin_port = htons(_port); + + if ( bind(_socket_fd, (struct sockaddr *) &sockin, (socklen_t) sizeof(sockin)) < 0 ) { + std::cout << "init_multicast: bind failed" << std::endl; + return -1; + } + + char loopch = 1; + + if(setsockopt(_socket_fd, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loopch, sizeof(loopch)) < 0) + { + perror("Setting IP_MULTICAST_LOOP error"); + return -1; + } + + _initialized = true; + return 0; + } + + #endif + + int send (std::string message) { + // weird syntax I've never used before - since the send syscall that i'm trying to use is overloaded in this class, + // I have to append :: to the front of it so that the compiler knows to look in the global namespace + int success = ::send(_socket_fd, message.c_str(), message.size(), 0); + if (success < message.size()) { + std::cout << "init_multicast: Failed to send message" << std::endl; + } + return success; + } + + int operator<< (std::string message) { + return send(message); + } + + std::string receive () { + char buffer[SOCKET_BUF_SIZE]; + int numBytes = recv(_socket_fd, buffer, SOCKET_BUF_SIZE, 0); + if (numBytes < 0) { + } else if (numBytes < SOCKET_BUF_SIZE) { + buffer[numBytes] = '\0'; + } + + return std::string(buffer); + } + + void operator>> (std::string& ret) { + ret = receive(); + } + + void operator>> (std::ostream& stream) { + stream << receive(); + } + + std::vector receive_bytes() { + unsigned char buffer[SOCKET_BUF_SIZE]; + int numBytes = recv(_socket_fd, buffer, SOCKET_BUF_SIZE, 0); + if (numBytes < 0) { + std::cout << "init_multicast: Failed to read from socket" << std::endl; + } + + std::vector bytes; + for (int i = 0; i < numBytes; i++) { + bytes.push_back(buffer[i]); + } + + return bytes; + } + + bool check_for_message_availible(long timeout_sec = 2) { + fd_set read_fd_set; + struct timeval timeout = { .tv_sec = timeout_sec, .tv_usec = 0 }; + FD_ZERO(&read_fd_set); + FD_SET(_socket_fd, &read_fd_set); + + // I have one question for the designers of the interface for this syscall: why + select(_socket_fd+1, &read_fd_set, NULL, NULL, &timeout); + + return FD_ISSET(_socket_fd, &read_fd_set); + } + + void clear_buffered_data() { + // Poll the socket + if (check_for_message_availible(0)) { + // Receive into the void if there is a message + receive(); + } + // otherwise no worries + } + + void close() { + ::close(_socket_fd); + } + + private: + int _port; + std::string _hostname; + int _socket_fd; + bool _initialized; + bool _multicast_socket; + +}; + +class VariableServerTest : public ::testing::Test { + protected: + VariableServerTest() { + socket_status = socket.init("localhost", 40000); + if (socket_status == 0) { + std::stringstream request; + request << "trick.var_set_client_tag(\"VSTest"; + request << numSession++; + request << "\") \n"; + + socket << request.str(); + } + } + ~VariableServerTest() { + socket << "trick.var_exit()\n"; + socket.close(); + } + + Socket socket; + int socket_status; + + static int numSession; +}; \ No newline at end of file diff --git a/test/SIM_test_varserv/models/test_client/test_client_err1.cpp b/test/SIM_test_varserv/models/test_client/test_client_err1.cpp new file mode 100644 index 00000000..8d4e5d18 --- /dev/null +++ b/test/SIM_test_varserv/models/test_client/test_client_err1.cpp @@ -0,0 +1,13 @@ +#include "test_client.hh" + +int VariableServerTest::numSession = 0; + + +TEST_F (VariableServerTest, SendExecutiveException) { + if (socket_status != 0) { + FAIL(); + } + + std::string command = "trick.exec_terminate_with_return(1, \"" + std::string(__FILE__) + "\", " + std::to_string(__LINE__) + ", \"Error termination for testing\")\n"; + socket << command; +} diff --git a/test/SIM_test_varserv/models/test_client/test_client_err2.cpp b/test/SIM_test_varserv/models/test_client/test_client_err2.cpp new file mode 100644 index 00000000..f32af43d --- /dev/null +++ b/test/SIM_test_varserv/models/test_client/test_client_err2.cpp @@ -0,0 +1,13 @@ +#include "test_client.hh" + +int VariableServerTest::numSession = 0; + + +TEST_F (VariableServerTest, SendStdException) { + if (socket_status != 0) { + FAIL(); + } + + std::string command = "vsx.vst.throw_exception()\n"; + socket << command; +} diff --git a/test/SIM_test_varserv/models/varserv/include/VS.hh b/test/SIM_test_varserv/models/varserv/include/VS.hh index 5aa7c205..270f3a6c 100644 --- a/test/SIM_test_varserv/models/varserv/include/VS.hh +++ b/test/SIM_test_varserv/models/varserv/include/VS.hh @@ -13,13 +13,21 @@ PROGRAMMERS: ( (Lindsay Landry) (L3) (9-12-2013) ) ( (Jackie Dea #ifndef VS_HH #define VS_HH +typedef struct { + unsigned char var1 :3; + short var2 :8; + int var3 :9; + unsigned int var4 :12; +} bitfield; + + class VSTest { public: char a; unsigned char b; short c; unsigned short d; - int e; /* m xy-position */ + int e; /* m xy-position */ unsigned int f; long g; unsigned long h; @@ -31,10 +39,15 @@ class VSTest { int n[5]; std::string o; char * p; - wchar_t * q; /**< trick_chkpnt_io(**) */ + wchar_t * q; /**< trick_chkpnt_io(**) */ + + bitfield my_bitfield; int large_arr[4000]; + int blocked_from_input; /** trick_io(*o) */ + int blocked_from_output; /** trick_io(*i) */ + int status; VSTest(); @@ -48,6 +61,8 @@ class VSTest { int success(); int fail(); + void throw_exception(); + const char *status_messages[3] = { "Variable Server Test Success", "Variable Server Test Failure", diff --git a/test/SIM_test_varserv/models/varserv/src/VS.cpp b/test/SIM_test_varserv/models/varserv/src/VS.cpp index d9d2460f..54a4fbc1 100644 --- a/test/SIM_test_varserv/models/varserv/src/VS.cpp +++ b/test/SIM_test_varserv/models/varserv/src/VS.cpp @@ -8,9 +8,11 @@ PROGRAMMERS: ( (Lindsay Landry) (L3) (9-12-2013) (Jackie Deans) (CACI) (11-30-2022) ) *******************************************************************************/ #include +#include +#include + #include "../include/VS.hh" #include "trick/exec_proto.h" -#include VSTest::VSTest() {} VSTest::~VSTest() {} @@ -40,6 +42,19 @@ int VSTest::default_vars() { for (int i = 0; i < 4000; i++) { large_arr[i] = i; } + + /* Mixed Types */ + // 3,-128,0,2112 + my_bitfield.var1 = (1 << (3 - 1)) - 1; + my_bitfield.var2 = -(1 << (8 - 1)); + my_bitfield.var3 = 0; + my_bitfield.var4 = (1 << (12 - 1)) + (1 << 12/2);; + + + blocked_from_input = 500; + blocked_from_output = 1000; + + return 0; } int VSTest::init() { @@ -57,8 +72,18 @@ int VSTest::success() { return 0; } +void VSTest::throw_exception() { + throw std::logic_error("Pretend an error has occured for testing"); +} + int VSTest::shutdown() { std::cout << "Shutting down with status: " << status << " Message: " << status_messages[status] << std::endl; + + // Check that the blocked_from_input variable hasnt changed + if (blocked_from_input != 500 || blocked_from_output != 0) { + status = 1; + } + exec_terminate_with_return( status , __FILE__ , __LINE__ , status_messages[status] ) ; return(0); diff --git a/test_overrides.mk b/test_overrides.mk index 9e793302..aed1b3e6 100644 --- a/test_overrides.mk +++ b/test_overrides.mk @@ -6,4 +6,4 @@ include ${TRICK_HOME}/share/trick/makefiles/Makefile.common unexport TRICK_PYTHON_PATH sim_test: - python3 trickops.py --quiet + python3 trickops.py diff --git a/test_sims.yml b/test_sims.yml index 7f708f95..4582b819 100644 --- a/test_sims.yml +++ b/test_sims.yml @@ -312,11 +312,16 @@ SIM_test_varserv: path: test/SIM_test_varserv build_args: "-t" binary: "T_main_{cpu}_test.exe" - labels: - - retries_allowed runs: RUN_test/unit_test.py: + phase: 1 returns: 0 + RUN_test/err1_test.py: + phase: 2 + returns: 10 + RUN_test/err2_test.py: + phase: 3 + returns: 10 SIM_amoeba: path: trick_sims/Cannon/SIM_amoeba build_args: "-t" diff --git a/trick_sims/SIM_billiards/Modified_data/realtime.py b/trick_sims/SIM_billiards/Modified_data/realtime.py index 172b2f1b..a13797be 100644 --- a/trick_sims/SIM_billiards/Modified_data/realtime.py +++ b/trick_sims/SIM_billiards/Modified_data/realtime.py @@ -6,5 +6,10 @@ trick.itimer_enable() trick.exec_set_enable_freeze(True) # trick.exec_set_freeze_command(True) +# Print stack trace if signal is caught. default True +trick.exec_set_stack_trace(False) +# Attach a debugger to the process if a signal is caught. default False +trick.exec_set_attach_debugger(False) + simControlPanel = trick.SimControlPanel() trick.add_external_application(simControlPanel) diff --git a/trick_sims/SIM_billiards/RUN_break/input.py b/trick_sims/SIM_billiards/RUN_break/input.py index e6e05b7c..5b1a81d9 100644 --- a/trick_sims/SIM_billiards/RUN_break/input.py +++ b/trick_sims/SIM_billiards/RUN_break/input.py @@ -146,17 +146,3 @@ else : print('===================================') print('PoolTableDisplay needs to be built.') print('===================================') - - -# PoolTableDisplay_path = "models/graphics/java/dist/PoolTableDisplay.jar" - -# if (os.path.isfile(PoolTableDisplay_path)) : -# PoolTableDisplay_cmd = "java -jar " \ -# + PoolTableDisplay_path \ -# + " " + str(varServerPort) + " &" ; -# print(PoolTableDisplay_cmd) -# os.system( PoolTableDisplay_cmd); -# else : -# print('=================================================================================================') -# print('PoolTableDisplay needs to be built. Please \"cd\" into ../models/graphics/java and type \"make\".') -# print('=================================================================================================') diff --git a/trick_sims/SIM_billiards/models/graphics/cpp/main.cpp b/trick_sims/SIM_billiards/models/graphics/cpp/main.cpp index f0e98769..21290ce9 100644 --- a/trick_sims/SIM_billiards/models/graphics/cpp/main.cpp +++ b/trick_sims/SIM_billiards/models/graphics/cpp/main.cpp @@ -492,7 +492,7 @@ std::vector split (std::string& str, const char delim) { std::stringstream ss(str); std::string s; std::vector ret; - while (std::getline(ss, s, delim)) { + while (ss >> s) { ret.push_back(s); } return ret; @@ -526,9 +526,11 @@ template T getVar(Socket& socket, std::string varName) { std::string requestString = "trick.var_send_once(\"" + varName + "\")\n"; std::string reply; + // std::cout << "Request: " << requestString << std::endl; socket << requestString; socket >> reply; + // std::cout << "Reply: " << reply << std::endl; return stringConvert(split(reply, '\t')[1]); } @@ -554,10 +556,13 @@ std::vector getVarList(Socket& socket, std::string varName, int num) { } std::string requestString = "trick.var_send_once(\"" + totalRequest + "\", " + std::to_string(num) + ")\n"; + // std::cout << "Request: " << requestString << std::endl; std::string reply; socket << requestString; socket >> reply; + + // std::cout << "Reply: " << reply << std::endl; return trickResponseConvert(reply); } @@ -604,6 +609,13 @@ int main(int argc, char *argv[]) std::vector tablePoints = fold(table_x, table_y); Table table; + // std::cout << "TablePoints: "; + + for (auto point : tablePoints ) { + // std::cout << point << " "; + } + // std::cout << "\ttableShape: " << tableShape << std::endl; + table.addShape(tablePoints, Eigen::Vector3d(0.2, 0.6, 0.2), true, tableShape, layer_TABLE); // Make the rail - translate each point on the table out from center by railWidth @@ -697,7 +709,7 @@ int main(int argc, char *argv[]) std::string cueRequest = ""; std::string templateString = "dyn.table.applyCueForce(%.3f, %.3f) \n"; - char buf[128]; + char buf[2048]; snprintf(buf, sizeof(buf), templateString.c_str(), mouseX, mouseY); cueRequest += std::string(buf); socket << cueRequest; @@ -716,7 +728,10 @@ int main(int argc, char *argv[]) view->callback_pre_draw = [&](igl::opengl::glfw::Viewer& viewer) -> bool { // Look for new data and redraw + // std::cout << "Waiting vars" << std::endl; socket >> reply; + // std::cout << "Got vars: " << reply << std::endl; + std::vector replyData = trickResponseConvert(reply); if (replyData.size() <= 1) { @@ -804,7 +819,7 @@ int main(int argc, char *argv[]) std::string positionRequest = ""; char * templateString = "trick.var_add(\"dyn.table.balls[%d][0].pos._x\")\ntrick.var_add(\"dyn.table.balls[%d][0].pos._y\")\ntrick.var_add(\"dyn.table.balls[%d][0].inPlay\")\n"; for (int i = 0; i < numBalls; i++) { - char buf[128]; + char buf[2048]; snprintf(buf, sizeof(buf), templateString, i, i, i); positionRequest += std::string(buf); } diff --git a/trick_source/java/src/main/java/trick/simcontrol/SimControlApplication.java b/trick_source/java/src/main/java/trick/simcontrol/SimControlApplication.java index 1bd6cfec..ac436568 100644 --- a/trick_source/java/src/main/java/trick/simcontrol/SimControlApplication.java +++ b/trick_source/java/src/main/java/trick/simcontrol/SimControlApplication.java @@ -1572,6 +1572,7 @@ public class SimControlApplication extends TrickApplication implements PropertyC @Override protected void finished() { try { + System.out.println("Finished with health status socket channel"); if (healthStatusSocketChannel != null) { healthStatusSocketChannel.close() ; } @@ -1606,7 +1607,11 @@ public class SimControlApplication extends TrickApplication implements PropertyC if (statusSimcom != null) { - results = statusSimcom.get().split("\t"); + String resultsStr = statusSimcom.get(); + if (resultsStr == null) + break; + + results = resultsStr.split("\t"); ii = 1 ; // whenever there is data in statusSimcom socket, do something diff --git a/trick_source/java/src/main/java/trick/simcontrol/utils/SimControlActionController.java b/trick_source/java/src/main/java/trick/simcontrol/utils/SimControlActionController.java index 3b8086e8..0e2adc7c 100644 --- a/trick_source/java/src/main/java/trick/simcontrol/utils/SimControlActionController.java +++ b/trick_source/java/src/main/java/trick/simcontrol/utils/SimControlActionController.java @@ -295,7 +295,7 @@ public class SimControlActionController { simcom.put("trick.debug_pause_off()\n" ) ; } } catch (IOException e) { - System.out.println("Put failed!"); + System.out.println("Put failed! Exception: " + e.getMessage()); } } diff --git a/trick_source/sim_services/CMakeLists.txt b/trick_source/sim_services/CMakeLists.txt index 9f92fa95..f251bcce 100644 --- a/trick_source/sim_services/CMakeLists.txt +++ b/trick_source/sim_services/CMakeLists.txt @@ -123,13 +123,15 @@ set( SS_SRC JITInputFile/JITInputFile JITInputFile/jit_input_file_c_intf JSONVariableServer/JSONVariableServer - JSONVariableServer/JSONVariableServerThread + JSONVariableServer/JSONVariableServerSessionThread MasterSlave/MSSharedMem MasterSlave/MSSocket MasterSlave/Master MasterSlave/Slave Message/MessageCout Message/MessageFile + Message/MessageHSFile + Message/MessageCustomFile Message/MessageLCout Message/MessagePublisher Message/MessageSubscriber @@ -190,32 +192,31 @@ set( SS_SRC VariableServer/VariableReference VariableServer/VariableServer VariableServer/VariableServerListenThread - VariableServer/VariableServerThread - VariableServer/VariableServerThread_commands - VariableServer/VariableServerThread_connect - VariableServer/VariableServerThread_copy_data - VariableServer/VariableServerThread_copy_sim_data - VariableServer/VariableServerThread_create_socket - VariableServer/VariableServerThread_freeze_init - VariableServer/VariableServerThread_loop - VariableServer/VariableServerThread_restart - VariableServer/VariableServerThread_write_data - VariableServer/VariableServerThread_write_stdio - VariableServer/VariableServer_copy_data_freeze - VariableServer/VariableServer_copy_data_freeze_scheduled - VariableServer/VariableServer_copy_data_scheduled - VariableServer/VariableServer_copy_data_top + VariableServer/VariableServerSessionThread + VariableServer/VariableServerSessionThread_commands + VariableServer/VariableServerSessionThread_connect + VariableServer/VariableServerSessionThread_copy_data + VariableServer/VariableServerSessionThread_copy_sim_data + VariableServer/VariableServerSessionThread_create_socket + VariableServer/VariableServerSessionThread_freeze_init + VariableServer/VariableServerSessionThread_loop + VariableServer/VariableServerSessionThread_restart + VariableServer/VariableServerSessionThread_write_data + VariableServer/VariableServerSessionThread_write_stdio + VariableServer/VariableServer_copy_and_write_freeze + VariableServer/VariableServer_copy_and_write_freeze_scheduled + VariableServer/VariableServer_copy_and_write_scheduled + VariableServer/VariableServer_copy_and_write_top VariableServer/VariableServer_default_data VariableServer/VariableServer_freeze_init VariableServer/VariableServer_get_next_freeze_call_time VariableServer/VariableServer_get_next_sync_call_time - VariableServer/VariableServer_get_var_server_port + VariableServer/VariableServer_open_additional_servers VariableServer/VariableServer_init VariableServer/VariableServer_restart VariableServer/VariableServer_shutdown VariableServer/exit_var_thread VariableServer/var_server_ext - VariableServer/vs_format_ascii Zeroconf/Zeroconf mains/master ) diff --git a/trick_source/sim_services/CommandLineArguments/command_line_c_intf.cpp b/trick_source/sim_services/CommandLineArguments/command_line_c_intf.cpp index c6ea47ee..9181ab69 100644 --- a/trick_source/sim_services/CommandLineArguments/command_line_c_intf.cpp +++ b/trick_source/sim_services/CommandLineArguments/command_line_c_intf.cpp @@ -114,3 +114,6 @@ std::string & command_line_args_get_input_file_ref(void) { return(the_cmd_args->get_input_file_ref()) ; } +void create_path(const char* directory) { + Trick::CommandLineArguments::create_path(directory); +} \ No newline at end of file diff --git a/trick_source/sim_services/InputProcessor/Makefile_deps b/trick_source/sim_services/InputProcessor/Makefile_deps index e72afe5c..0a9e1319 100644 --- a/trick_source/sim_services/InputProcessor/Makefile_deps +++ b/trick_source/sim_services/InputProcessor/Makefile_deps @@ -133,8 +133,8 @@ object_${TRICK_HOST_CPU}/MTV.o: MTV.cpp ${TRICK_HOME}/include/trick/MTV.hh \ ${TRICK_HOME}/include/trick/trick_byteswap.h \ ${TRICK_HOME}/include/trick/trick_error_hndlr.h \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh \ ${TRICK_HOME}/include/trick/EventManager_c_intf.hh diff --git a/trick_source/sim_services/Message/Makefile b/trick_source/sim_services/Message/Makefile index 6f722048..80911292 100644 --- a/trick_source/sim_services/Message/Makefile +++ b/trick_source/sim_services/Message/Makefile @@ -1,3 +1,5 @@ include $(dir $(lastword $(MAKEFILE_LIST)))../../../share/trick/makefiles/Makefile.common include ${TRICK_HOME}/share/trick/makefiles/Makefile.tricklib -include Makefile_deps + +TRICK_CXXFLAGS += -std=c++11 \ No newline at end of file diff --git a/trick_source/sim_services/Message/Makefile_deps b/trick_source/sim_services/Message/Makefile_deps index c874fb4d..dca9b269 100644 --- a/trick_source/sim_services/Message/Makefile_deps +++ b/trick_source/sim_services/Message/Makefile_deps @@ -31,6 +31,8 @@ object_${TRICK_HOST_CPU}/Message_c_intf.o: Message_c_intf.cpp \ ${TRICK_HOME}/include/trick/MessageSubscriber.hh \ ${TRICK_HOME}/include/trick/MessageCout.hh \ ${TRICK_HOME}/include/trick/MessageFile.hh \ + ${TRICK_HOME}/include/trick/MessageHSFile.hh \ + ${TRICK_HOME}/include/trick/MessageCustomFile.hh \ ${TRICK_HOME}/include/trick/MessageTCDevice.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ ${TRICK_HOME}/include/trick/tc.h \ diff --git a/trick_source/sim_services/Message/MessageCustomFile.cpp b/trick_source/sim_services/Message/MessageCustomFile.cpp new file mode 100644 index 00000000..db17ab6b --- /dev/null +++ b/trick_source/sim_services/Message/MessageCustomFile.cpp @@ -0,0 +1,51 @@ + +#include +#include + + +#include "trick/MessageCustomFile.hh" +#include "trick/Message_proto.hh" + + +int Trick::MessageCustomFile::level_counter = 200; + +/** +@details +-# Initializes everything +*/ +Trick::MessageCustomFile::MessageCustomFile() : _this_level(level_counter++) { +} + +/** +@details +-# If enabled and level is this file's level + -# Write the header and message to the file stream +-# Flush the stream +*/ +void Trick::MessageCustomFile::update( unsigned int level , std::string header, std::string message ) { + if ( enabled && level == _this_level ) { + out_stream << header << message ; + out_stream.flush() ; + } +} + +int Trick::MessageCustomFile::get_level() const { + return _this_level; +} + +void Trick::MessageCustomFile::set_level(int level) { + if (level >= 0) + _this_level = level; +} + +void Trick::MessageCustomFile::set_name(std::string name) { + if (name == "") { + name = "CustomLog" + std::to_string(_this_level); + } + this->name = name; +} +void Trick::MessageCustomFile::set_file_name(std::string file_name) { + this->file_name = file_name; +} + + diff --git a/trick_source/sim_services/Message/MessageCustomManager.cpp b/trick_source/sim_services/Message/MessageCustomManager.cpp new file mode 100644 index 00000000..dbeda119 --- /dev/null +++ b/trick_source/sim_services/Message/MessageCustomManager.cpp @@ -0,0 +1,59 @@ +#include "trick/MessageCustomManager.hh" +#include "trick/Message_proto.hh" +#include "trick/memorymanager_c_intf.h" + +Trick::MessageCustomManager * the_message_custom_manager ; + +int open_custom_message_file(std::string file_name, std::string subscriber_name, int level) { + if (the_message_custom_manager == NULL) { + std::cout << "Problem: custom_message_manager not yet instantiated" << std::endl; + return -1; + } + + return the_message_custom_manager->open_custom_message_file(file_name, subscriber_name, level); +} + +/** + @brief The constructor. + */ +Trick::MessageCustomManager::MessageCustomManager() { + the_message_custom_manager = this; +} + +Trick::MessageCustomManager::~MessageCustomManager() { + the_message_custom_manager = NULL; + + for (Trick::MessageCustomFile * message_file : _custom_message_files) { + TMM_delete_var_a(message_file); + } +} + +int Trick::MessageCustomManager::open_custom_message_file(std::string file_name, std::string subscriber_name, int level) { + Trick::MessageCustomFile * new_message_file = (Trick::MessageCustomFile *) TMM_declare_var_s("Trick::MessageCustomFile"); + + new_message_file->set_level(level); + new_message_file->set_file_name(file_name); + new_message_file->set_name(subscriber_name); + new_message_file->init(); + + _custom_message_files.push_back(new_message_file); + + return new_message_file->get_level(); +} + +/** + @brief Output message to the file. + */ +void Trick::MessageCustomManager::update( unsigned int level , std::string header , std::string message ) { + for (auto message_file : _custom_message_files) { + message_file->update(level, header, message); + } +} + +int Trick::MessageCustomManager::restart( ) { + for (auto message_file : _custom_message_files) { + message_file->restart(); + } + + return 0; +} \ No newline at end of file diff --git a/trick_source/sim_services/Message/MessageFile.cpp b/trick_source/sim_services/Message/MessageFile.cpp index d22a3c5e..6411cca3 100644 --- a/trick_source/sim_services/Message/MessageFile.cpp +++ b/trick_source/sim_services/Message/MessageFile.cpp @@ -1,21 +1,22 @@ #include #include +#include #include "trick/MessageFile.hh" #include "trick/command_line_protos.h" +#include "trick/message_proto.h" +#include "trick/message_type.h" + + /** @details -# Initializes everything */ Trick::MessageFile::MessageFile() { - enabled = 1 ; color = 0 ; - file_name = "send_hs" ; - name = "file" ; - } /** @@ -29,18 +30,6 @@ int Trick::MessageFile::set_file_name(std::string in_name) { } -/** -@details --# Deletes the current output file --# Opens a new file with the name "file_name" -*/ -int Trick::MessageFile::init() { - - unlink((std::string(command_line_args_get_output_dir()) + "/" + file_name).c_str()) ; - out_stream.open((std::string(command_line_args_get_output_dir()) + "/" + file_name).c_str() , std::fstream::out | std::fstream::app ) ; - return(0) ; -} - /** @details -# If enabled and level < 100 @@ -56,6 +45,34 @@ void Trick::MessageFile::update( unsigned int level , std::string header, std::s } +/** +@details +-# Deletes the current output file +-# Opens a new file with the name "file_name" +*/ +int Trick::MessageFile::init() { + + unlink((std::string(command_line_args_get_output_dir()) + "/" + file_name).c_str()) ; + + // Create the directory if we need to + int pos = file_name.find_last_of("/"); + if (pos != std::string::npos) { + std::string dir = std::string(command_line_args_get_output_dir()) + "/" + file_name.substr(0, pos); + create_path(dir.c_str()); + } + + out_stream.open((std::string(command_line_args_get_output_dir()) + "/" + file_name).c_str() , std::fstream::out | std::fstream::app ) ; + if (!out_stream.is_open()) { + message_publish(MSG_ERROR, "Failed to open message file %s\n",file_name.c_str()); + } + return(0) ; +} + +int Trick::MessageFile::restart() { + out_stream.open((std::string(command_line_args_get_output_dir()) + "/" + file_name).c_str() , std::fstream::out | std::fstream::app ) ; + return(0) ; +} + /** @details -# Close the file stream diff --git a/trick_source/sim_services/Message/MessageHSFile.cpp b/trick_source/sim_services/Message/MessageHSFile.cpp new file mode 100644 index 00000000..7643d81d --- /dev/null +++ b/trick_source/sim_services/Message/MessageHSFile.cpp @@ -0,0 +1,32 @@ + +#include +#include + +#include "trick/MessageHSFile.hh" +#include "trick/command_line_protos.h" + +/** +@details +-# Initializes everything +*/ +Trick::MessageHSFile::MessageHSFile() { + name = "file"; + file_name = "send_hs"; +} + +/** +@details +-# If enabled and level < 100 + -# Write the header and message to the file stream + -# Flush the stream +*/ +void Trick::MessageHSFile::update( unsigned int level , std::string header, std::string message ) { + + if ( enabled && level < 100 ) { + out_stream << header << message ; + out_stream.flush() ; + } + +} + + diff --git a/trick_source/sim_services/Message/MessagePublisher.cpp b/trick_source/sim_services/Message/MessagePublisher.cpp index 3a108f95..a5b491bc 100644 --- a/trick_source/sim_services/Message/MessagePublisher.cpp +++ b/trick_source/sim_services/Message/MessagePublisher.cpp @@ -25,6 +25,10 @@ Trick::MessagePublisher::MessagePublisher() { } +Trick::MessagePublisher::~MessagePublisher() { + the_message_publisher = NULL; +} + void Trick::MessagePublisher::set_print_format() { num_digits = (int)round(log10((double)tics_per_sec)) ; snprintf(print_format, sizeof(print_format), "|L %%3d|%%s|%%s|%%s|T %%d|%%lld.%%0%dlld| ", num_digits) ; diff --git a/trick_source/sim_services/Message/Message_c_intf.cpp b/trick_source/sim_services/Message/Message_c_intf.cpp index 5e717e9a..0353c9dd 100644 --- a/trick_source/sim_services/Message/Message_c_intf.cpp +++ b/trick_source/sim_services/Message/Message_c_intf.cpp @@ -6,6 +6,7 @@ #include "trick/MessagePublisher.hh" #include "trick/MessageCout.hh" #include "trick/MessageFile.hh" +#include "trick/MessageCustomFile.hh" #include "trick/MessageTCDevice.hh" #include "trick/message_proto.h" diff --git a/trick_source/sim_services/MonteCarlo/MonteCarlo_master_shutdown.cpp b/trick_source/sim_services/MonteCarlo/MonteCarlo_master_shutdown.cpp index 38b67256..1b53cb75 100644 --- a/trick_source/sim_services/MonteCarlo/MonteCarlo_master_shutdown.cpp +++ b/trick_source/sim_services/MonteCarlo/MonteCarlo_master_shutdown.cpp @@ -6,6 +6,7 @@ #include "trick/message_type.h" #include "trick/tc_proto.h" #include "trick/exec_proto.h" +#include "trick/SysThread.hh" /** @par Detailed Design: */ void Trick::MonteCarlo::master_shutdown() { @@ -41,6 +42,8 @@ void Trick::MonteCarlo::master_shutdown() { except_return = -2 ; } + SysThread::ensureAllShutdown(); + exit(except_return); } diff --git a/trick_source/sim_services/MonteCarlo/MonteCarlo_slave_funcs.cpp b/trick_source/sim_services/MonteCarlo/MonteCarlo_slave_funcs.cpp index 777381cf..8fd5bf19 100644 --- a/trick_source/sim_services/MonteCarlo/MonteCarlo_slave_funcs.cpp +++ b/trick_source/sim_services/MonteCarlo/MonteCarlo_slave_funcs.cpp @@ -1,5 +1,7 @@ #include "trick/MonteCarlo.hh" +#include "trick/SysThread.hh" + /** @par Detailed Design: */ void Trick::MonteCarlo::slave_shutdown() { @@ -8,6 +10,8 @@ void Trick::MonteCarlo::slave_shutdown() { /**
  • Run the shutdown jobs and exit. */ run_queue(&slave_shutdown_queue, "in slave_shutdown queue") ; + SysThread::ensureAllShutdown(); + exit(0); } @@ -15,6 +19,9 @@ void Trick::MonteCarlo::slave_shutdown() { void Trick::MonteCarlo::slave_die() { /**
    • Kill any active child process executing a run and exit immediately. */ slave_kill_run(); + + SysThread::ensureAllShutdown(); + exit(1); } diff --git a/trick_source/sim_services/MonteCarlo/test/Makefile b/trick_source/sim_services/MonteCarlo/test/Makefile index c0f892b6..81df6675 100644 --- a/trick_source/sim_services/MonteCarlo/test/Makefile +++ b/trick_source/sim_services/MonteCarlo/test/Makefile @@ -9,7 +9,7 @@ include $(dir $(lastword $(MAKEFILE_LIST)))../../../../share/trick/makefiles/Mak # Flags passed to the preprocessor. TRICK_CXXFLAGS += -I$(GTEST_HOME)/include -I$(TRICK_HOME)/include -g -Wall -Wextra ${TRICK_TEST_FLAGS} -TRICK_LIBS = -L${TRICK_LIB_DIR} -ltrick -ltrick_pyip -ltrick_comm -ltrick_math -ltrick_mm -ltrick_units +TRICK_LIBS = -L${TRICK_LIB_DIR} -ltrick -ltrick_pyip -ltrick_comm -ltrick_math -ltrick_mm -ltrick_units -ltrick_var_binary_parser -ltrick_connection_handlers -ltrick_comm TRICK_EXEC_LINK_LIBS += -L${GTEST_HOME}/lib64 -L${GTEST_HOME}/lib -lgtest -lgtest_main # All tests produced by this Makefile. Remember to add new tests you diff --git a/trick_source/sim_services/RealtimeSync/RealtimeSync.cpp b/trick_source/sim_services/RealtimeSync/RealtimeSync.cpp index 2dfa1bef..e6b52b84 100644 --- a/trick_source/sim_services/RealtimeSync/RealtimeSync.cpp +++ b/trick_source/sim_services/RealtimeSync/RealtimeSync.cpp @@ -502,3 +502,6 @@ int Trick::RealtimeSync::shutdown() { return(0) ; } +bool Trick::RealtimeSync::is_active() { + return active; +} \ No newline at end of file diff --git a/trick_source/sim_services/RealtimeSync/RealtimeSync_c_intf.cpp b/trick_source/sim_services/RealtimeSync/RealtimeSync_c_intf.cpp index c3f38c7b..b473b96b 100644 --- a/trick_source/sim_services/RealtimeSync/RealtimeSync_c_intf.cpp +++ b/trick_source/sim_services/RealtimeSync/RealtimeSync_c_intf.cpp @@ -59,7 +59,7 @@ extern "C" int real_time_restart(long long ref_time ) { */ extern "C" int is_real_time() { if ( the_rts != NULL ) { - return((int)(the_rts->active)) ; + return((int)(the_rts->is_active())) ; } return(0) ; } diff --git a/trick_source/sim_services/ThreadBase/SysThread.cpp b/trick_source/sim_services/ThreadBase/SysThread.cpp index cfb98f6f..70484338 100644 --- a/trick_source/sim_services/ThreadBase/SysThread.cpp +++ b/trick_source/sim_services/ThreadBase/SysThread.cpp @@ -9,6 +9,7 @@ #endif #include #include +#include #include "trick/SysThread.hh" @@ -30,7 +31,13 @@ std::vector& Trick::SysThread::all_sys_threads() { return all_sys_threads; } -Trick::SysThread::SysThread(std::string in_name, bool sd) : self_deleting(sd), ThreadBase(in_name) { +Trick::SysThread::SysThread(std::string in_name) : ThreadBase(in_name) { + pthread_mutex_init(&_restart_pause_mutex, NULL); + pthread_cond_init(&_thread_has_paused_cv, NULL); + pthread_cond_init(&_thread_wakeup_cv, NULL); + _thread_has_paused = true; + _thread_should_pause = false; + pthread_mutex_lock(&(list_mutex())); all_sys_threads().push_back(this); pthread_mutex_unlock(&(list_mutex())); @@ -41,9 +48,6 @@ Trick::SysThread::~SysThread() { pthread_mutex_lock(&(list_mutex())); if (!shutdown_finished) { all_sys_threads().erase(std::remove(all_sys_threads().begin(), all_sys_threads().end(), this), all_sys_threads().end()); - if (all_sys_threads().size() == 0) { - pthread_cond_signal(&(list_empty_cv())); - } } pthread_mutex_unlock(&(list_mutex())); } @@ -51,27 +55,58 @@ Trick::SysThread::~SysThread() { int Trick::SysThread::ensureAllShutdown() { pthread_mutex_lock(&(list_mutex())); + // Cancel all threads for (SysThread * thread : all_sys_threads()) { thread->cancel_thread(); } - auto it = all_sys_threads().begin(); - while (it != all_sys_threads().end()){ - SysThread * thread = *it; - if (!(thread->self_deleting)) { - thread->join_thread(); - it = all_sys_threads().erase(it); - } else { - ++it; - } - } - - while (all_sys_threads().size() != 0) { - pthread_cond_wait(&(list_empty_cv()), &(list_mutex())); + // Join all threads + for (SysThread * thread : all_sys_threads()) { + thread->join_thread(); } + // Success! shutdown_finished = true; pthread_mutex_unlock(&(list_mutex())); return 0; +} + +// To be called from main thread +void Trick::SysThread::force_thread_to_pause() { + pthread_mutex_lock(&_restart_pause_mutex); + // Tell thread to pause, and wait for it to signal that it has + _thread_should_pause = true; + while (!_thread_has_paused) { + pthread_cond_wait(&_thread_has_paused_cv, &_restart_pause_mutex); + } + pthread_mutex_unlock(&_restart_pause_mutex); +} + +// To be called from main thread +void Trick::SysThread::unpause_thread() { + pthread_mutex_lock(&_restart_pause_mutex); + // Tell thread to wake up + _thread_should_pause = false; + pthread_cond_signal(&_thread_wakeup_cv); + pthread_mutex_unlock(&_restart_pause_mutex); +} + + +// To be called from this thread +void Trick::SysThread::test_pause() { + pthread_mutex_lock(&_restart_pause_mutex) ; + if (_thread_should_pause) { + // Tell main thread that we're pausing + _thread_has_paused = true; + pthread_cond_signal(&_thread_has_paused_cv); + + // Wait until we're told to wake up + while (_thread_should_pause) { + pthread_cond_wait(&_thread_wakeup_cv, &_restart_pause_mutex); + } + } + + _thread_has_paused = false; + pthread_mutex_unlock(&_restart_pause_mutex) ; } \ No newline at end of file diff --git a/trick_source/sim_services/ThreadBase/ThreadBase.cpp b/trick_source/sim_services/ThreadBase/ThreadBase.cpp index 11af01e7..8a1a5ba0 100644 --- a/trick_source/sim_services/ThreadBase/ThreadBase.cpp +++ b/trick_source/sim_services/ThreadBase/ThreadBase.cpp @@ -17,8 +17,12 @@ Trick::ThreadBase::ThreadBase(std::string in_name) : name(in_name) , pthread_id(0) , pid(0) , - rt_priority(0) + rt_priority(0), + created(false), + should_shutdown(false), + cancellable(true) { + pthread_mutex_init(&shutdown_mutex, NULL); #if __linux max_cpu = sysconf( _SC_NPROCESSORS_ONLN ) ; #ifdef CPU_ALLOC @@ -274,11 +278,17 @@ int Trick::ThreadBase::execute_priority() { int Trick::ThreadBase::create_thread() { + if (created) { + message_publish(MSG_ERROR, "create_thread called on thread %s (%p) which has already been started.\n", name.c_str(), this); + return 0; + } + pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); pthread_create(&pthread_id, &attr, Trick::ThreadBase::thread_helper , (void *)this); + created = true; #if __linux #ifdef __GNUC__ @@ -294,19 +304,56 @@ int Trick::ThreadBase::create_thread() { } int Trick::ThreadBase::cancel_thread() { + pthread_mutex_lock(&shutdown_mutex); + should_shutdown = true; + pthread_mutex_unlock(&shutdown_mutex); + if ( pthread_id != 0 ) { - pthread_cancel(pthread_id) ; + if (cancellable) + pthread_cancel(pthread_id) ; } return(0) ; } int Trick::ThreadBase::join_thread() { if ( pthread_id != 0 ) { - pthread_join(pthread_id, NULL) ; + if ((errno = pthread_join(pthread_id, NULL)) != 0) { + std::string msg = "Thread " + name + " had an error in join"; + perror(msg.c_str()); + } else { + pthread_id = 0; + } } return(0) ; } +void Trick::ThreadBase::test_shutdown() { + test_shutdown (NULL, NULL); +} + +void Trick::ThreadBase::test_shutdown(void (*exit_handler) (void *), void * exit_arg) { + pthread_mutex_lock(&shutdown_mutex); + if (should_shutdown) { + pthread_mutex_unlock(&shutdown_mutex); + + thread_shutdown(exit_handler, exit_arg); + } + pthread_mutex_unlock(&shutdown_mutex); +} + + +void Trick::ThreadBase::thread_shutdown() { + thread_shutdown (NULL, NULL); +} + +void Trick::ThreadBase::thread_shutdown(void (*exit_handler) (void *), void * exit_arg) { + if (exit_handler != NULL) { + exit_handler(exit_arg); + } + + pthread_exit(0); +} + void * Trick::ThreadBase::thread_helper( void * context ) { sigset_t sigs; diff --git a/trick_source/sim_services/VariableServer/Makefile_deps b/trick_source/sim_services/VariableServer/Makefile_deps index aac0a008..8d83bab4 100644 --- a/trick_source/sim_services/VariableServer/Makefile_deps +++ b/trick_source/sim_services/VariableServer/Makefile_deps @@ -1,4 +1,4 @@ -object_${TRICK_HOST_CPU}/VariableServerThread_loop.o: VariableServerThread_loop.cpp \ +object_${TRICK_HOST_CPU}/VariableServerSessionThread_loop.o: VariableServerSessionThread_loop.cpp \ ${TRICK_HOME}/include/trick/VariableServer.hh \ ${TRICK_HOME}/include/trick/tc.h \ ${TRICK_HOME}/include/trick/trick_error_hndlr.h \ @@ -10,9 +10,10 @@ object_${TRICK_HOST_CPU}/VariableServerThread_loop.o: VariableServerThread_loop. ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh \ ${TRICK_HOME}/include/trick/input_processor_proto.h \ ${TRICK_HOME}/include/trick/tc_proto.h \ @@ -24,8 +25,8 @@ object_${TRICK_HOST_CPU}/VariableServerThread_loop.o: VariableServerThread_loop. ${TRICK_HOME}/include/trick/ExecutiveException.hh \ ${TRICK_HOME}/include/trick/exec_proto.h \ ${TRICK_HOME}/include/trick/sim_mode.h -object_${TRICK_HOST_CPU}/VariableServer_copy_data_scheduled.o: \ - VariableServer_copy_data_scheduled.cpp \ +object_${TRICK_HOST_CPU}/VariableServer_copy_and_write_scheduled.o: \ + VariableServer_copy_and_write_scheduled.cpp \ ${TRICK_HOME}/include/trick/VariableServer.hh \ ${TRICK_HOME}/include/trick/tc.h \ ${TRICK_HOME}/include/trick/trick_error_hndlr.h \ @@ -37,9 +38,10 @@ object_${TRICK_HOST_CPU}/VariableServer_copy_data_scheduled.o: \ ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh \ ${TRICK_HOME}/include/trick/TrickConstant.hh object_${TRICK_HOST_CPU}/VariableServer_shutdown.o: VariableServer_shutdown.cpp \ @@ -54,12 +56,13 @@ object_${TRICK_HOST_CPU}/VariableServer_shutdown.o: VariableServer_shutdown.cpp ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh -object_${TRICK_HOST_CPU}/VariableServer_copy_data_freeze_scheduled.o: \ - VariableServer_copy_data_freeze_scheduled.cpp \ +object_${TRICK_HOST_CPU}/VariableServer_copy_and_write_freeze_scheduled.o: \ + VariableServer_copy_and_write_freeze_scheduled.cpp \ ${TRICK_HOME}/include/trick/VariableServer.hh \ ${TRICK_HOME}/include/trick/tc.h \ ${TRICK_HOME}/include/trick/trick_error_hndlr.h \ @@ -71,12 +74,13 @@ object_${TRICK_HOST_CPU}/VariableServer_copy_data_freeze_scheduled.o: \ ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh \ ${TRICK_HOME}/include/trick/TrickConstant.hh -object_${TRICK_HOST_CPU}/VariableReference.o: VariableReference.cpp \ +object_${TRICK_HOST_CPU}/VariableReference.o: \ ${TRICK_HOME}/include/trick/VariableServer.hh \ ${TRICK_HOME}/include/trick/tc.h \ ${TRICK_HOME}/include/trick/trick_error_hndlr.h \ @@ -88,9 +92,10 @@ object_${TRICK_HOST_CPU}/VariableReference.o: VariableReference.cpp \ ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh \ ${TRICK_HOME}/include/trick/memorymanager_c_intf.h \ ${TRICK_HOME}/include/trick/var.h \ @@ -111,12 +116,13 @@ object_${TRICK_HOST_CPU}/VariableServer_get_next_freeze_call_time.o: \ ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh \ ${TRICK_HOME}/include/trick/TrickConstant.hh -object_${TRICK_HOST_CPU}/VariableServerThread_write_stdio.o: VariableServerThread_write_stdio.cpp \ +object_${TRICK_HOST_CPU}/VariableServerSessionThread_write_stdio.o: VariableServerSessionThread_write_stdio.cpp \ ${TRICK_HOME}/include/trick/VariableServer.hh \ ${TRICK_HOME}/include/trick/tc.h \ ${TRICK_HOME}/include/trick/trick_error_hndlr.h \ @@ -128,9 +134,10 @@ object_${TRICK_HOST_CPU}/VariableServerThread_write_stdio.o: VariableServerThrea ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh \ ${TRICK_HOME}/include/trick/variable_server_message_types.h \ ${TRICK_HOME}/include/trick/tc_proto.h @@ -147,9 +154,10 @@ object_${TRICK_HOST_CPU}/VariableServer_get_next_sync_call_time.o: \ ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh \ ${TRICK_HOME}/include/trick/TrickConstant.hh object_${TRICK_HOST_CPU}/VariableServer_restart.o: VariableServer_restart.cpp \ @@ -164,9 +172,10 @@ object_${TRICK_HOST_CPU}/VariableServer_restart.o: VariableServer_restart.cpp \ ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh \ ${TRICK_HOME}/include/trick/message_proto.h \ ${TRICK_HOME}/include/trick/message_type.h \ @@ -183,9 +192,10 @@ object_${TRICK_HOST_CPU}/var_server_ext.o: var_server_ext.cpp \ ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh \ ${TRICK_HOME}/include/trick/exec_proto.h \ ${TRICK_HOME}/include/trick/sim_mode.h \ @@ -194,13 +204,14 @@ object_${TRICK_HOST_CPU}/var_server_ext.o: var_server_ext.cpp \ ${TRICK_HOME}/include/trick/memorymanager_c_intf.h \ ${TRICK_HOME}/include/trick/var.h \ ${TRICK_HOME}/include/trick/io_alloc.h -object_${TRICK_HOST_CPU}/VariableServerThread_create_socket.o: \ - VariableServerThread_create_socket.cpp \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ +object_${TRICK_HOST_CPU}/VariableServerSessionThread_create_socket.o: \ + VariableServerSessionThread_create_socket.cpp \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/tc.h \ ${TRICK_HOME}/include/trick/trick_error_hndlr.h \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/reference.h \ ${TRICK_HOME}/include/trick/attributes.h \ ${TRICK_HOME}/include/trick/parameter_types.h \ @@ -215,8 +226,9 @@ object_${TRICK_HOST_CPU}/VariableServerListenThread.o: VariableServerListenThrea ${TRICK_HOME}/include/trick/tc.h \ ${TRICK_HOME}/include/trick/trick_error_hndlr.h \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/reference.h \ ${TRICK_HOME}/include/trick/attributes.h \ ${TRICK_HOME}/include/trick/parameter_types.h \ @@ -229,7 +241,7 @@ object_${TRICK_HOST_CPU}/VariableServerListenThread.o: VariableServerListenThrea ${TRICK_HOME}/include/trick/command_line_protos.h \ ${TRICK_HOME}/include/trick/message_proto.h \ ${TRICK_HOME}/include/trick/message_type.h -object_${TRICK_HOST_CPU}/VariableServerThread_write_data.o: VariableServerThread_write_data.cpp \ +object_${TRICK_HOST_CPU}/VariableServerSessionThread_write_data.o: VariableServerSessionThread_write_data.cpp \ ${TRICK_HOME}/include/trick/VariableServer.hh \ ${TRICK_HOME}/include/trick/tc.h \ ${TRICK_HOME}/include/trick/trick_error_hndlr.h \ @@ -241,9 +253,10 @@ object_${TRICK_HOST_CPU}/VariableServerThread_write_data.o: VariableServerThread ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh \ ${TRICK_HOME}/include/trick/variable_server_message_types.h \ ${TRICK_HOME}/include/trick/bitfield_proto.h \ @@ -263,9 +276,10 @@ object_${TRICK_HOST_CPU}/VariableServer_init.o: VariableServer_init.cpp \ ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh \ ${TRICK_HOME}/include/trick/exec_proto.hh \ ${TRICK_HOME}/include/trick/Executive.hh \ @@ -277,7 +291,7 @@ object_${TRICK_HOST_CPU}/VariableServer_init.o: VariableServer_init.cpp \ ${TRICK_HOME}/include/trick/Threads.hh \ ${TRICK_HOME}/include/trick/ThreadTrigger.hh \ ${TRICK_HOME}/include/trick/sim_mode.h -object_${TRICK_HOST_CPU}/VariableServer_copy_data_freeze.o: VariableServer_copy_data_freeze.cpp \ +object_${TRICK_HOST_CPU}/VariableServer_copy_and_write_freeze.o: VariableServer_copy_and_write_freeze.cpp \ ${TRICK_HOME}/include/trick/VariableServer.hh \ ${TRICK_HOME}/include/trick/tc.h \ ${TRICK_HOME}/include/trick/trick_error_hndlr.h \ @@ -289,9 +303,10 @@ object_${TRICK_HOST_CPU}/VariableServer_copy_data_freeze.o: VariableServer_copy_ ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh object_${TRICK_HOST_CPU}/VariableServer_default_data.o: VariableServer_default_data.cpp \ ${TRICK_HOME}/include/trick/VariableServer.hh \ @@ -305,9 +320,10 @@ object_${TRICK_HOST_CPU}/VariableServer_default_data.o: VariableServer_default_d ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh object_${TRICK_HOST_CPU}/VariableServer_freeze_init.o: VariableServer_freeze_init.cpp \ ${TRICK_HOME}/include/trick/VariableServer.hh \ @@ -321,9 +337,10 @@ object_${TRICK_HOST_CPU}/VariableServer_freeze_init.o: VariableServer_freeze_ini ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh \ ${TRICK_HOME}/include/trick/variable_server_proto.h \ ${TRICK_HOME}/include/trick/TrickConstant.hh @@ -339,43 +356,26 @@ object_${TRICK_HOST_CPU}/VariableServer.o: VariableServer.cpp \ ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh \ ${TRICK_HOME}/include/trick/tc_proto.h -object_${TRICK_HOST_CPU}/vs_format_ascii.o: vs_format_ascii.cpp \ - ${TRICK_HOME}/include/trick/parameter_types.h \ - ${TRICK_HOME}/include/trick/attributes.h \ - ${TRICK_HOME}/include/trick/bitfield_proto.h \ - ${TRICK_HOME}/include/trick/wcs_ext.h \ - ${TRICK_HOME}/include/trick/VariableServer.hh \ - ${TRICK_HOME}/include/trick/tc.h \ - ${TRICK_HOME}/include/trick/trick_error_hndlr.h \ - ${TRICK_HOME}/include/trick/reference.h \ - ${TRICK_HOME}/include/trick/value.h \ - ${TRICK_HOME}/include/trick/dllist.h \ - ${TRICK_HOME}/include/trick/JobData.hh \ - ${TRICK_HOME}/include/trick/InstrumentBase.hh \ - ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ - ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ - ${TRICK_HOME}/include/trick/VariableServerListenThread.hh \ - ${TRICK_HOME}/include/trick/TrickConstant.hh -object_${TRICK_HOST_CPU}/VariableServerThread_restart.o: VariableServerThread_restart.cpp \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ +object_${TRICK_HOST_CPU}/VariableServerSessionThread_restart.o: VariableServerSessionThread_restart.cpp \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/tc.h \ ${TRICK_HOME}/include/trick/trick_error_hndlr.h \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/reference.h \ ${TRICK_HOME}/include/trick/attributes.h \ ${TRICK_HOME}/include/trick/parameter_types.h \ ${TRICK_HOME}/include/trick/value.h \ ${TRICK_HOME}/include/trick/dllist.h \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h -object_${TRICK_HOST_CPU}/VariableServer_copy_data_top.o: VariableServer_copy_data_top.cpp \ +object_${TRICK_HOST_CPU}/VariableServer_copy_and_write_top.o: VariableServer_copy_and_write_top.cpp \ ${TRICK_HOME}/include/trick/VariableServer.hh \ ${TRICK_HOME}/include/trick/tc.h \ ${TRICK_HOME}/include/trick/trick_error_hndlr.h \ @@ -387,9 +387,10 @@ object_${TRICK_HOST_CPU}/VariableServer_copy_data_top.o: VariableServer_copy_dat ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh object_${TRICK_HOST_CPU}/exit_var_thread.o: exit_var_thread.cpp \ ${TRICK_HOME}/include/trick/VariableServer.hh \ @@ -403,12 +404,13 @@ object_${TRICK_HOST_CPU}/exit_var_thread.o: exit_var_thread.cpp \ ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh \ ${TRICK_HOME}/include/trick/tc_proto.h -object_${TRICK_HOST_CPU}/VariableServerThread_copy_data.o: VariableServerThread_copy_data.cpp \ +object_${TRICK_HOST_CPU}/VariableServerSessionThread_copy_data.o: VariableServerSessionThread_copy_data.cpp \ ${TRICK_HOME}/include/trick/VariableServer.hh \ ${TRICK_HOME}/include/trick/tc.h \ ${TRICK_HOME}/include/trick/trick_error_hndlr.h \ @@ -420,16 +422,17 @@ object_${TRICK_HOST_CPU}/VariableServerThread_copy_data.o: VariableServerThread_ ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh \ ${TRICK_HOME}/include/trick/exec_proto.h \ ${TRICK_HOME}/include/trick/sim_mode.h \ ${TRICK_HOME}/include/trick/realtimesync_proto.h \ ${TRICK_HOME}/include/trick/Clock.hh \ ${TRICK_HOME}/include/trick/Timer.hh -object_${TRICK_HOST_CPU}/VariableServerThread_connect.o: VariableServerThread_connect.cpp \ +object_${TRICK_HOST_CPU}/VariableServerSessionThread_connect.o: VariableServerSessionThread_connect.cpp \ ${TRICK_HOME}/include/trick/VariableServer.hh \ ${TRICK_HOME}/include/trick/tc.h \ ${TRICK_HOME}/include/trick/trick_error_hndlr.h \ @@ -441,13 +444,14 @@ object_${TRICK_HOST_CPU}/VariableServerThread_connect.o: VariableServerThread_co ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh \ ${TRICK_HOME}/include/trick/release.h -object_${TRICK_HOST_CPU}/VariableServerThread_copy_sim_data.o: \ - VariableServerThread_copy_sim_data.cpp \ +object_${TRICK_HOST_CPU}/VariableServerSessionThread_copy_sim_data.o: \ + VariableServerSessionThread_copy_sim_data.cpp \ ${TRICK_HOME}/include/trick/VariableServer.hh \ ${TRICK_HOME}/include/trick/tc.h \ ${TRICK_HOME}/include/trick/trick_error_hndlr.h \ @@ -459,21 +463,23 @@ object_${TRICK_HOST_CPU}/VariableServerThread_copy_sim_data.o: \ ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh \ ${TRICK_HOME}/include/trick/memorymanager_c_intf.h \ ${TRICK_HOME}/include/trick/var.h \ ${TRICK_HOME}/include/trick/io_alloc.h \ ${TRICK_HOME}/include/trick/exec_proto.h \ ${TRICK_HOME}/include/trick/sim_mode.h -object_${TRICK_HOST_CPU}/VariableServerThread_freeze_init.o: VariableServerThread_freeze_init.cpp \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ +object_${TRICK_HOST_CPU}/VariableServerSession_freeze_init.o: VariableServerSession_freeze_init.cpp \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/tc.h \ ${TRICK_HOME}/include/trick/trick_error_hndlr.h \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/reference.h \ ${TRICK_HOME}/include/trick/attributes.h \ ${TRICK_HOME}/include/trick/parameter_types.h \ @@ -481,8 +487,8 @@ object_${TRICK_HOST_CPU}/VariableServerThread_freeze_init.o: VariableServerThrea ${TRICK_HOME}/include/trick/dllist.h \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ ${TRICK_HOME}/include/trick/TrickConstant.hh -object_${TRICK_HOST_CPU}/VariableServer_get_var_server_port.o: \ - VariableServer_get_var_server_port.cpp \ +object_${TRICK_HOST_CPU}/VariableServer_open_additional_servers.o: \ + VariableServer_open_additional_servers.cpp \ ${TRICK_HOME}/include/trick/VariableServer.hh \ ${TRICK_HOME}/include/trick/tc.h \ ${TRICK_HOME}/include/trick/trick_error_hndlr.h \ @@ -494,16 +500,18 @@ object_${TRICK_HOST_CPU}/VariableServer_get_var_server_port.o: \ ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh -object_${TRICK_HOST_CPU}/VariableServerThread.o: VariableServerThread.cpp \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ +object_${TRICK_HOST_CPU}/VariableServerSessionThread.o: VariableServerSessionThread.cpp \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/tc.h \ ${TRICK_HOME}/include/trick/trick_error_hndlr.h \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/reference.h \ ${TRICK_HOME}/include/trick/attributes.h \ ${TRICK_HOME}/include/trick/parameter_types.h \ @@ -513,7 +521,7 @@ object_${TRICK_HOST_CPU}/VariableServerThread.o: VariableServerThread.cpp \ ${TRICK_HOME}/include/trick/exec_proto.h \ ${TRICK_HOME}/include/trick/sim_mode.h \ ${TRICK_HOME}/include/trick/TrickConstant.hh -object_${TRICK_HOST_CPU}/VariableServerThread_loop.o: VariableServerThread_loop.cpp \ +object_${TRICK_HOST_CPU}/VariableServerSessionThread_loop.o: VariableServerSessionThread_loop.cpp \ ${TRICK_HOME}/include/trick/VariableServer.hh \ ${TRICK_HOME}/include/trick/tc.h \ ${TRICK_HOME}/include/trick/trick_error_hndlr.h \ @@ -525,9 +533,10 @@ object_${TRICK_HOST_CPU}/VariableServerThread_loop.o: VariableServerThread_loop. ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh \ ${TRICK_HOME}/include/trick/input_processor_proto.h \ ${TRICK_HOME}/include/trick/tc_proto.h \ @@ -539,8 +548,8 @@ object_${TRICK_HOST_CPU}/VariableServerThread_loop.o: VariableServerThread_loop. ${TRICK_HOME}/include/trick/ExecutiveException.hh \ ${TRICK_HOME}/include/trick/exec_proto.h \ ${TRICK_HOME}/include/trick/sim_mode.h -object_${TRICK_HOST_CPU}/VariableServer_copy_data_scheduled.o: \ - VariableServer_copy_data_scheduled.cpp \ +object_${TRICK_HOST_CPU}/VariableServer_copy_and_write_scheduled.o: \ + VariableServer_copy_and_write_scheduled.cpp \ ${TRICK_HOME}/include/trick/VariableServer.hh \ ${TRICK_HOME}/include/trick/tc.h \ ${TRICK_HOME}/include/trick/trick_error_hndlr.h \ @@ -552,9 +561,10 @@ object_${TRICK_HOST_CPU}/VariableServer_copy_data_scheduled.o: \ ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh \ ${TRICK_HOME}/include/trick/TrickConstant.hh object_${TRICK_HOST_CPU}/VariableServer_shutdown.o: VariableServer_shutdown.cpp \ @@ -569,12 +579,13 @@ object_${TRICK_HOST_CPU}/VariableServer_shutdown.o: VariableServer_shutdown.cpp ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh -object_${TRICK_HOST_CPU}/VariableServer_copy_data_freeze_scheduled.o: \ - VariableServer_copy_data_freeze_scheduled.cpp \ +object_${TRICK_HOST_CPU}/VariableServer_copy_and_write_freeze_scheduled.o: \ + VariableServer_copy_and_write_freeze_scheduled.cpp \ ${TRICK_HOME}/include/trick/VariableServer.hh \ ${TRICK_HOME}/include/trick/tc.h \ ${TRICK_HOME}/include/trick/trick_error_hndlr.h \ @@ -586,12 +597,15 @@ object_${TRICK_HOST_CPU}/VariableServer_copy_data_freeze_scheduled.o: \ ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh \ ${TRICK_HOME}/include/trick/TrickConstant.hh -object_${TRICK_HOST_CPU}/VariableReference.o: VariableReference.cpp \ +object_${TRICK_HOST_CPU}/VariableReference.o: \ +VariableReference.cpp \ +${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServer.hh \ ${TRICK_HOME}/include/trick/tc.h \ ${TRICK_HOME}/include/trick/trick_error_hndlr.h \ @@ -603,9 +617,10 @@ object_${TRICK_HOST_CPU}/VariableReference.o: VariableReference.cpp \ ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh \ ${TRICK_HOME}/include/trick/memorymanager_c_intf.h \ ${TRICK_HOME}/include/trick/var.h \ @@ -613,7 +628,7 @@ object_${TRICK_HOST_CPU}/VariableReference.o: VariableReference.cpp \ ${TRICK_HOME}/include/trick/wcs_ext.h \ ${TRICK_HOME}/include/trick/message_proto.h \ ${TRICK_HOME}/include/trick/message_type.h -object_${TRICK_HOST_CPU}/VariableServerThread_write_stdio.o: VariableServerThread_write_stdio.cpp \ +object_${TRICK_HOST_CPU}/VariableServerSessionThread_write_stdio.o: VariableServerSessionThread_write_stdio.cpp \ ${TRICK_HOME}/include/trick/VariableServer.hh \ ${TRICK_HOME}/include/trick/tc.h \ ${TRICK_HOME}/include/trick/trick_error_hndlr.h \ @@ -625,13 +640,14 @@ object_${TRICK_HOST_CPU}/VariableServerThread_write_stdio.o: VariableServerThrea ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh \ ${TRICK_HOME}/include/trick/variable_server_message_types.h \ ${TRICK_HOME}/include/trick/tc_proto.h -object_${TRICK_HOST_CPU}/VariableServerThread_commands.o: VariableServerThread_commands.cpp \ +object_${TRICK_HOST_CPU}/VariableServerSessionThread_commands.o: VariableServerSessionThread_commands.cpp \ ${TRICK_HOME}/include/trick/VariableServer.hh \ ${TRICK_HOME}/include/trick/tc.h \ ${TRICK_HOME}/include/trick/trick_error_hndlr.h \ @@ -643,9 +659,10 @@ object_${TRICK_HOST_CPU}/VariableServerThread_commands.o: VariableServerThread_c ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh \ ${TRICK_HOME}/include/trick/variable_server_message_types.h \ ${TRICK_HOME}/include/trick/memorymanager_c_intf.h \ @@ -674,9 +691,10 @@ object_${TRICK_HOST_CPU}/VariableServer_get_next_sync_call_time.o: \ ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh \ ${TRICK_HOME}/include/trick/TrickConstant.hh object_${TRICK_HOST_CPU}/VariableServer_restart.o: VariableServer_restart.cpp \ @@ -691,9 +709,10 @@ object_${TRICK_HOST_CPU}/VariableServer_restart.o: VariableServer_restart.cpp \ ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh \ ${TRICK_HOME}/include/trick/message_proto.h \ ${TRICK_HOME}/include/trick/message_type.h \ @@ -710,9 +729,10 @@ object_${TRICK_HOST_CPU}/var_server_ext.o: var_server_ext.cpp \ ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh \ ${TRICK_HOME}/include/trick/exec_proto.h \ ${TRICK_HOME}/include/trick/sim_mode.h \ @@ -721,13 +741,14 @@ object_${TRICK_HOST_CPU}/var_server_ext.o: var_server_ext.cpp \ ${TRICK_HOME}/include/trick/memorymanager_c_intf.h \ ${TRICK_HOME}/include/trick/var.h \ ${TRICK_HOME}/include/trick/io_alloc.h -object_${TRICK_HOST_CPU}/VariableServerThread_create_socket.o: \ - VariableServerThread_create_socket.cpp \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ +object_${TRICK_HOST_CPU}/VariableServerSessionThread_create_socket.o: \ + VariableServerSessionThread_create_socket.cpp \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/tc.h \ ${TRICK_HOME}/include/trick/trick_error_hndlr.h \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/reference.h \ ${TRICK_HOME}/include/trick/attributes.h \ ${TRICK_HOME}/include/trick/parameter_types.h \ @@ -741,9 +762,12 @@ object_${TRICK_HOST_CPU}/VariableServerListenThread.o: VariableServerListenThrea ${TRICK_HOME}/include/trick/VariableServerListenThread.hh \ ${TRICK_HOME}/include/trick/tc.h \ ${TRICK_HOME}/include/trick/trick_error_hndlr.h \ + ${TRICK_HOME}/include/trick/TCPClientListener.hh \ + ${TRICK_HOME}/include/trick/MulticastGroup.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/reference.h \ ${TRICK_HOME}/include/trick/attributes.h \ ${TRICK_HOME}/include/trick/parameter_types.h \ @@ -756,7 +780,7 @@ object_${TRICK_HOST_CPU}/VariableServerListenThread.o: VariableServerListenThrea ${TRICK_HOME}/include/trick/command_line_protos.h \ ${TRICK_HOME}/include/trick/message_proto.h \ ${TRICK_HOME}/include/trick/message_type.h -object_${TRICK_HOST_CPU}/VariableServerThread_write_data.o: VariableServerThread_write_data.cpp \ +object_${TRICK_HOST_CPU}/VariableServerSessionThread_write_data.o: VariableServerSessionThread_write_data.cpp \ ${TRICK_HOME}/include/trick/VariableServer.hh \ ${TRICK_HOME}/include/trick/tc.h \ ${TRICK_HOME}/include/trick/trick_error_hndlr.h \ @@ -768,9 +792,10 @@ object_${TRICK_HOST_CPU}/VariableServerThread_write_data.o: VariableServerThread ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh \ ${TRICK_HOME}/include/trick/variable_server_message_types.h \ ${TRICK_HOME}/include/trick/bitfield_proto.h \ @@ -790,9 +815,10 @@ object_${TRICK_HOST_CPU}/VariableServer_init.o: VariableServer_init.cpp \ ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh \ ${TRICK_HOME}/include/trick/exec_proto.hh \ ${TRICK_HOME}/include/trick/Executive.hh \ @@ -804,7 +830,7 @@ object_${TRICK_HOST_CPU}/VariableServer_init.o: VariableServer_init.cpp \ ${TRICK_HOME}/include/trick/Threads.hh \ ${TRICK_HOME}/include/trick/ThreadTrigger.hh \ ${TRICK_HOME}/include/trick/sim_mode.h -object_${TRICK_HOST_CPU}/VariableServer_copy_data_freeze.o: VariableServer_copy_data_freeze.cpp \ +object_${TRICK_HOST_CPU}/VariableServer_copy_and_write_freeze.o: VariableServer_copy_and_write_freeze.cpp \ ${TRICK_HOME}/include/trick/VariableServer.hh \ ${TRICK_HOME}/include/trick/tc.h \ ${TRICK_HOME}/include/trick/trick_error_hndlr.h \ @@ -816,9 +842,10 @@ object_${TRICK_HOST_CPU}/VariableServer_copy_data_freeze.o: VariableServer_copy_ ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh object_${TRICK_HOST_CPU}/VariableServer_default_data.o: VariableServer_default_data.cpp \ ${TRICK_HOME}/include/trick/VariableServer.hh \ @@ -832,9 +859,10 @@ object_${TRICK_HOST_CPU}/VariableServer_default_data.o: VariableServer_default_d ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh object_${TRICK_HOST_CPU}/VariableServer_freeze_init.o: VariableServer_freeze_init.cpp \ ${TRICK_HOME}/include/trick/VariableServer.hh \ @@ -848,9 +876,10 @@ object_${TRICK_HOST_CPU}/VariableServer_freeze_init.o: VariableServer_freeze_ini ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh \ ${TRICK_HOME}/include/trick/variable_server_proto.h \ ${TRICK_HOME}/include/trick/TrickConstant.hh @@ -866,43 +895,26 @@ object_${TRICK_HOST_CPU}/VariableServer.o: VariableServer.cpp \ ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh \ ${TRICK_HOME}/include/trick/tc_proto.h -object_${TRICK_HOST_CPU}/vs_format_ascii.o: vs_format_ascii.cpp \ - ${TRICK_HOME}/include/trick/parameter_types.h \ - ${TRICK_HOME}/include/trick/attributes.h \ - ${TRICK_HOME}/include/trick/bitfield_proto.h \ - ${TRICK_HOME}/include/trick/wcs_ext.h \ - ${TRICK_HOME}/include/trick/VariableServer.hh \ - ${TRICK_HOME}/include/trick/tc.h \ - ${TRICK_HOME}/include/trick/trick_error_hndlr.h \ - ${TRICK_HOME}/include/trick/reference.h \ - ${TRICK_HOME}/include/trick/value.h \ - ${TRICK_HOME}/include/trick/dllist.h \ - ${TRICK_HOME}/include/trick/JobData.hh \ - ${TRICK_HOME}/include/trick/InstrumentBase.hh \ - ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ - ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ - ${TRICK_HOME}/include/trick/VariableServerListenThread.hh \ - ${TRICK_HOME}/include/trick/TrickConstant.hh -object_${TRICK_HOST_CPU}/VariableServerThread_restart.o: VariableServerThread_restart.cpp \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ +object_${TRICK_HOST_CPU}/VariableServerSessionThread_restart.o: VariableServerSessionThread_restart.cpp \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/tc.h \ ${TRICK_HOME}/include/trick/trick_error_hndlr.h \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/reference.h \ ${TRICK_HOME}/include/trick/attributes.h \ ${TRICK_HOME}/include/trick/parameter_types.h \ ${TRICK_HOME}/include/trick/value.h \ ${TRICK_HOME}/include/trick/dllist.h \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h -object_${TRICK_HOST_CPU}/VariableServer_copy_data_top.o: VariableServer_copy_data_top.cpp \ +object_${TRICK_HOST_CPU}/VariableServer_copy_and_write_top.o: VariableServer_copy_and_write_top.cpp \ ${TRICK_HOME}/include/trick/VariableServer.hh \ ${TRICK_HOME}/include/trick/tc.h \ ${TRICK_HOME}/include/trick/trick_error_hndlr.h \ @@ -914,9 +926,10 @@ object_${TRICK_HOST_CPU}/VariableServer_copy_data_top.o: VariableServer_copy_dat ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh object_${TRICK_HOST_CPU}/exit_var_thread.o: exit_var_thread.cpp \ ${TRICK_HOME}/include/trick/VariableServer.hh \ @@ -930,12 +943,13 @@ object_${TRICK_HOST_CPU}/exit_var_thread.o: exit_var_thread.cpp \ ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh \ ${TRICK_HOME}/include/trick/tc_proto.h -object_${TRICK_HOST_CPU}/VariableServerThread_copy_data.o: VariableServerThread_copy_data.cpp \ +object_${TRICK_HOST_CPU}/VariableServerSessionThread_copy_data.o: VariableServerSessionThread_copy_data.cpp \ ${TRICK_HOME}/include/trick/VariableServer.hh \ ${TRICK_HOME}/include/trick/tc.h \ ${TRICK_HOME}/include/trick/trick_error_hndlr.h \ @@ -947,9 +961,10 @@ object_${TRICK_HOST_CPU}/VariableServerThread_copy_data.o: VariableServerThread_ ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh \ ${TRICK_HOME}/include/trick/exec_proto.h \ ${TRICK_HOME}/include/trick/sim_mode.h \ @@ -969,12 +984,13 @@ object_${TRICK_HOST_CPU}/VariableServer_get_next_freeze_call_time.o: \ ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh \ ${TRICK_HOME}/include/trick/TrickConstant.hh -object_${TRICK_HOST_CPU}/VariableServerThread_connect.o: VariableServerThread_connect.cpp \ +object_${TRICK_HOST_CPU}/VariableServerSessionThread_connect.o: VariableServerSessionThread_connect.cpp \ ${TRICK_HOME}/include/trick/VariableServer.hh \ ${TRICK_HOME}/include/trick/tc.h \ ${TRICK_HOME}/include/trick/trick_error_hndlr.h \ @@ -986,13 +1002,14 @@ object_${TRICK_HOST_CPU}/VariableServerThread_connect.o: VariableServerThread_co ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh \ ${TRICK_HOME}/include/trick/release.h -object_${TRICK_HOST_CPU}/VariableServerThread_copy_sim_data.o: \ - VariableServerThread_copy_sim_data.cpp \ +object_${TRICK_HOST_CPU}/VariableServerSessionThread_copy_sim_data.o: \ + VariableServerSessionThread_copy_sim_data.cpp \ ${TRICK_HOME}/include/trick/VariableServer.hh \ ${TRICK_HOME}/include/trick/tc.h \ ${TRICK_HOME}/include/trick/trick_error_hndlr.h \ @@ -1004,21 +1021,23 @@ object_${TRICK_HOST_CPU}/VariableServerThread_copy_sim_data.o: \ ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh \ ${TRICK_HOME}/include/trick/memorymanager_c_intf.h \ ${TRICK_HOME}/include/trick/var.h \ ${TRICK_HOME}/include/trick/io_alloc.h \ ${TRICK_HOME}/include/trick/exec_proto.h \ ${TRICK_HOME}/include/trick/sim_mode.h -object_${TRICK_HOST_CPU}/VariableServerThread_freeze_init.o: VariableServerThread_freeze_init.cpp \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ +object_${TRICK_HOST_CPU}/VariableServerSessionThread_freeze_init.o: VariableServerSessionThread_freeze_init.cpp \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/tc.h \ ${TRICK_HOME}/include/trick/trick_error_hndlr.h \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/reference.h \ ${TRICK_HOME}/include/trick/attributes.h \ ${TRICK_HOME}/include/trick/parameter_types.h \ @@ -1026,8 +1045,8 @@ object_${TRICK_HOST_CPU}/VariableServerThread_freeze_init.o: VariableServerThrea ${TRICK_HOME}/include/trick/dllist.h \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ ${TRICK_HOME}/include/trick/TrickConstant.hh -object_${TRICK_HOST_CPU}/VariableServer_get_var_server_port.o: \ - VariableServer_get_var_server_port.cpp \ +object_${TRICK_HOST_CPU}/VariableServer_open_additional_servers.o: \ + VariableServer_open_additional_servers.cpp \ ${TRICK_HOME}/include/trick/VariableServer.hh \ ${TRICK_HOME}/include/trick/tc.h \ ${TRICK_HOME}/include/trick/trick_error_hndlr.h \ @@ -1039,16 +1058,18 @@ object_${TRICK_HOST_CPU}/VariableServer_get_var_server_port.o: \ ${TRICK_HOME}/include/trick/JobData.hh \ ${TRICK_HOME}/include/trick/InstrumentBase.hh \ ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/VariableServerListenThread.hh -object_${TRICK_HOST_CPU}/VariableServerThread.o: VariableServerThread.cpp \ - ${TRICK_HOME}/include/trick/VariableServerThread.hh \ +object_${TRICK_HOST_CPU}/VariableServerSessionThread.o: VariableServerSessionThread.cpp \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ ${TRICK_HOME}/include/trick/tc.h \ ${TRICK_HOME}/include/trick/trick_error_hndlr.h \ ${TRICK_HOME}/include/trick/ThreadBase.hh \ - ${TRICK_HOME}/include/trick/VariableServerReference.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ ${TRICK_HOME}/include/trick/reference.h \ ${TRICK_HOME}/include/trick/attributes.h \ ${TRICK_HOME}/include/trick/parameter_types.h \ @@ -1058,3 +1079,51 @@ object_${TRICK_HOST_CPU}/VariableServerThread.o: VariableServerThread.cpp \ ${TRICK_HOME}/include/trick/exec_proto.h \ ${TRICK_HOME}/include/trick/sim_mode.h \ ${TRICK_HOME}/include/trick/TrickConstant.hh +object_${TRICK_HOST_CPU}/VariableServerSession.o: VariableServerSession.cpp \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ + ${TRICK_HOME}/include/trick/tc.h \ + ${TRICK_HOME}/include/trick/trick_error_hndlr.h \ + ${TRICK_HOME}/include/trick/ThreadBase.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ + ${TRICK_HOME}/include/trick/reference.h \ + ${TRICK_HOME}/include/trick/attributes.h \ + ${TRICK_HOME}/include/trick/parameter_types.h \ + ${TRICK_HOME}/include/trick/value.h \ + ${TRICK_HOME}/include/trick/dllist.h \ + ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ + ${TRICK_HOME}/include/trick/exec_proto.h \ + ${TRICK_HOME}/include/trick/sim_mode.h \ + ${TRICK_HOME}/include/trick/TrickConstant.hh + object_${TRICK_HOST_CPU}/TCConnection.o: TCConnection.cpp \ + ${TRICK_HOME}/include/trick/TCConnection.hh \ + ${TRICK_HOME}/include/trick/ClientConnection.hh \ + ${TRICK_HOME}/include/trick/TCPClientListener.hh \ + ${TRICK_HOME}/include/trick/VariableServerSessionThread.hh \ + ${TRICK_HOME}/include/trick/tc.h \ + ${TRICK_HOME}/include/trick/trick_error_hndlr.h \ + ${TRICK_HOME}/include/trick/ThreadBase.hh \ + ${TRICK_HOME}/include/trick/VariableReference.hh \ + ${TRICK_HOME}/include/trick/VariableServerSession.hh \ + ${TRICK_HOME}/include/trick/reference.h \ + ${TRICK_HOME}/include/trick/attributes.h \ + ${TRICK_HOME}/include/trick/parameter_types.h \ + ${TRICK_HOME}/include/trick/value.h \ + ${TRICK_HOME}/include/trick/dllist.h \ + ${TRICK_HOME}/include/trick/variable_server_sync_types.h \ + ${TRICK_HOME}/include/trick/exec_proto.h \ + ${TRICK_HOME}/include/trick/sim_mode.h \ + ${TRICK_HOME}/include/trick/TrickConstant.hh \ + ${TRICK_HOME}/include/trick/tc_proto.h +object_${TRICK_HOST_CPU}/TCPClientListener.o: TCPClientListener.cpp \ + ${TRICK_HOME}/include/trick/TCPConnection.hh \ + ${TRICK_HOME}/include/trick/ClientConnection.hh \ + ${TRICK_HOME}/include/trick/TCPClientListener.hh \ + ${TRICK_HOME}/include/trick/tc.h \ + ${TRICK_HOME}/include/trick/trick_error_hndlr.h \ + ${TRICK_HOME}/include/trick/tc_proto.h + object_${TRICK_HOST_CPU}/MulticastGroup.o: MulticastGroup.cpp \ + ${TRICK_HOME}/include/trick/MulticastGroup.hh \ + ${TRICK_HOME}/include/trick/tc.h \ + ${TRICK_HOME}/include/trick/trick_error_hndlr.h \ + ${TRICK_HOME}/include/trick/tc_proto.h diff --git a/trick_source/sim_services/VariableServer/VariableReference.cpp b/trick_source/sim_services/VariableServer/VariableReference.cpp index 925adec3..4329dbe0 100644 --- a/trick_source/sim_services/VariableServer/VariableReference.cpp +++ b/trick_source/sim_services/VariableServer/VariableReference.cpp @@ -1,80 +1,737 @@ - #include #include #include -#include "trick/VariableServer.hh" +#include // for fpclassify +#include // for setprecision +#include +#include + +#include "trick/VariableReference.hh" #include "trick/memorymanager_c_intf.h" #include "trick/wcs_ext.h" +#include "trick/map_trick_units_to_udunits.hh" #include "trick/message_proto.h" #include "trick/message_type.h" - -Trick::VariableReference::VariableReference(REF2 * in_ref ) { +#include "trick/UdUnits.hh" +#include "trick/bitfield_proto.h" +#include "trick/trick_byteswap.h" - int k ; - - // VariableReference copy setup: set address & size to copy into buffer - conversion_factor = cv_get_trivial() ; - - ref = in_ref ; - address = ref->address ; - size = ref->attr->size ; - // char* and wchar* in Trick 7 have a type of string and wstring, respectively - // but in Trick 10 they are type char and wchar (probably John Penn's fault), - // so we need to keep track that they are really string and wstring - string_type = ref->attr->type ; - need_deref = false ; - - if ( ref->num_index == ref->attr->num_index ) { - // single value - } else if ( ref->attr->index[ref->attr->num_index - 1].size != 0 ) { - // fixed array - for ( k = ref->attr->num_index-1; k > ref->num_index-1 ; k-- ) { - size *= ref->attr->index[k].size ; - } - } else { - // arrays with pointers - if ((ref->attr->num_index - ref->num_index) > 1 ) { - // you cannot request more than one dimension because they are not contiguous - message_publish(MSG_ERROR, "Variable Server Error: var_add(%s) requests more than one dimension of dynamic array.\n", ref->reference); - message_publish(MSG_ERROR, "Data is not contiguous so returned values are unpredictable.\n") ; - } - if ( ref->attr->type == TRICK_CHARACTER ) { - // treat char* like a c++ string (see below) - string_type = TRICK_STRING ; - need_deref = true; - } else if ( ref->attr->type == TRICK_WCHAR ) { - // treat wchar_t* like a wstring (see below) - string_type = TRICK_WSTRING ; - } else { - need_deref = true ; - size *= get_size((char*)address) ; - } - } - - // handle strings: set a max buffer size, the copy size may vary so will be set in copy_sim_data - if (( string_type == TRICK_STRING ) || ( string_type == TRICK_WSTRING )) { - size = MAX_ARRAY_LENGTH ; - } - - buffer_in = calloc( size, 1 ) ; - buffer_out = calloc( size, 1 ) ; - +// Static variables to be addresses that are known to be the error ref address +int Trick::VariableReference::_bad_ref_int = 0 ; +int Trick::VariableReference::_do_not_resolve_bad_ref_int = 0 ; +REF2* Trick::VariableReference::make_error_ref(std::string in_name) { + REF2* new_ref; + new_ref = (REF2*)calloc(1, sizeof(REF2)); + new_ref->reference = strdup(in_name.c_str()) ; + new_ref->units = NULL ; + new_ref->address = (char *)&_bad_ref_int ; + new_ref->attr = (ATTRIBUTES*)calloc(1, sizeof(ATTRIBUTES)) ; + new_ref->attr->type = TRICK_NUMBER_OF_TYPES ; + new_ref->attr->units = (char *)"--" ; + new_ref->attr->size = sizeof(int) ; + return new_ref; } -std::ostream& Trick::operator<< (std::ostream& s, const Trick::VariableReference& vref) { +REF2* Trick::VariableReference::make_do_not_resolve_ref(std::string in_name) { + REF2* new_ref; + new_ref = (REF2*)calloc(1, sizeof(REF2)); + new_ref->reference = strdup(in_name.c_str()) ; + new_ref->units = NULL ; + new_ref->address = (char *)&_do_not_resolve_bad_ref_int ; + new_ref->attr = (ATTRIBUTES*)calloc(1, sizeof(ATTRIBUTES)) ; + new_ref->attr->type = TRICK_NUMBER_OF_TYPES ; + new_ref->attr->units = (char *)"--" ; + new_ref->attr->size = sizeof(int) ; + return new_ref; +} - if (vref.ref->reference != NULL) { - s << " \"" << vref.ref->reference << "\""; - } else { - s<< " null"; +// Helper function to deal with time variable +REF2* make_time_ref(double * time) { + REF2* new_ref; + new_ref = (REF2*)calloc(1, sizeof(REF2)); + new_ref->reference = strdup("time") ; + new_ref->units = strdup("s") ; + new_ref->address = (char *)time ; + new_ref->attr = (ATTRIBUTES*)calloc(1, sizeof(ATTRIBUTES)) ; + new_ref->attr->type = TRICK_DOUBLE ; + new_ref->attr->units = strdup("s") ; + new_ref->attr->size = sizeof(double) ; + return new_ref; +} + +Trick::VariableReference::VariableReference(std::string var_name, double* time) : _staged(false), _write_ready(false) { + if (var_name != "time") { + ASSERT(0); } - return s; + + _var_info = make_time_ref(time); + + // Set up member variables + _address = _var_info->address; + _size = _var_info->attr->size ; + _deref = false; + + // Deal with weirdness around string vs wstring + _trick_type = _var_info->attr->type ; + + // Allocate stage and write buffers + _stage_buffer = calloc(_size, 1) ; + _write_buffer = calloc(_size, 1) ; + + _conversion_factor = cv_get_trivial(); + _base_units = _var_info->attr->units; + _requested_units = "s"; + _name = _var_info->reference; +} + +Trick::VariableReference::VariableReference(std::string var_name) : _staged(false), _write_ready(false) { + + if (var_name == "time") { + ASSERT(0); + } else { + // get variable attributes from memory manager + _var_info = ref_attributes(var_name.c_str()); + } + + // Handle error cases + if ( _var_info == NULL ) { + // TODO: ERROR LOGGER sendErrorMessage("Variable Server could not find variable %s.\n", var_name); + // PRINTF IS NOT AN ERROR LOGGER @me + message_publish(MSG_ERROR, "Variable Server could not find variable %s.\n", var_name.c_str()); + _var_info = make_error_ref(var_name); + } else if ( _var_info->attr ) { + if ( _var_info->attr->type == TRICK_STRUCTURED ) { + // sendErrorMessage("Variable Server: var_add cant add \"%s\" because its a composite variable.\n", var_name); + message_publish(MSG_ERROR, "Variable Server: var_add cant add \"%s\" because its a composite variable.\n", var_name.c_str()); + + free(_var_info); + _var_info = make_do_not_resolve_ref(var_name); + + } else if ( _var_info->attr->type == TRICK_STL ) { + // sendErrorMessage("Variable Server: var_add cant add \"%s\" because its an STL variable.\n", var_name); + message_publish(MSG_ERROR,"Variable Server: var_add cant add \"%s\" because its an STL variable.\n", var_name.c_str()); + + free(_var_info); + _var_info = make_do_not_resolve_ref(var_name); + } + } else { + // sendErrorMessage("Variable Server: BAD MOJO - Missing ATTRIBUTES."); + message_publish(MSG_ERROR, "Variable Server: BAD MOJO - Missing ATTRIBUTES."); + + free(_var_info); + _var_info = make_error_ref(var_name); + } + + // Set up member variables + _var_info->units = NULL; + _address = _var_info->address; + _size = _var_info->attr->size ; + _deref = false; + + // Deal with weirdness around string vs wstring + _trick_type = _var_info->attr->type ; + + if ( _var_info->num_index == _var_info->attr->num_index ) { + // single value - nothing else necessary + } else if ( _var_info->attr->index[_var_info->attr->num_index - 1].size != 0 ) { + // Constrained array + for ( int i = _var_info->attr->num_index-1; i > _var_info->num_index-1 ; i-- ) { + _size *= _var_info->attr->index[i].size ; + } + } else { + // Unconstrained array + if ((_var_info->attr->num_index - _var_info->num_index) > 1 ) { + message_publish(MSG_ERROR, "Variable Server Error: var_add(%s) requests more than one dimension of dynamic array.\n", _var_info->reference); + message_publish(MSG_ERROR, "Data is not contiguous so returned values are unpredictable.\n") ; + } + if ( _var_info->attr->type == TRICK_CHARACTER ) { + _trick_type = TRICK_STRING ; + _deref = true; + } else if ( _var_info->attr->type == TRICK_WCHAR ) { + _trick_type = TRICK_WSTRING ; + _deref = true; + } else { + _deref = true ; + _size *= get_size((char*)_address) ; + } + } + // handle strings: set a max buffer size, the copy size may vary so will be set in copy_sim_data + if (( _trick_type == TRICK_STRING ) || ( _trick_type == TRICK_WSTRING )) { + _size = MAX_ARRAY_LENGTH ; + } + + // Allocate stage and write buffers + _stage_buffer = calloc(_size, 1) ; + _write_buffer = calloc(_size, 1) ; + + _conversion_factor = cv_get_trivial(); + _base_units = _var_info->attr->units; + _requested_units = ""; + _name = _var_info->reference; + + // Done! } Trick::VariableReference::~VariableReference() { - free(ref) ; - free(buffer_in) ; - free(buffer_out) ; + if (_var_info != NULL) { + free( _var_info ); + _var_info = NULL; + } + if (_stage_buffer != NULL) { + free (_stage_buffer); + _stage_buffer = NULL; + } + if (_write_buffer != NULL) { + free (_write_buffer); + _write_buffer = NULL; + } + if (_conversion_factor != NULL) { + cv_free(_conversion_factor); + } } + +std::string Trick::VariableReference::getName() const { + return _name; +} + +int Trick::VariableReference::getSizeBinary() const { + return _size; +} + +TRICK_TYPE Trick::VariableReference::getType() const { + return _trick_type; +} + +std::string Trick::VariableReference::getBaseUnits() const { + return _base_units; +} + +int Trick::VariableReference::setRequestedUnits(std::string units_name) { + // Some error logging lambdas - these should probably go somewhere else + // But I do kinda like them + auto publish = [](MESSAGE_TYPE type, const std::string& message) { + std::ostringstream oss; + oss << "Variable Server: " << message << std::endl; + message_publish(type, oss.str().c_str()); + }; + + auto publishError = [&](const std::string& units) { + std::ostringstream oss; + oss << "units error for [" << getName() << "] [" << units << "]"; + publish(MSG_ERROR, oss.str()); + }; + + // If the units_name parameter is "xx", set it to the current units. + if (!units_name.compare("xx")) { + units_name = getBaseUnits(); + } + + // if unitless ('--') then do not convert to udunits + if(units_name.compare("--")) { + // Check to see if this is an old style Trick unit that needs to be converted to new udunits + std::string new_units = map_trick_units_to_udunits(units_name) ; + // Warn if a conversion has taken place + if ( units_name.compare(new_units) ) { + // TODO: MAKE BETTER SYSTEM FOR ERROR LOGGING + std::ostringstream oss; + oss << "[" << getName() << "] old-style units converted from [" + << units_name << "] to [" << new_units << "]"; + publish(MSG_WARNING, oss.str()); + } + + // Interpret base unit + ut_unit * from = ut_parse(Trick::UdUnits::get_u_system(), getBaseUnits().c_str(), UT_ASCII) ; + if ( !from ) { + std::cout << "Error in interpreting base units" << std::endl; + publishError(getBaseUnits()); + ut_free(from) ; + return -1 ; + } + + // Interpret requested unit + ut_unit * to = ut_parse(Trick::UdUnits::get_u_system(), new_units.c_str(), UT_ASCII) ; + if ( !to ) { + std::cout << "Error in interpreting requested units" << std::endl; + publishError(new_units); + ut_free(from) ; + ut_free(to) ; + return -1 ; + } + + // Create a converter from the base to the requested + auto new_conversion_factor = ut_get_converter(from, to) ; + ut_free(from) ; + ut_free(to) ; + if ( !new_conversion_factor ) { + std::ostringstream oss; + oss << "[" << getName() << "] cannot convert units from [" << getBaseUnits() + << "] to [" << new_units << "]"; + publish(MSG_ERROR, oss.str()); + return -1 ; + } else { + _conversion_factor = new_conversion_factor; + } + + // Set the requested units. This will cause the unit string to be printed in write_value_ascii + _requested_units = new_units; + } + return 0; +} + +int Trick::VariableReference::stageValue(bool validate_address) { + _write_ready = false; + + // Copy bytes from
      to staging_point. + + // Try to recreate connection if it has been broken + if (_var_info->address == &_bad_ref_int) { + REF2 *new_ref = ref_attributes(_var_info->reference); + if (new_ref != NULL) { + _var_info = new_ref; + _address = _var_info->address; + // _requested_units = ""; + } + } + + // if there's a pointer somewhere in the address path, follow it in case pointer changed + if ( _var_info->pointer_present == 1 ) { + _address = follow_address_path(_var_info) ; + if (_address == NULL) { + tagAsInvalid(); + } else if ( validate_address ) { + validate(); + } else { + _var_info->address = _address ; + } + } + + // if this variable is a string we need to get the raw character string out of it. + if (( _trick_type == TRICK_STRING ) && !_deref) { + std::string * str_ptr = (std::string *)_var_info->address ; + // Get a pointer to the internal character array + _address = (void *)(str_ptr->c_str()) ; + } + + // if this variable itself is a pointer, dereference it + if ( _deref ) { + _address = *(void**)_var_info->address ; + } + + // handle c++ string and char* + if ( _trick_type == TRICK_STRING ) { + if (_address == NULL) { + _size = 0 ; + } else { + _size = strlen((char*)_address) + 1 ; + } + } + // handle c++ wstring and wchar_t* + if ( _trick_type == TRICK_WSTRING ) { + if (_address == NULL) { + _size = 0 ; + } else { + _size = wcslen((wchar_t *)_address) * sizeof(wchar_t); + } + } + if(_address != NULL) { + memcpy( _stage_buffer , _address , _size ) ; + } + + _staged = true; + return 0; +} + +bool Trick::VariableReference::validate() { + // The address is not NULL. + // Should be called by VariableServer Session if validateAddress is on. + // check the memory manager if the address falls into + // any of the memory blocks it knows of. Don't do this if we have a std::string or + // wstring type, or we already are pointing to a bad ref. + if ( (_trick_type != TRICK_STRING) and + (_trick_type != TRICK_WSTRING) and + (_var_info->address != &_bad_ref_int) and + (get_alloc_info_of(_address) == NULL) ) { + + // This variable is broken, make it into an error ref + tagAsInvalid(); + return false; + } + + // Everything is fine + return true; +} + +static void write_escaped_string( std::ostream& os, const char* s) { + for (int ii=0 ; iiattr->size ; + + switch (_trick_type) { + + case TRICK_CHARACTER: + if (_var_info->attr->num_index == _var_info->num_index) { + // Single char + out << (int)cv_convert_double(_conversion_factor, *(char *)buf_ptr); + } else { + // All but last dim specified, leaves a char array + write_escaped_string(out, (const char *) buf_ptr); + bytes_written = _size ; + } + break; + case TRICK_UNSIGNED_CHARACTER: + if (_var_info->attr->num_index == _var_info->num_index) { + // Single char + out << (unsigned int)cv_convert_double(_conversion_factor,*(unsigned char *)buf_ptr); + } else { + // All but last dim specified, leaves a char array + write_escaped_string(out, (const char *) buf_ptr); + bytes_written = _size ; + } + break; + + case TRICK_WCHAR:{ + if (_var_info->attr->num_index == _var_info->num_index) { + out << *(wchar_t *) buf_ptr; + } else { + // convert wide char string char string + size_t len = wcs_to_ncs_len((wchar_t *)buf_ptr) + 1 ; + + char temp_buf[len]; + wcs_to_ncs((wchar_t *) buf_ptr, temp_buf, len); + out << temp_buf; + bytes_written = _size ; + } + } + break; + + case TRICK_STRING: + if ((char *) buf_ptr != NULL) { + write_escaped_string(out, (const char *) buf_ptr); + bytes_written = _size ; + } else { + out << '\0'; + } + break; + + case TRICK_WSTRING: + if ((wchar_t *) buf_ptr != NULL) { + // convert wide char string char string + size_t len = wcs_to_ncs_len( (wchar_t *)buf_ptr) + 1 ; + + char temp_buf[len]; + wcs_to_ncs( (wchar_t *) buf_ptr, temp_buf, len); + out << temp_buf; + bytes_written = _size ; + } else { + out << '\0'; + } + break; + case TRICK_SHORT: + out << (short)cv_convert_double(_conversion_factor,*(short *)buf_ptr); + break; + + case TRICK_UNSIGNED_SHORT: + out << (unsigned short)cv_convert_double(_conversion_factor,*(unsigned short *)buf_ptr); + break; + + case TRICK_INTEGER: + case TRICK_ENUMERATED: + out << (int)cv_convert_double(_conversion_factor,*(int *)buf_ptr); + break; + + case TRICK_BOOLEAN: + out << (int)cv_convert_double(_conversion_factor,*(bool *)buf_ptr); + break; + + case TRICK_BITFIELD: + out << (GET_BITFIELD(buf_ptr, _var_info->attr->size, _var_info->attr->index[0].start, _var_info->attr->index[0].size)); + break; + + case TRICK_UNSIGNED_BITFIELD: + out << (GET_UNSIGNED_BITFIELD(buf_ptr, _var_info->attr->size, _var_info->attr->index[0].start, _var_info->attr->index[0].size)); + break; + + case TRICK_UNSIGNED_INTEGER: + out << (unsigned int)cv_convert_double(_conversion_factor,*(unsigned int *)buf_ptr); + break; + + case TRICK_LONG: { + long l = *(long *)buf_ptr; + if (_conversion_factor != cv_get_trivial()) { + l = (long)cv_convert_double(_conversion_factor, l); + } + out << l; + break; + } + + case TRICK_UNSIGNED_LONG: { + unsigned long ul = *(unsigned long *)buf_ptr; + if (_conversion_factor != cv_get_trivial()) { + ul = (unsigned long)cv_convert_double(_conversion_factor, ul); + } + out << ul; + break; + } + + case TRICK_FLOAT: + out << std::setprecision(8) << cv_convert_float(_conversion_factor,*(float *)buf_ptr); + break; + + case TRICK_DOUBLE: + out << std::setprecision(16) << cv_convert_double(_conversion_factor,*(double *)buf_ptr); + break; + + case TRICK_LONG_LONG: { + long long ll = *(long long *)buf_ptr; + if (_conversion_factor != cv_get_trivial()) { + ll = (long long)cv_convert_double(_conversion_factor, ll); + } + out << ll; + break; + } + + case TRICK_UNSIGNED_LONG_LONG: { + unsigned long long ull = *(unsigned long long *)buf_ptr; + if (_conversion_factor != cv_get_trivial()) { + ull = (unsigned long long)cv_convert_double(_conversion_factor, ull); + } + out << ull; + break; + } + + case TRICK_NUMBER_OF_TYPES: + out << "BAD_REF"; + break; + + default:{ + + break; + } + } // end switch + + if (bytes_written < _size) { + // if returning an array, continue array as comma separated values + out << ","; + buf_ptr = (void*) ((long)buf_ptr + _var_info->attr->size) ; + } + } //end while + + if (_requested_units != "") { + if ( _var_info->attr->mods & TRICK_MODS_UNITSDASHDASH ) { + out << " {--}"; + } else { + out << " {" << _requested_units << "}"; + } + } + + return 0; +} + +void Trick::VariableReference::tagAsInvalid () { + std::string save_name(getName()) ; + free(_var_info) ; + _var_info = make_error_ref(save_name) ; + _address = _var_info->address ; +} + + +int Trick::VariableReference::prepareForWrite() { + if (!_staged) { + return 1; + } + + void * temp_p = _stage_buffer; + _stage_buffer = _write_buffer; + _write_buffer = temp_p; + + _staged = false; + _write_ready = true; + return 0; +} + +bool Trick::VariableReference::isStaged() const { + return _staged; +} + +bool Trick::VariableReference::isWriteReady() const { + return _write_ready; +} + +int Trick::VariableReference::writeTypeBinary( std::ostream& out, bool byteswap ) const { + int local_type = _trick_type; + if (byteswap) { + local_type = trick_byteswap_int(local_type); + } + out.write(const_cast(reinterpret_cast(&local_type)), sizeof(int)); + + return 0; +} + +int Trick::VariableReference::writeSizeBinary( std::ostream& out, bool byteswap ) const { + int local_size = _size; + if (byteswap) { + local_size = trick_byteswap_int(local_size); + } + out.write(const_cast(reinterpret_cast(&local_size)), sizeof(int)); + + return 0; +} + +int Trick::VariableReference::writeNameBinary( std::ostream& out, bool byteswap ) const { + std::string name = getName(); + out.write(name.c_str(), name.size()); + + return 0; +} + +int Trick::VariableReference::writeNameLengthBinary( std::ostream& out, bool byteswap ) const { + int name_size = getName().size(); + if (byteswap) { + name_size = trick_byteswap_int(name_size); + } + + out.write(const_cast(reinterpret_cast(&name_size)), sizeof(int)); + + return 0; +} + + +void Trick::VariableReference::byteswap_var (char * out, char * in) const { + byteswap_var(out, in, *this); +} + + +void Trick::VariableReference::byteswap_var (char * out, char * in, const VariableReference& ref) { + ATTRIBUTES * attr = ref._var_info->attr; + int array_size = 1; + + // Determine how many elements are in this array if it is an array + for (int j = 0; j < ref._var_info->attr->num_index; j++) { + array_size *= attr->index[j].size; + } + + switch (attr->size) { + case 1: + // If these are just characters, no need to byteswap + for (int j = 0; j < array_size; j++) { + out[j] = in[j]; + } + break; + + case 2: { + short * short_in = reinterpret_cast (in); + short * short_out = reinterpret_cast (out); + + for (int j = 0; j < array_size; j++) { + short_out[j] = trick_byteswap_short(short_in[j]); + } + break; + } + + case 4: { + int * int_in = reinterpret_cast (in); + int * int_out = reinterpret_cast (out); + + for (int j = 0; j < array_size; j++) { + int_out[j] = trick_byteswap_int(int_in[j]); + } + break; + } + case 8: { + // We don't actually care if this is double or long, just that it's the right size + double * double_in = reinterpret_cast (in); + double * double_out = reinterpret_cast (out); + + for (int j = 0; j < array_size; j++) { + double_out[j] = trick_byteswap_double(double_in[j]); + } + break; + } + } +} + + + +int Trick::VariableReference::writeValueBinary( std::ostream& out, bool byteswap ) const { + + if ( _trick_type == TRICK_BITFIELD ) { + int temp_i = GET_BITFIELD(_write_buffer , _var_info->attr->size , + _var_info->attr->index[0].start, _var_info->attr->index[0].size) ; + out.write((char *)(&temp_i), _size); + return _size; + } + + if ( _trick_type == TRICK_UNSIGNED_BITFIELD ) { + int temp_unsigned = GET_UNSIGNED_BITFIELD(_write_buffer , _var_info->attr->size , + _var_info->attr->index[0].start, _var_info->attr->index[0].size) ; + out.write((char *)(&temp_unsigned), _size); + return _size; + } + + if (_trick_type == TRICK_NUMBER_OF_TYPES) { + // TRICK_NUMBER_OF_TYPES is an error case + int temp_zero = 0 ; + out.write((char *)(&temp_zero), _size); + return _size; + } + + if (byteswap) { + char * byteswap_buf = (char *) calloc (_size, 1); + byteswap_var(byteswap_buf, (char *) _write_buffer); + out.write(byteswap_buf, _size); + free (byteswap_buf); + } + else { + out.write((char *) _write_buffer, _size); + } + + return _size; + +} + +std::ostream& Trick::operator<< (std::ostream& s, const Trick::VariableReference& ref) { + s << " \"" << ref.getName() << "\""; + return s; +} \ No newline at end of file diff --git a/trick_source/sim_services/VariableServer/VariableServer.cpp b/trick_source/sim_services/VariableServer/VariableServer.cpp index f791d67f..3db21fb7 100644 --- a/trick_source/sim_services/VariableServer/VariableServer.cpp +++ b/trick_source/sim_services/VariableServer/VariableServer.cpp @@ -16,10 +16,11 @@ Trick::VariableServer::VariableServer() : } Trick::VariableServer::~VariableServer() { + the_vs = NULL; } std::ostream& Trick::operator<< (std::ostream& s, Trick::VariableServer& vs) { - std::map < pthread_t , VariableServerThread * >::iterator it ; + std::map < pthread_t , VariableServerSessionThread * >::iterator it ; s << "{\"variable_server_connections\":[\n"; int count = 0; @@ -52,51 +53,81 @@ bool Trick::VariableServer::get_info_msg() { void Trick::VariableServer::set_var_server_info_msg_on() { info_msg = true; + for ( auto& session_it : var_server_sessions ) { + session_it.second->set_info_message(info_msg); + } } void Trick::VariableServer::set_var_server_info_msg_off() { info_msg = false; + for ( auto& session_it : var_server_sessions ) { + session_it.second->set_info_message(info_msg); + } } bool Trick::VariableServer::get_log() { return log ; } +bool Trick::VariableServer::get_session_log() { + return session_log ; +} + void Trick::VariableServer::set_var_server_log_on() { log = true; // turn log on for all current vs clients - std::map < pthread_t , VariableServerThread * >::iterator it ; - for ( it = var_server_threads.begin() ; it != var_server_threads.end() ; it++ ) { - (*it).second->set_log_on(); + for ( auto& session_it : var_server_sessions ) { + session_it.second->set_log(true); } } void Trick::VariableServer::set_var_server_log_off() { log = false; // turn log off for all current vs clients - std::map < pthread_t , VariableServerThread * >::iterator it ; - for ( it = var_server_threads.begin() ; it != var_server_threads.end() ; it++ ) { - (*it).second->set_log_off(); + for ( auto& session_it : var_server_sessions ) { + session_it.second->set_log(false); + } +} + +void Trick::VariableServer::set_var_server_session_log_on() { + session_log = true; + // turn log on for all current vs clients + for ( auto& session_it : var_server_sessions ) { + session_it.second->set_session_log(true); + } +} + +void Trick::VariableServer::set_var_server_session_log_off() { + session_log = false; + // turn log off for all current vs clients + for ( auto& session_it : var_server_sessions ) { + session_it.second->set_session_log(false); } } const char * Trick::VariableServer::get_hostname() { - return (listen_thread.get_hostname()) ; + return listen_thread.get_hostname(); } Trick::VariableServerListenThread & Trick::VariableServer::get_listen_thread() { return listen_thread ; } -void Trick::VariableServer::add_vst(pthread_t in_thread_id, VariableServerThread * in_vst) { +void Trick::VariableServer::add_vst(pthread_t in_thread_id, VariableServerSessionThread * in_vst) { pthread_mutex_lock(&map_mutex) ; var_server_threads[in_thread_id] = in_vst ; pthread_mutex_unlock(&map_mutex) ; } -Trick::VariableServerThread * Trick::VariableServer::get_vst(pthread_t thread_id) { - std::map < pthread_t , Trick::VariableServerThread * >::iterator it ; - Trick::VariableServerThread * ret = NULL ; +void Trick::VariableServer::add_session(pthread_t in_thread_id, VariableServerSession * in_session) { + pthread_mutex_lock(&map_mutex) ; + var_server_sessions[in_thread_id] = in_session ; + pthread_mutex_unlock(&map_mutex) ; +} + +Trick::VariableServerSessionThread * Trick::VariableServer::get_vst(pthread_t thread_id) { + std::map < pthread_t , Trick::VariableServerSessionThread * >::iterator it ; + Trick::VariableServerSessionThread * ret = NULL ; pthread_mutex_lock(&map_mutex) ; it = var_server_threads.find(thread_id) ; if ( it != var_server_threads.end() ) { @@ -106,16 +137,33 @@ Trick::VariableServerThread * Trick::VariableServer::get_vst(pthread_t thread_id return ret ; } +Trick::VariableServerSession * Trick::VariableServer::get_session(pthread_t thread_id) { + Trick::VariableServerSession * ret = NULL ; + pthread_mutex_lock(&map_mutex) ; + auto it = var_server_sessions.find(thread_id) ; + if ( it != var_server_sessions.end() ) { + ret = (*it).second ; + } + pthread_mutex_unlock(&map_mutex) ; + return ret ; +} + void Trick::VariableServer::delete_vst(pthread_t thread_id) { pthread_mutex_lock(&map_mutex) ; var_server_threads.erase(thread_id) ; pthread_mutex_unlock(&map_mutex) ; } +void Trick::VariableServer::delete_session(pthread_t thread_id) { + pthread_mutex_lock(&map_mutex) ; + var_server_sessions.erase(thread_id) ; + pthread_mutex_unlock(&map_mutex) ; +} + void Trick::VariableServer::set_copy_data_job( Trick::JobData * in_job ) { copy_data_job = in_job ; } -void Trick::VariableServer::set_copy_data_freeze_job( Trick::JobData * in_job ) { - copy_data_freeze_job = in_job ; +void Trick::VariableServer::set_copy_and_write_freeze_job( Trick::JobData * in_job ) { + copy_and_write_freeze_job = in_job ; } diff --git a/trick_source/sim_services/VariableServer/VariableServerListenThread.cpp b/trick_source/sim_services/VariableServer/VariableServerListenThread.cpp index 51a0f399..4cf8ccac 100644 --- a/trick_source/sim_services/VariableServer/VariableServerListenThread.cpp +++ b/trick_source/sim_services/VariableServer/VariableServerListenThread.cpp @@ -3,201 +3,196 @@ #include #include "trick/VariableServerListenThread.hh" -#include "trick/VariableServerThread.hh" -#include "trick/tc_proto.h" +#include "trick/VariableServerSessionThread.hh" #include "trick/exec_proto.h" #include "trick/command_line_protos.h" #include "trick/message_proto.h" #include "trick/message_type.h" -Trick::VariableServerListenThread::VariableServerListenThread() : +#define MAX_MACHINE_NAME 80 + + +Trick::VariableServerListenThread::VariableServerListenThread() : VariableServerListenThread (NULL) {} + +Trick::VariableServerListenThread::VariableServerListenThread(TCPClientListener * listener) : Trick::SysThread("VarServListen"), - port(0), - user_port_requested(false), - broadcast(true), - listen_dev() + _requested_source_address(""), + _requested_port(0), + _user_requested_address(false), + _broadcast(true), + _listener(listener), + _multicast(new MulticastGroup()) { - char hname[80]; - gethostname(hname , (size_t) 80 ) ; - source_address = std::string(hname) ; - strcpy(listen_dev.client_tag, ""); - tc_error(&listen_dev, 0); - pthread_mutex_init(&restart_pause, NULL); + if (_listener != NULL) { + // If we were passed a listener + // We assume it is already initialized + _requested_source_address = _listener->getHostname(); + _requested_port = _listener->getPort(); + _user_requested_address = true; + } else { + // Otherwise, make one + _listener = new TCPClientListener; + } + + cancellable = false; } Trick::VariableServerListenThread::~VariableServerListenThread() { - free(listen_dev.hostname) ; - free(listen_dev.error_handler) ; + delete _listener; + delete _multicast; +} + +void Trick::VariableServerListenThread::set_multicast_group (MulticastGroup * group) { + delete _multicast; + _multicast = group; } const char * Trick::VariableServerListenThread::get_hostname() { - return listen_dev.hostname; + std::string hostname = _requested_source_address; + char * ret = (char *) malloc(hostname.length() + 1); + strncpy(ret, hostname.c_str(), hostname.length()); + ret[hostname.length()] = '\0'; + return ret; } unsigned short Trick::VariableServerListenThread::get_port() { - return port; + return _requested_port; } void Trick::VariableServerListenThread::set_port(unsigned short in_port) { - port = in_port; - user_port_requested = true ; + _requested_port = in_port; + _user_requested_address = true ; } -#ifndef __APPLE__ void Trick::VariableServerListenThread::set_socket_priority(unsigned short in_socket_priority) { - socket_priority = in_socket_priority; - user_socket_priority_requested = true ; + _socket_priority = in_socket_priority; + _user_socket_priority_requested = true ; } -#endif std::string Trick::VariableServerListenThread::get_user_tag() { - return user_tag ; + return _user_tag ; } const std::string& Trick::VariableServerListenThread::get_user_tag_ref() { - return user_tag ; + return _user_tag ; } void Trick::VariableServerListenThread::set_user_tag(std::string in_tag) { - user_tag = in_tag ; + _user_tag = in_tag ; } void Trick::VariableServerListenThread::set_source_address(const char * address) { if ( address == NULL ) { - source_address = std::string("") ; + _requested_source_address = std::string("") ; } else { - source_address = std::string(address) ; + _requested_source_address = std::string(address) ; } - user_port_requested = true ; + _user_requested_address = true; } std::string Trick::VariableServerListenThread::get_source_address() { - return source_address ; + return _requested_source_address ; } bool Trick::VariableServerListenThread::get_broadcast() { - return broadcast; + return _broadcast; } void Trick::VariableServerListenThread::set_broadcast(bool in_broadcast) { - broadcast = in_broadcast; + _broadcast = in_broadcast; } +// Called from default data int Trick::VariableServerListenThread::init_listen_device() { - int ret; - - ret = tc_init(&listen_dev); - if (ret == TC_SUCCESS) { - port = listen_dev.port ; - } else { - fprintf(stderr, "ERROR: Could not establish listen port for Variable Server. Aborting.\n"); - ret = -1 ; - } - return ret ; + int ret = _listener->initialize(); + _requested_port = _listener->getPort(); + _requested_source_address = _listener->getHostname(); + apply_socket_priority(); + return ret; } +// Called from init jobs int Trick::VariableServerListenThread::check_and_move_listen_device() { int ret ; - if ( source_address.compare(listen_dev.hostname) or port != listen_dev.port ) { + if (_user_requested_address) { /* The user has requested a different source address or port in the input file */ - tc_disconnect(&listen_dev) ; - ret = tc_init_with_connection_info(&listen_dev, AF_INET, SOCK_STREAM, source_address.c_str(), port) ; - if (ret != TC_SUCCESS) { + _listener->disconnect(); + ret = _listener->initialize(_requested_source_address, _requested_port); + _requested_port = _listener->getPort(); + _requested_source_address = _listener->getHostname(); + apply_socket_priority(); + if (ret != 0) { message_publish(MSG_ERROR, "ERROR: Could not establish variable server source_address %s: port %d. Aborting.\n", - source_address.c_str(), port); + _requested_source_address.c_str(), _requested_port); return -1 ; } } return 0 ; } -void Trick::VariableServerListenThread::create_tcp_socket(const char * address, unsigned short in_port ) { - int result = tc_init_with_connection_info(&listen_dev, AF_INET, SOCK_STREAM, address, in_port) ; - if (result != 0) { - message_publish(MSG_ERROR, "ERROR: Could not establish additional listen port at address %s and port %d for Variable Server.\n", address, in_port); - } -} - void * Trick::VariableServerListenThread::thread_body() { + // This thread listens for incoming client connections, and when one is received, creates a new thread to handle the session + // Also broadcasts on multicast channel - fd_set rfds; - struct timeval timeout_time = { 2, 0 }; - char buf1[1024] = { 0 } ; - struct passwd * passp ; - Trick::VariableServerThread * vst ; - int value; - std::string version; - char * user_name ; + test_shutdown(); - int mcast_socket = 0; - struct sockaddr_in mcast_addr ; - struct sockaddr_in mcast_addr_legacy ; - version = std::string(exec_get_current_version()) ; + std::string version = std::string(exec_get_current_version()) ; version.erase(version.find_last_not_of(" \t\f\v\n\r")+1); - pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); - - passp = getpwuid(getuid()) ; - + // get username to _broadcast on multicast channel + struct passwd *passp = getpwuid(getuid()) ; + std::string user_name; if ( passp == NULL ) { user_name = strdup("unknown") ; } else { user_name = strdup(passp->pw_name) ; } + + _listener->setBlockMode(true); - tc_blockio(&listen_dev, TC_COMM_BLOCKIO); - - if ( broadcast ) { - if ((mcast_socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { - perror("vs_mcast_init socket"); - } - - value = 1; - if (setsockopt(mcast_socket, SOL_SOCKET, SO_REUSEADDR, (char *) &value, (socklen_t) sizeof(value)) < 0) { - perror("setsockopt: reuseaddr"); - } -#ifdef SO_REUSEPORT - if (setsockopt(mcast_socket, SOL_SOCKET, SO_REUSEPORT, (char *) &value, sizeof(value)) < 0) { - perror("setsockopt: reuseport"); - } -#endif - - /* Set up destination address */ - memset(&mcast_addr, 0, sizeof(mcast_addr)); - mcast_addr.sin_family = AF_INET; - mcast_addr.sin_addr.s_addr = inet_addr("239.3.14.15"); - mcast_addr.sin_port = htons((uint16_t) 9265); - - memset(&mcast_addr_legacy, 0, sizeof(mcast_addr_legacy)); - mcast_addr_legacy.sin_family = AF_INET; - mcast_addr_legacy.sin_addr.s_addr = inet_addr("224.3.14.15"); - mcast_addr_legacy.sin_port = htons((uint16_t) 9265); + if ( _broadcast ) { + initializeMulticast(); } while (1) { + // Quit here if it's time + test_shutdown(); - FD_ZERO(&rfds); - FD_SET(listen_dev.socket, &rfds); - timeout_time.tv_sec = 2 ; - select(listen_dev.socket + 1, &rfds, NULL, NULL, &timeout_time); + // Pause here if we need to + test_pause(); - if (FD_ISSET(listen_dev.socket, &rfds)) { - // pause here during restart - pthread_mutex_lock(&restart_pause) ; - vst = new Trick::VariableServerThread(&listen_dev) ; + // Look for a new client requesting a connection + if (_listener->checkForNewConnections()) { + + // Create a new thread to service this connection + VariableServerSessionThread * vst = new Trick::VariableServerSessionThread() ; + vst->set_connection(_listener->setUpNewConnection()); vst->copy_cpus(get_cpus()) ; vst->create_thread() ; - vst->wait_for_accept() ; - pthread_mutex_unlock(&restart_pause) ; - } else if ( broadcast ) { - snprintf(buf1 , sizeof(buf1), "%s\t%hu\t%s\t%d\t%s\t%s\t%s\t%s\t%s\t%hu\n" , listen_dev.hostname , (unsigned short)listen_dev.port , - user_name , (int)getpid() , command_line_args_get_default_dir() , command_line_args_get_cmdline_name() , - command_line_args_get_input_file() , version.c_str() , user_tag.c_str(), (unsigned short)listen_dev.port ) ; + ConnectionStatus status = vst->wait_for_accept() ; - sendto(mcast_socket , buf1 , strlen(buf1) , 0 , (struct sockaddr *)&mcast_addr , (socklen_t)sizeof(mcast_addr)) ; - sendto(mcast_socket , buf1 , strlen(buf1) , 0 , (struct sockaddr *)&mcast_addr_legacy , (socklen_t)sizeof(mcast_addr)) ; + if (status == CONNECTION_FAIL) { + // If the connection failed, the thread will exit. + // Make sure it joins fully before deleting the vst object + vst->join_thread(); + delete vst; + } + } else if ( _broadcast ) { + // Otherwise, broadcast on the multicast channel if enabled + char buf1[1024]; + snprintf(buf1 , sizeof(buf1), "%s\t%hu\t%s\t%d\t%s\t%s\t%s\t%s\t%s\t%hu\n" , _listener->getHostname().c_str(), (unsigned short)_listener->getPort() , + user_name.c_str() , (int)getpid() , command_line_args_get_default_dir() , command_line_args_get_cmdline_name() , + command_line_args_get_input_file() , version.c_str() , _user_tag.c_str(), (unsigned short)_listener->getPort() ) ; + + std::string message(buf1); + + if (!_multicast->isInitialized()) { + // In case broadcast was turned on after this loop was entered + initializeMulticast(); + } + _multicast->broadcast(message); } } @@ -210,58 +205,60 @@ int Trick::VariableServerListenThread::restart() { int ret ; - if ( user_port_requested ) { + _listener->restart(); - char hname[80]; - static struct sockaddr_in s_in; - gethostname(hname, (size_t) 80); - // Test to see if the restart address is on this machine. If it is not, it's not an error, clear source address - if ( strcmp( source_address.c_str(), hname )) { - if (! inet_pton(AF_INET, source_address.c_str(), (struct in_addr *)&s_in.sin_addr.s_addr) ) { - //printf("clearing source_address\n") ; - source_address.clear() ; - } + if ( _user_requested_address ) { + // If the use requested an address and/or port, make sure we reinitialize to the same one + if (!_listener->validateSourceAddress(_requested_source_address)) { + _requested_source_address.clear() ; } - printf("variable server restart user_port requested set %d\n", port) ; + message_publish(MSG_INFO, "variable server restart user_port requested set %s:%d\n",_requested_source_address.c_str(), _requested_port); - tc_disconnect(&listen_dev) ; - ret = tc_init_with_connection_info(&listen_dev, AF_INET, SOCK_STREAM, source_address.c_str(), port) ; - if (ret != TC_SUCCESS) { - message_publish(MSG_ERROR, "ERROR: Could not establish listen port %d for Variable Server. Aborting.\n", port); + _listener->disconnect(); + ret = _listener->initialize(_requested_source_address, _requested_port); + apply_socket_priority(); + + if (ret != 0) { + message_publish(MSG_ERROR, "ERROR: Could not establish listen port %d for Variable Server. Aborting.\n", _requested_port); return (-1); } } else { - struct sockaddr_in s_in; - int s_in_size = sizeof(s_in) ; - getsockname( listen_dev.socket , (struct sockaddr *)&s_in, (socklen_t *)&s_in_size) ; - printf("restart variable server message port = %d\n" , ntohs(s_in.sin_port)) ; - port = listen_dev.port = ntohs(s_in.sin_port); + // Otherwise, just ask the listener what port it's using + _listener->checkSocket(); + message_publish(MSG_INFO, "restart variable server message port = %d\n", _listener->getPort()); } + initializeMulticast(); + return 0 ; } +void Trick::VariableServerListenThread::initializeMulticast() { + _multicast->initialize(); + _multicast->addAddress("239.3.14.15", 9265); + _multicast->addAddress("224.3.14.15", 9265); +} + void Trick::VariableServerListenThread::pause_listening() { - pthread_mutex_lock(&restart_pause) ; + force_thread_to_pause(); } void Trick::VariableServerListenThread::restart_listening() { - pthread_mutex_unlock(&restart_pause) ; + _listener->restart(); + unpause_thread(); } void Trick::VariableServerListenThread::dump( std::ostream & oss ) { oss << "Trick::VariableServerListenThread (" << name << ")" << std::endl ; - oss << " source_address = " << source_address << std::endl ; - oss << " port = " << port << std::endl ; - oss << " user_port_requested = " << user_port_requested << std::endl ; - oss << " user_tag = " << user_tag << std::endl ; - oss << " broadcast = " << broadcast << std::endl ; + oss << " source_address = " << _listener->getHostname() << std::endl ; + oss << " port = " << _listener->getPort() << std::endl ; + oss << " user_requested_address = " << _user_requested_address << std::endl ; + oss << " user_tag = " << _user_tag << std::endl ; + oss << " broadcast = " << _broadcast << std::endl ; Trick::ThreadBase::dump(oss) ; } -#ifndef __APPLE__ -void Trick::VariableServerListenThread::apply_socket_priority(TCDevice *listen_device, TCDevice *device) { - if (user_socket_priority_requested) tc_set_socket_priority(listen_device, device, socket_priority); +void Trick::VariableServerListenThread::apply_socket_priority() { + if (_user_socket_priority_requested) _listener->checkSocket(); } -#endif diff --git a/trick_source/sim_services/VariableServer/VariableServerSession.cpp b/trick_source/sim_services/VariableServer/VariableServerSession.cpp new file mode 100644 index 00000000..e9329671 --- /dev/null +++ b/trick_source/sim_services/VariableServer/VariableServerSession.cpp @@ -0,0 +1,255 @@ +#include "trick/VariableServerSession.hh" +#include "trick/TrickConstant.hh" +#include "trick/exec_proto.h" +#include "trick/Message_proto.hh" +#include "trick/message_proto.h" +#include "trick/input_processor_proto.h" +#include "trick/realtimesync_proto.h" + +int Trick::VariableServerSession::instance_counter = 0; +std::string Trick::VariableServerSession::log_subdir = "session_logs"; + +Trick::VariableServerSession::VariableServerSession() { + _debug = 0; + _enabled = true ; + _log = false ; + _session_log = false; + _session_log_msg_stream = -1; + _info_msg = false; + _copy_mode = VS_COPY_ASYNC ; + _write_mode = VS_WRITE_ASYNC ; + _frame_multiple = 1 ; + _frame_offset = 0 ; + _freeze_frame_multiple = 1 ; + _freeze_frame_offset = 0 ; + _update_rate = 0.1 ; + _cycle_tics = (long long)(_update_rate * exec_get_time_tic_value()) ; + if (_cycle_tics == 0) { + _cycle_tics = 1; + } + + _next_tics = TRICK_MAX_LONG_LONG ; + _freeze_next_tics = TRICK_MAX_LONG_LONG ; + _byteswap = false ; + _validate_address = false ; + _send_stdio = false ; + + _binary_data = false; + _byteswap = false; + _binary_data_nonames = false; + + _exit_cmd = false; + _pause_cmd = false; + + _instance_num = instance_counter++; + + pthread_mutex_init(&_copy_mutex, NULL); +} + +Trick::VariableServerSession::~VariableServerSession() { + for (unsigned int ii = 0 ; ii < _session_variables.size() ; ii++ ) { + delete _session_variables[ii]; + } + } + + +void Trick::VariableServerSession::set_connection(ClientConnection * conn) { + _connection = conn; + log_connection_opened(); +} + +bool Trick::VariableServerSession::is_session_log_open() { + return _session_log_msg_stream != -1; +} + +void Trick::VariableServerSession::open_session_log() { + std::string name = "VSSession" + std::to_string(_instance_num); + _session_log_msg_stream = open_custom_message_file(log_subdir + "/" + name + ".log", name); +} + + +// Command to turn on log to varserver_log file +int Trick::VariableServerSession::set_log(bool on_off) { + _log = on_off; + return(0) ; +} + +// Command to turn on individual session log file +int Trick::VariableServerSession::set_session_log(bool on_off) { + _session_log = on_off; + return(0) ; +} + +bool Trick::VariableServerSession::get_pause() { + return _pause_cmd ; +} + +void Trick::VariableServerSession::set_pause( bool on_off) { + _pause_cmd = on_off ; +} + + +bool Trick::VariableServerSession::get_exit_cmd() { + return _exit_cmd ; +} + +void Trick::VariableServerSession::pause_copy() { + pthread_mutex_lock(&_copy_mutex); +} + +void Trick::VariableServerSession::unpause_copy() { + pthread_mutex_unlock(&_copy_mutex); +} + +void Trick::VariableServerSession::disconnect_references() { + for (VariableReference * variable : _session_variables) { + variable->tagAsInvalid(); + } +} + +long long Trick::VariableServerSession::get_next_tics() const { + if ( ! _enabled ) { + return TRICK_MAX_LONG_LONG ; + } + return _next_tics ; +} + +long long Trick::VariableServerSession::get_freeze_next_tics() const { + if ( ! _enabled ) { + return TRICK_MAX_LONG_LONG ; + } + return _freeze_next_tics ; +} + +void Trick::VariableServerSession::log_connection_opened() { + if (_session_log) { + if (!is_session_log_open()) { + open_session_log(); + } + + message_publish(_session_log_msg_stream, "Variable Server Session started with %s:%d\n", _connection->getClientHostname().c_str(), _connection->getClientPort()); + } +} + +void Trick::VariableServerSession::log_received_message(const std::string& msg) { + if (_log) { + message_publish(MSG_PLAYBACK, "tag=<%s> time=%f %s", _connection->getClientTag().c_str(), exec_get_sim_time(), msg.c_str()); + } + + if (_session_log) { + if (!is_session_log_open()) + open_session_log(); + + message_publish(_session_log_msg_stream, "tag=<%s> time=%f %s", _connection->getClientTag().c_str(), exec_get_sim_time(), msg.c_str()); + } + + if (_debug >= 3) { + message_publish(MSG_DEBUG, "%p tag=<%s> var_server received bytes = msg_len = %d\n", _connection, _connection->getClientTag().c_str(), msg.size()); + } + + if (_debug >= 1 || _info_msg) { + message_publish(MSG_DEBUG, "tag=<%s> time=%f %s", _connection->getClientTag().c_str(), exec_get_sim_time(), msg.c_str()); + } + +} + +int Trick::VariableServerSession::handle_message() { + + std::string received_message; + int nbytes = _connection->read(received_message); + if (nbytes > 0) { + log_received_message(received_message); + ip_parse(received_message.c_str()); /* returns 0 if no parsing error */ + } + + return nbytes; +} + +Trick::VariableReference * Trick::VariableServerSession::find_session_variable(std::string name) const { + for (VariableReference * ref : _session_variables) { + // Look for matching name + if (name.compare(ref->getName()) == 0) { + return ref; + } + } + + return NULL; +} + +int Trick::VariableServerSession::set_info_message(bool on) { + _info_msg = on; + return 0; +} + +double Trick::VariableServerSession::get_update_rate() const { + return _update_rate; +} + +VS_WRITE_MODE Trick::VariableServerSession::get_write_mode () const { + return _write_mode; +} + +VS_COPY_MODE Trick::VariableServerSession::get_copy_mode () const { + return _copy_mode; +} + +long long Trick::VariableServerSession::get_cycle_tics() const { + return _cycle_tics; +} + +int Trick::VariableServerSession::get_frame_multiple () const { + return _frame_multiple; +} + +int Trick::VariableServerSession::get_frame_offset () const { + return _frame_offset; +} + +int Trick::VariableServerSession::get_freeze_frame_multiple () const { + return _freeze_frame_multiple; +} + +int Trick::VariableServerSession::get_freeze_frame_offset () const { + return _freeze_frame_offset; +} + +bool Trick::VariableServerSession::get_enabled () const { + return _enabled; +} + +void Trick::VariableServerSession::set_freeze_next_tics(long long tics) { + _freeze_next_tics = tics; +} + +void Trick::VariableServerSession::set_next_tics(long long tics) { + _next_tics = tics; +} + +void Trick::VariableServerSession::set_exit_cmd() { + _exit_cmd = true; +} + + + +std::ostream& Trick::operator<< (std::ostream& s, const Trick::VariableServerSession& session) { + if (session._binary_data) { + s << " \"format\":\"BINARY\",\n"; + } else { + s << " \"format\":\"ASCII\",\n"; + } + s << " \"update_rate\":" << session.get_update_rate() << ",\n"; + + s << " \"variables\":[\n"; + + int n_vars = (int)session._session_variables.size(); + for (int i=0 ; i1) { + s << "," ; + } + s << "\n"; + } + s << " ]\n"; + + return s; +} \ No newline at end of file diff --git a/trick_source/sim_services/VariableServer/VariableServerSessionThread.cpp b/trick_source/sim_services/VariableServer/VariableServerSessionThread.cpp new file mode 100644 index 00000000..a1108d23 --- /dev/null +++ b/trick_source/sim_services/VariableServer/VariableServerSessionThread.cpp @@ -0,0 +1,134 @@ + +#include +#include +#include "trick/VariableServerSessionThread.hh" +#include "trick/exec_proto.h" +#include "trick/message_proto.h" +#include "trick/message_type.h" +#include "trick/TrickConstant.hh" +#include "trick/UDPConnection.hh" +#include "trick/TCPConnection.hh" + + +Trick::VariableServer * Trick::VariableServerSessionThread::_vs = NULL ; + +static int instance_num = 0; + +Trick::VariableServerSessionThread::VariableServerSessionThread() : VariableServerSessionThread (new VariableServerSession()) {} + +Trick::VariableServerSessionThread::VariableServerSessionThread(VariableServerSession * session) : + Trick::SysThread(std::string("VarServer" + std::to_string(instance_num++))) , _debug(0), _session(session), _connection(NULL) { + + _connection_status = CONNECTION_PENDING ; + + + pthread_mutex_init(&_connection_status_mutex, NULL); + pthread_cond_init(&_connection_status_cv, NULL); + + cancellable = false; +} + +Trick::VariableServerSessionThread::~VariableServerSessionThread() { + cleanup(); +} + +std::ostream& Trick::operator<< (std::ostream& s, Trick::VariableServerSessionThread& vst) { + // Write a JSON representation of a Trick::VariableServerSessionThread to an ostream. + s << " \"connection\":{\n"; + s << " \"client_tag\":\"" << vst._connection->getClientTag() << "\",\n"; + + s << " \"client_IP_address\":\"" << vst._connection->getClientHostname() << "\",\n"; + s << " \"client_port\":\"" << vst._connection->getClientPort() << "\",\n"; + + pthread_mutex_lock(&vst._connection_status_mutex); + if (vst._connection_status == CONNECTION_SUCCESS) { + s << *(vst._session); + } + pthread_mutex_unlock(&vst._connection_status_mutex); + + s << " }" << std::endl; + return s; +} + +void Trick::VariableServerSessionThread::set_vs_ptr(Trick::VariableServer * in_vs) { + _vs = in_vs ; +} + +Trick::VariableServer * Trick::VariableServerSessionThread::get_vs() { + return _vs ; +} + +void Trick::VariableServerSessionThread::set_client_tag(std::string tag) { + _connection->setClientTag(tag); +} + +void Trick::VariableServerSessionThread::set_connection(Trick::ClientConnection * in_connection) { + _connection = in_connection; +} + +Trick::ConnectionStatus Trick::VariableServerSessionThread::wait_for_accept() { + + pthread_mutex_lock(&_connection_status_mutex); + while ( _connection_status == CONNECTION_PENDING ) { + pthread_cond_wait(&_connection_status_cv, &_connection_status_mutex); + } + pthread_mutex_unlock(&_connection_status_mutex); + + return _connection_status; +} + +// Gets called from the main thread as a job +void Trick::VariableServerSessionThread::preload_checkpoint() { + + // Stop variable server processing at the top of the processing loop. + force_thread_to_pause(); + + + // Make sure that the _session has been initialized + pthread_mutex_lock(&_connection_status_mutex); + if (_connection_status == CONNECTION_SUCCESS) { + + // Let the thread complete any data copying it has to do + // and then suspend data copying until the checkpoint is reloaded. + _session->pause_copy(); + + // Save the pause state of this thread. + _saved_pause_cmd = _session->get_pause(); + + // Disallow data writing. + _session->set_pause(true); + + // Temporarily "disconnect" the variable references from Trick Managed Memory + // by tagging each as a "bad reference". + _session->disconnect_references(); + + // Allow data copying to continue. + _session->unpause_copy(); + } + pthread_mutex_unlock(&_connection_status_mutex); +} + +// Gets called from the main thread as a job +void Trick::VariableServerSessionThread::restart() { + // Set the pause state of this thread back to its "pre-checkpoint reload" state. + _connection->restart(); + + pthread_mutex_lock(&_connection_status_mutex); + if (_connection_status == CONNECTION_SUCCESS) { + _session->set_pause(_saved_pause_cmd) ; + } + pthread_mutex_unlock(&_connection_status_mutex); + + // Restart the variable server processing. + unpause_thread(); +} + +void Trick::VariableServerSessionThread::cleanup() { + _connection->disconnect(); + + if (_session != NULL) { + delete _session; + _session = NULL; + } +} + diff --git a/trick_source/sim_services/VariableServer/VariableServerSessionThread_loop.cpp b/trick_source/sim_services/VariableServer/VariableServerSessionThread_loop.cpp new file mode 100644 index 00000000..2dad9d0e --- /dev/null +++ b/trick_source/sim_services/VariableServer/VariableServerSessionThread_loop.cpp @@ -0,0 +1,139 @@ + +#include +#ifdef __linux +#include +#endif + +#include "trick/VariableServer.hh" +#include "trick/message_proto.h" +#include "trick/realtimesync_proto.h" +#include "trick/ExecutiveException.hh" +#include "trick/exec_proto.h" + +#include "trick/VariableServerSessionThread.hh" + +void exit_var_thread(void *in_vst) ; + +void * Trick::VariableServerSessionThread::thread_body() { + + // Check for short running sims + test_shutdown(NULL, NULL); + + // We need to make the thread to VariableServerSessionThread map before we accept the connection. + // Otherwise we have a race where this thread is unknown to the variable server and the + // client gets confirmation that the connection is ready for communication. + _vs->add_vst( pthread_self() , this ) ; + + // Accept client connection + int status = _connection->start(); + + if (status != 0) { + _vs->delete_vst(pthread_self()); + + // Tell main thread that we failed to initialize + pthread_mutex_lock(&_connection_status_mutex); + _connection_status = CONNECTION_FAIL; + pthread_cond_signal(&_connection_status_cv); + pthread_mutex_unlock(&_connection_status_mutex); + + thread_shutdown(); + } + + // if log is set on for variable server (e.g., in input file), turn log on for each client + if (_vs->get_log()) { + _session->set_log(true); + } + + if (_vs->get_session_log()) { + _session->set_session_log(true); + } + + if (_vs->get_info_msg()) { + _session->set_info_message(true); + } + + // Give the initialized connection to the session + // Don't touch the connection anymore until we shut them both down + _session->set_connection(_connection); + _vs->add_session( pthread_self(), _session ); + + // Tell main that we are ready + pthread_mutex_lock(&_connection_status_mutex); + _connection_status = CONNECTION_SUCCESS; + pthread_cond_signal(&_connection_status_cv); + pthread_mutex_unlock(&_connection_status_mutex); + + try { + while (1) { + // Shutdown here if it's time + test_shutdown(exit_var_thread, (void *) this); + + // Pause here if we are in a restart condition + test_pause(); + + // Look for a message from the client + // Parse and execute if one is availible + int read_status = _session->handle_message(); + if ( read_status < 0 ) { + break ; + } + + // Check to see if exit is necessary + if (_session->get_exit_cmd() == true) { + break; + } + + // Tell session it's time to copy and write if the mode is correct + int ret =_session->copy_and_write_async(); + if (ret < 0) { + break; + } + + // Sleep for the appropriate cycle time + usleep((unsigned int) (_session->get_update_rate() * 1000000)); + } + } catch (Trick::ExecutiveException & ex ) { + message_publish(MSG_ERROR, "\nVARIABLE SERVER COMMANDED exec_terminate\n ROUTINE: %s\n DIAGNOSTIC: %s\n" , + ex.file.c_str(), ex.message.c_str()) ; + + exec_signal_terminate(); + + } catch (const std::exception &ex) { + message_publish(MSG_ERROR, "\nVARIABLE SERVER caught std::exception\n DIAGNOSTIC: %s\n" , + ex.what()) ; + + exec_signal_terminate(); + +#ifdef __linux +#ifdef __GNUC__ +#if __GNUC__ >= 4 && __GNUC_MINOR__ >= 2 + // for post gcc 4.1.2 or whatever glibc version is used in RHEL6 and above. + } catch (abi::__forced_unwind&) { + //pthread_exit and pthread_cancel will cause an abi::__forced_unwind to be thrown. Rethrow it. + throw; +#endif +#endif +#endif + } catch (...) { +#ifdef __linux +#ifdef __GNUC__ +#if (__GNUC__ == 4 && __GNUC_MINOR__ == 1) || __GNUC__ == 12 + // for gcc 4.1.2 or whatever glib version in RHEL 5 that does not work with the abi::__forced_unwind + // Also seems to have a problem with gcc 12 + throw; +#else + message_publish(MSG_ERROR, "\nVARIABLE SERVER caught unknown exception\n" ) ; + exec_signal_terminate(); +#endif +#endif +#endif + } + + if (_debug >= 3) { + message_publish(MSG_DEBUG, "%p tag=<%s> var_server receive loop exiting\n", _connection, _connection->getClientTag().c_str()); + } + + thread_shutdown(exit_var_thread, this); + // No return from this. +} + diff --git a/trick_source/sim_services/VariableServer/VariableServerSession_commands.cpp b/trick_source/sim_services/VariableServer/VariableServerSession_commands.cpp new file mode 100644 index 00000000..4217d531 --- /dev/null +++ b/trick_source/sim_services/VariableServer/VariableServerSession_commands.cpp @@ -0,0 +1,392 @@ + +#include +#include +#include +#include +#include +#include "trick/VariableServerSession.hh" +#include "trick/variable_server_message_types.h" +#include "trick/memorymanager_c_intf.h" +#include "trick/exec_proto.h" +#include "trick/command_line_protos.h" +#include "trick/message_proto.h" +#include "trick/message_type.h" +#include "trick/TrickConstant.hh" +#include "trick/sie_c_intf.h" +#include "trick/UdUnits.hh" +#include "trick/map_trick_units_to_udunits.hh" + + +int Trick::VariableServerSession::var_add(std::string in_name) { + VariableReference * new_var; + if (in_name == "time") { + new_var = new VariableReference(in_name, &_time); + } else { + new_var = new VariableReference(in_name); + } + + _session_variables.push_back(new_var) ; + + return(0) ; +} + +int Trick::VariableServerSession::var_add(std::string var_name, std::string units_name) { + var_add(var_name) ; + var_units(var_name, units_name) ; + return(0) ; +} + +// Helper function for var_send_once +std::vector split (const std::string& str, const char delim) { + std::stringstream ss(str); + std::string s; + std::vector ret; + while (std::getline(ss, s, delim)) { + ret.push_back(s); + } + return ret; +} + +int Trick::VariableServerSession::var_send_once(std::string in_name, int num_vars) { + std::vector var_names = split(in_name, ','); + + if (var_names.size() != num_vars) { + message_publish(MSG_ERROR, "Number of variables sent to var_send_once (%d) does not match num_vars (%d).\n", var_names.size(), num_vars); + return -1; + } + + std::vector given_vars; + for (auto& varName : var_names) { + VariableReference * new_var; + if (varName == "time") { + new_var = new VariableReference(varName, &_time); + } else { + new_var = new VariableReference(varName); + } + given_vars.push_back(new_var); + } + copy_sim_data(given_vars, false); + write_data(given_vars, VS_SEND_ONCE); + + return(0) ; +} + + +int Trick::VariableServerSession::var_remove(std::string in_name) { + + for (unsigned int ii = 0 ; ii < _session_variables.size() ; ii++ ) { + std::string var_name = _session_variables[ii]->getName(); + if ( ! var_name.compare(in_name) ) { + delete _session_variables[ii]; + _session_variables.erase(_session_variables.begin() + ii) ; + break ; + } + } + + return(0) ; + +} + +int Trick::VariableServerSession::var_units(std::string var_name, std::string units_name) { + VariableReference * variable = find_session_variable(var_name); + + if (variable == NULL) { + // TODO: ERROR LOGGER HERE + return -1; + } + + return variable->setRequestedUnits(units_name); +} + +int Trick::VariableServerSession::var_exists(std::string in_name) { + char buf1[5] ; + bool error = false ; + + unsigned int msg_type ; + REF2* var_ref = ref_attributes(in_name.c_str()); + + if ( var_ref == (REF2*)NULL ) { + error = true; + } + + if (_binary_data) { + /* send binary 1 or 0 */ + msg_type = VS_VAR_EXISTS ; + memcpy(buf1, &msg_type , sizeof(msg_type)) ; + + buf1[4] = (error==false); + + if (_debug >= 2) { + message_publish(MSG_DEBUG, "%p tag=<%s> var_server sending 1 binary byte\n", _connection, _connection->getClientTag().c_str()); + } + + _connection->write(buf1, 5); + } else { + /* send ascii "1" or "0" */ + sprintf(buf1, "%d\t%d\n", VS_VAR_EXISTS, (error==false)); + if (_debug >= 2) { + message_publish(MSG_DEBUG, "%p tag=<%s> var_server sending:\n%s\n", _connection, _connection->getClientTag().c_str(), buf1) ; + } + std::string write_string(buf1); + if (write_string.length() != strlen(buf1)) { + std::cout << "PROBLEM WITH STRING LENGTH: VAR_EXISTS ASCII" << std::endl; + } + _connection->write(write_string); + } + + return(0) ; +} + +int Trick::VariableServerSession::var_clear() { + + while( !_session_variables.empty() ) { + delete _session_variables.back(); + _session_variables.pop_back(); + } + + return(0) ; +} + +int Trick::VariableServerSession::var_send() { + copy_sim_data(); + write_data(); + return(0) ; +} + +int Trick::VariableServerSession::var_cycle(double in_rate) { + _update_rate = in_rate ; + _cycle_tics = (long long)(_update_rate * exec_get_time_tic_value()) ; + return(0) ; +} + +int Trick::VariableServerSession::var_exit() { + _exit_cmd = true ; + return(0) ; +} + +int Trick::VariableServerSession::var_validate_address(bool on_off) { + _validate_address = on_off ; + return(0) ; +} + +int Trick::VariableServerSession::var_debug(int level) { + _debug = level ; + return(0) ; +} + +int Trick::VariableServerSession::var_ascii() { + _binary_data = 0 ; + return(0) ; +} + +int Trick::VariableServerSession::var_binary() { + _binary_data = 1 ; + return(0) ; +} + +int Trick::VariableServerSession::var_binary_nonames() { + _binary_data = 1 ; + _binary_data_nonames = 1 ; + return(0) ; +} + +int Trick::VariableServerSession::var_set_copy_mode(int mode) { + if ( mode >= VS_COPY_ASYNC and mode <= VS_COPY_TOP_OF_FRAME ) { + _copy_mode = (VS_COPY_MODE)mode ; + if ( _copy_mode == VS_COPY_SCHEDULED ) { + long long sim_time_tics ; + sim_time_tics = exec_get_time_tics() ; + // round the next call time to a multiple of the cycle + sim_time_tics -= sim_time_tics % _cycle_tics ; + _next_tics = sim_time_tics + _cycle_tics ; + + sim_time_tics = exec_get_freeze_time_tics() ; + // round the next call time to a multiple of the cycle + sim_time_tics -= sim_time_tics % _cycle_tics ; + _freeze_next_tics = sim_time_tics + _cycle_tics ; + + } else { + _next_tics = TRICK_MAX_LONG_LONG ; + } + return 0 ; + } + return -1 ; +} + +int Trick::VariableServerSession::var_set_write_mode(int mode) { + if ( mode >= VS_WRITE_ASYNC and mode <= VS_WRITE_WHEN_COPIED ) { + _write_mode = (VS_WRITE_MODE)mode ; + return 0 ; + } + return -1 ; +} + +int Trick::VariableServerSession::var_sync(int mode) { + + switch (mode) { + case 1: + var_set_copy_mode(VS_COPY_SCHEDULED) ; + var_set_write_mode(VS_WRITE_ASYNC) ; + break ; + case 2: + var_set_copy_mode(VS_COPY_SCHEDULED) ; + var_set_write_mode(VS_WRITE_WHEN_COPIED) ; + break ; + case 0: + default: + var_set_copy_mode(VS_COPY_ASYNC) ; + var_set_write_mode(VS_WRITE_ASYNC) ; + break ; + } + + return 0 ; +} + +int Trick::VariableServerSession::var_set_frame_multiple(unsigned int mult) { + _frame_multiple = mult ; + return 0 ; +} + +int Trick::VariableServerSession::var_set_frame_offset(unsigned int offset) { + _frame_offset = offset ; + return 0 ; +} + +int Trick::VariableServerSession::var_set_freeze_frame_multiple(unsigned int mult) { + _freeze_frame_multiple = mult ; + return 0 ; +} + +int Trick::VariableServerSession::var_set_freeze_frame_offset(unsigned int offset) { + _freeze_frame_offset = offset ; + return 0 ; +} + +int Trick::VariableServerSession::var_byteswap(bool on_off) { + _byteswap = on_off ; + return(0) ; +} + +bool Trick::VariableServerSession::get_send_stdio() { + return _send_stdio ; +} + +int Trick::VariableServerSession::set_send_stdio(bool on_off) { + _send_stdio = on_off ; + return(0) ; +} + +int Trick::VariableServerSession::send_list_size() { + + unsigned int msg_type = VS_LIST_SIZE; + int var_count = _session_variables.size(); + + // send number of variables + if (_binary_data) { + // send in the binary message header format: + // + char buf1[12] ; + + unsigned int msg_type = VS_LIST_SIZE; + memcpy(buf1, &msg_type , sizeof(msg_type)) ; + + memset(&(buf1[4]), 0, sizeof(int)); // message size = 0 + memcpy(&(buf1[8]), &var_count, sizeof(var_count)); + + if (_debug >= 2) { + message_publish(MSG_DEBUG, "%p tag=<%s> var_server sending %d event variables\n", _connection, _connection->getClientTag().c_str(), var_count); + } + + _connection->write(buf1, sizeof (buf1)); + } else { + std::stringstream write_string; + write_string << VS_LIST_SIZE << "\t" << var_count << "\n"; + // ascii + if (_debug >= 2) { + message_publish(MSG_DEBUG, "%p tag=<%s> var_server sending number of event variables:\n%s\n", _connection, _connection->getClientTag().c_str(), write_string.str().c_str()) ; + } + + _connection->write(write_string.str()); + } + + return 0 ; +} + +int Trick::VariableServerSession::transmit_file(std::string filename) { + const unsigned int packet_size = 4095 ; + FILE * fp ; + unsigned int file_size ; + unsigned int current_size = 0 ; + unsigned int bytes_read ; + char buffer[packet_size+1] ; + int ret ; + + if (_debug >= 2) { + message_publish(MSG_DEBUG,"%p tag=<%s> var_server opening %s.\n", _connection, _connection->getClientTag().c_str(), filename.c_str()) ; + } + + if ((fp = fopen(filename.c_str() , "r")) == NULL ) { + message_publish(MSG_ERROR,"Variable Server Error: Cannot open %s.\n", filename.c_str()) ; + sprintf(buffer, "%d\t-1\n", VS_SIE_RESOURCE) ; + std::string message(buffer); + _connection->write(message); + return(-1) ; + } + + fseek(fp , 0L, SEEK_END) ; + file_size = ftell(fp) ; + + sprintf(buffer, "%d\t%u\n\0" , VS_SIE_RESOURCE, file_size) ; + std::string message(buffer); + _connection->write(message); + rewind(fp) ; + + // Switch to blocking writes since this could be a large transfer. + if (_connection->setBlockMode(true)) { + message_publish(MSG_DEBUG,"Variable Server Error: Failed to set socket to blocking mode.\n"); + } + + while ( current_size < file_size ) { + bytes_read = fread(buffer , 1 , packet_size , fp) ; + message = std::string(buffer); + message.resize(bytes_read); + ret = _connection->write(message); + if (ret != (int)bytes_read) { + message_publish(MSG_ERROR,"Variable Server Error: Failed to send file. Bytes read: %d Bytes sent: %d\n", bytes_read, ret) ; + return(-1); + } + current_size += bytes_read ; + } + + // Switch back to non-blocking writes. + if (_connection->setBlockMode(false)) { + message_publish(MSG_DEBUG,"Variable Server Error: Failed to set socket to non-blocking mode.\n"); + return(-1); + } + + return(0) ; +} + +int Trick::VariableServerSession::send_file(std::string file_name) { + return transmit_file(file_name) ; +} + +int Trick::VariableServerSession::send_sie_resource() { + sie_append_runtime_objs() ; + return transmit_file(std::string(command_line_args_get_default_dir()) + "/S_sie.resource") ; +} + +int Trick::VariableServerSession::send_sie_class() { + sie_class_attr_map_print_xml() ; + return transmit_file(std::string(command_line_args_get_default_dir()) + "/" + "S_sie_class.xml") ; +} + +int Trick::VariableServerSession::send_sie_enum() { + sie_enum_attr_map_print_xml() ; + return transmit_file(std::string(command_line_args_get_default_dir()) + "/" + "S_sie_enum.xml") ; +} + +int Trick::VariableServerSession::send_sie_top_level_objects() { + sie_top_level_objects_print_xml() ; + return transmit_file(std::string(command_line_args_get_default_dir()) + "/" + "S_sie_top_level_objects.xml") ; +} diff --git a/trick_source/sim_services/VariableServer/VariableServerSession_copy_and_write_modes.cpp b/trick_source/sim_services/VariableServer/VariableServerSession_copy_and_write_modes.cpp new file mode 100644 index 00000000..15a74656 --- /dev/null +++ b/trick_source/sim_services/VariableServer/VariableServerSession_copy_and_write_modes.cpp @@ -0,0 +1,119 @@ + +#include +#include + +#include "trick/VariableServerSession.hh" +#include "trick/variable_server_sync_types.h" +#include "trick/realtimesync_proto.h" + +// These methods should be called from approprate jobs or from the VST + +int Trick::VariableServerSession::copy_and_write_freeze(long long curr_freeze_frame) { + int ret = 0 ; + + if (!get_enabled()) + return ret; + + if (get_copy_mode() == VS_COPY_TOP_OF_FRAME) { + long long temp_frame = curr_freeze_frame % get_freeze_frame_multiple() ; + if ( temp_frame == get_freeze_frame_offset() ) { + copy_sim_data() ; + if ( !get_pause() and get_write_mode() == VS_WRITE_WHEN_COPIED and is_real_time()) { + ret = write_data() ; + if ( ret < 0 ) { + set_exit_cmd(); + } + } + } + } + + return ret ; +} + +int Trick::VariableServerSession::copy_and_write_freeze_scheduled(long long curr_tics) { + int ret = 0 ; + + if (!get_enabled()) + return ret; + + if (get_copy_mode() == VS_COPY_SCHEDULED) { + if ( get_freeze_next_tics() <= curr_tics ) { + copy_sim_data() ; + if ( !get_pause() && get_write_mode() == VS_WRITE_WHEN_COPIED && is_real_time()) { + ret = write_data() ; + if ( ret < 0 ) { + set_exit_cmd(); + } + } + set_freeze_next_tics(curr_tics + get_cycle_tics()) ; + } + } + return ret ; +} + +int Trick::VariableServerSession::copy_and_write_scheduled(long long curr_tics) { + int ret = 0 ; + + if (!get_enabled()) + return ret; + + if (get_copy_mode() == VS_COPY_SCHEDULED) { + if ( get_next_tics() <= curr_tics ) { + copy_sim_data() ; + if ( !get_pause() && get_write_mode() == VS_WRITE_WHEN_COPIED && is_real_time()) { + ret = write_data() ; + if ( ret < 0 ) { + set_exit_cmd(); + } + } + set_next_tics(curr_tics + get_cycle_tics()) ; + } + } + return ret ; +} + +int Trick::VariableServerSession::copy_and_write_top(long long curr_frame) { + int ret = 0 ; + + if (!get_enabled()) + return ret; + + if (get_copy_mode() == VS_COPY_TOP_OF_FRAME) { + long long temp_frame = curr_frame % get_frame_multiple() ; + if ( temp_frame == get_frame_offset() ) { + copy_sim_data() ; + if ( !get_pause() && get_write_mode() == VS_WRITE_WHEN_COPIED && is_real_time()) { + ret = write_data() ; + if ( ret < 0 ) { + set_exit_cmd(); + } + } + } + } + return ret ; +} + +int Trick::VariableServerSession::copy_and_write_async() { + int ret = 0; + + if (!get_enabled()) + return ret; + + if (get_copy_mode() == VS_COPY_ASYNC ) { + copy_sim_data() ; + } + + // Write data out to connection if async mode or non-realtime, and not paused + bool should_write_async = (get_write_mode() == VS_WRITE_ASYNC) || + ( get_copy_mode() == VS_COPY_ASYNC && get_write_mode() == VS_WRITE_WHEN_COPIED)|| + (! is_real_time()); + + if ( !get_pause() && should_write_async) { + ret = write_data() ; + if ( ret < 0 ) { + set_exit_cmd(); + } + } + + return ret; +} diff --git a/trick_source/sim_services/VariableServer/VariableServerSession_copy_sim_data.cpp b/trick_source/sim_services/VariableServer/VariableServerSession_copy_sim_data.cpp new file mode 100644 index 00000000..50febc3f --- /dev/null +++ b/trick_source/sim_services/VariableServer/VariableServerSession_copy_sim_data.cpp @@ -0,0 +1,35 @@ + +#include +#include + +#include "trick/VariableServerSession.hh" +#include "trick/memorymanager_c_intf.h" +#include "trick/exec_proto.h" + + +// These actually do the copying + +int Trick::VariableServerSession::copy_sim_data() { + return copy_sim_data(_session_variables, true); +} + +int Trick::VariableServerSession::copy_sim_data(std::vector& given_vars, bool cyclical) { + + if (given_vars.size() == 0) { + return 0; + } + + if ( pthread_mutex_trylock(&_copy_mutex) == 0 ) { + // Get the simulation time we start this copy + _time = (double)exec_get_time_tics() / exec_get_time_tic_value() ; + + + for (auto curr_var : given_vars ) { + curr_var->stageValue(); + } + + pthread_mutex_unlock(&_copy_mutex) ; + } + + return 0; +} diff --git a/trick_source/sim_services/VariableServer/VariableServerSession_freeze_init.cpp b/trick_source/sim_services/VariableServer/VariableServerSession_freeze_init.cpp new file mode 100644 index 00000000..0f16323b --- /dev/null +++ b/trick_source/sim_services/VariableServer/VariableServerSession_freeze_init.cpp @@ -0,0 +1,16 @@ + +#include +#include + +#include "trick/VariableServerSession.hh" +#include "trick/TrickConstant.hh" + +int Trick::VariableServerSession::freeze_init() { + if ( _enabled && _copy_mode == VS_COPY_SCHEDULED) { + _freeze_next_tics = _cycle_tics ; + } else { + _freeze_next_tics = TRICK_MAX_LONG_LONG ; + } + return 0 ; +} + diff --git a/trick_source/sim_services/VariableServer/VariableServerSession_write_data.cpp b/trick_source/sim_services/VariableServer/VariableServerSession_write_data.cpp new file mode 100644 index 00000000..e4bece8e --- /dev/null +++ b/trick_source/sim_services/VariableServer/VariableServerSession_write_data.cpp @@ -0,0 +1,238 @@ +/* +PURPOSE: (Allows clients to get and set Trick parameters) +PROGRAMMERS: (((Alex Lin) (NASA) (8/06) (--))) +*/ + +#include +#include +#include +#include // for setprecision +#include "trick/VariableServerSession.hh" +#include "trick/parameter_types.h" +#include "trick/bitfield_proto.h" +#include "trick/trick_byteswap.h" +#include "trick/tc_proto.h" +#include "trick/message_proto.h" +#include "trick/message_type.h" + +#define MAX_MSG_LEN 8192 + + + +int Trick::VariableServerSession::write_binary_data(const std::vector& given_vars, VS_MESSAGE_TYPE message_type) { + typedef std::vector VarList; + typedef std::pair MessageData; + + // Some constants to make size calculations more readable + static const int header_size = 12; + static const int sizeof_size = 4; + static const int type_size = 4; + + std::vector message_sizes_and_vars; + + // Calculate how many messages and what vars in each + int total_size = header_size; + VarList curr_message_vars; + for (int i = 0; i < given_vars.size(); i++) { + VariableReference * var = given_vars[i]; + + int total_var_size = 0; + if (!_binary_data_nonames) { + total_var_size += sizeof_size; + total_var_size += var->getName().size(); + } + + total_var_size += type_size; + total_var_size += sizeof_size; + total_var_size += var->getSizeBinary(); + + // Check if this variable will fit in a message at all + if (header_size + total_var_size > MAX_MSG_LEN) { + message_publish(MSG_WARNING, "tag=<%s> Variable Server buffer[%d] too small (need %d) for symbol %s, SKIPPING IT.\n", + _connection->getClientTag().c_str(), MAX_MSG_LEN, header_size + total_var_size, var->getName().c_str()); + + continue; + } + + // If this variable won't fit in the current message, truncate the message and plan to put this var in a new one + if (total_size + total_var_size > MAX_MSG_LEN) { + message_sizes_and_vars.emplace_back(MessageData(total_size, curr_message_vars)); + + if (_debug >= 2) { + message_publish(MSG_DEBUG, "%p tag=<%s> var_server buffer[%d] too small (need %d), sending multiple binary packets.\n", + _connection, _connection->getClientTag().c_str(), MAX_MSG_LEN, total_size + total_var_size); + } + + total_size = header_size; + curr_message_vars.clear(); + } + total_size += total_var_size; + curr_message_vars.push_back(var); + } + + message_sizes_and_vars.emplace_back(MessageData(total_size, curr_message_vars)); + + // Now write out all of these messages + int var_index = 0; + for (const auto& message_info : message_sizes_and_vars) { + int curr_message_size = message_info.first; + VarList curr_message_vars = message_info.second; + + std::stringstream stream; + + int written_message_type = message_type; + int written_header_size = curr_message_size - 4; + int written_num_vars = curr_message_vars.size(); + + if (_byteswap) { + written_message_type = trick_byteswap_int(written_message_type); + written_header_size = trick_byteswap_int(written_header_size); + written_num_vars = trick_byteswap_int(written_num_vars); + } + + // Header format: + // + + // Write the header first + stream.write((char *)(&written_message_type), sizeof(int)); + stream.write((char *)(&written_header_size), sizeof(int)); + stream.write((char *)(&written_num_vars), sizeof(int)); + + // Write variables next + for (VariableReference * var : curr_message_vars) { + // Each variable is formatted as: + // writeNameLengthBinary(stream, _byteswap); + var->writeNameBinary(stream, _byteswap); + } + var->writeTypeBinary(stream, _byteswap); + var->writeSizeBinary(stream, _byteswap); + var->writeValueBinary(stream, _byteswap); + } + + if (_debug >= 2) { + message_publish(MSG_DEBUG, "%p tag=<%s> var_server sending %u binary bytes containing %d variables.\n", + _connection, _connection->getClientTag().c_str(), curr_message_size, curr_message_vars.size()); + } + + // Send it out! + char write_buf[MAX_MSG_LEN]; + stream.read(write_buf, curr_message_size); + _connection->write(write_buf, curr_message_size); + } + + return 0; +} + +int Trick::VariableServerSession::write_ascii_data(const std::vector& given_vars, VS_MESSAGE_TYPE message_type ) { + // Load message type first + std::stringstream message_stream; + message_stream << (int)message_type; + + int message_size = message_stream.str().size(); + + // Load each variable + for (int i = 0; i < given_vars.size(); i++) { + message_stream << "\t"; + + std::stringstream var_stream; + given_vars[i]->writeValueAscii(var_stream); + + // Unfortunately, there isn't a good way to get the size of the buffer without putting it into a string + std::string var_string = var_stream.str(); + int var_size = var_string.size(); + + // Check if this single variable is too big, truncate if so + if (var_size + 2 > MAX_MSG_LEN) { + message_publish(MSG_WARNING, "tag=<%s> Variable Server buffer[%d] too small for symbol %s, TRUNCATED IT.\n", + _connection->getClientTag().c_str(), MAX_MSG_LEN, given_vars[i]->getName().c_str()); + + var_string = var_string.substr(0, MAX_MSG_LEN-2); + var_size = var_string.size(); + } + + // Check that there's enough room for the next variable, tab character, and possible newline + if (message_size + var_size + 2 > MAX_MSG_LEN) { + + // Write out an incomplete message + std::string message = message_stream.str(); + + if (_debug >= 2) { + message_publish(MSG_DEBUG, "%p tag=<%s> var_server buffer[%d] too small (need %d), sending multiple ascii packets.\n", + _connection, _connection->getClientTag().c_str(), MAX_MSG_LEN, message_size + var_size + 2); + + message_publish(MSG_DEBUG, "%p tag=<%s> var_server sedning %d ascii bytes:\n%s\n", + _connection, _connection->getClientTag().c_str(), message_size, message.c_str()); + } + + int result = _connection->write(message); + if (result < 0) { + return result; + } + + // Clear out the message stream + message_stream.str(""); + message_size = 0; + } + + // Concatenate + message_stream << var_string; + message_size += var_size + 1; + } + + + // End with newline + message_stream << '\n'; + std::string message = message_stream.str(); + + if (_debug >= 2) { + message_publish(MSG_DEBUG, "%p tag=<%s> var_server sedning %d ascii bytes:\n%s\n", + _connection, _connection->getClientTag().c_str(), message.size(), message.c_str()); + } + + int result = _connection->write(message); + return result; +} + +int Trick::VariableServerSession::write_data() { + return write_data(_session_variables, VS_VAR_LIST); +} + +int Trick::VariableServerSession::write_data(std::vector& given_vars, VS_MESSAGE_TYPE message_type) { + // do not send anything when there are no variables! + if ( given_vars.size() == 0) { + return(0); + } + + int result = 0; + + if ( pthread_mutex_trylock(&_copy_mutex) == 0 ) { + // Check that all of the variables are staged + for (VariableReference * variable : given_vars ) { + if (!variable->isStaged()) { + pthread_mutex_unlock(&_copy_mutex) ; + return 0; + } + } + + // Swap buffer_in and buffer_out for each vars[ii]. + for (VariableReference * variable : given_vars ) { + variable->prepareForWrite(); + } + + pthread_mutex_unlock(&_copy_mutex) ; + + // Send out in correct format + if (_binary_data) { + result = write_binary_data(given_vars, message_type ); + } else { + // ascii mode + result = write_ascii_data(given_vars, message_type ); + } + } + + return result; +} diff --git a/trick_source/sim_services/VariableServer/VariableServerSession_write_stdio.cpp b/trick_source/sim_services/VariableServer/VariableServerSession_write_stdio.cpp new file mode 100644 index 00000000..f95e77bf --- /dev/null +++ b/trick_source/sim_services/VariableServer/VariableServerSession_write_stdio.cpp @@ -0,0 +1,17 @@ +#include + +#include "trick/VariableServerSession.hh" +#include "trick/variable_server_message_types.h" +#include "trick/tc_proto.h" + +int Trick::VariableServerSession::write_stdio(int stream, std::string text) { + + std::stringstream outstream; + + outstream << VS_STDIO << " " << stream << " " << (int)text.length() << "\n"; + outstream << text; + + _connection->write(outstream.str()); + + return 0 ; +} diff --git a/trick_source/sim_services/VariableServer/VariableServerThread.cpp b/trick_source/sim_services/VariableServer/VariableServerThread.cpp deleted file mode 100644 index 45b75747..00000000 --- a/trick_source/sim_services/VariableServer/VariableServerThread.cpp +++ /dev/null @@ -1,146 +0,0 @@ - -#include -#include -#include "trick/VariableServerThread.hh" -#include "trick/exec_proto.h" -#include "trick/TrickConstant.hh" - -Trick::VariableServer * Trick::VariableServerThread::vs = NULL ; - -Trick::VariableServerThread::VariableServerThread(TCDevice * in_listen_dev) : - Trick::SysThread("VarServer", true) , - listen_dev(in_listen_dev) { - - debug = 0 ; - enabled = true ; - log = false ; - connection_accepted = false ; - conn_type = TCP ; - copy_mode = VS_COPY_ASYNC ; - write_mode = VS_WRITE_ASYNC ; - frame_multiple = 1 ; - frame_offset = 0 ; - freeze_frame_multiple = 1 ; - freeze_frame_offset = 0 ; - binary_data = false; - multicast = false; - byteswap = false ; - - pause_cmd = false ; - exit_cmd = false ; - - validate_address = false ; - send_stdio = false ; - - update_rate = 0.1 ; - cycle_tics = (long long)(update_rate * exec_get_time_tic_value()) ; - - next_tics = TRICK_MAX_LONG_LONG ; - freeze_next_tics = TRICK_MAX_LONG_LONG ; - - connection.disabled = TC_COMM_FALSE ; - connection.disable_handshaking = TC_COMM_TRUE ; - connection.blockio_limit = 0.0 ; - connection.blockio_type = TC_COMM_BLOCKIO ; - connection.client_id = 0 ; - strcpy(connection.client_tag, "") ; - connection.error_handler = (TrickErrorHndlr *) calloc(1, (int)sizeof(TrickErrorHndlr)); - connection.error_handler->report_level = TRICK_ERROR_CAUTION; - - pthread_mutex_init(©_mutex, NULL); - pthread_mutex_init(&restart_pause, NULL); - - var_data_staged = false; - packets_copied = 0 ; - - incoming_msg = (char *) calloc(1, MAX_CMD_LEN); - stripped_msg = (char *) calloc(1, MAX_CMD_LEN); - -} - -Trick::VariableServerThread::~VariableServerThread() { - free( incoming_msg ) ; - free( stripped_msg ) ; -} - - -std::ostream& Trick::operator<< (std::ostream& s, Trick::VariableServerThread& vst) { - - // Write a JSON representation of a Trick::VariableServerThread to an ostream. - struct sockaddr_in otherside; - socklen_t len = (socklen_t)sizeof(otherside); - - s << " \"connection\":{\n"; - s << " \"client_tag\":\"" << vst.connection.client_tag << "\",\n"; - - int err = getpeername(vst.connection.socket, (struct sockaddr*)&otherside, &len); - - if (err == 0) { - s << " \"client_IP_address\":\"" << inet_ntoa(otherside.sin_addr) << "\",\n"; - s << " \"client_port\":\"" << ntohs(otherside.sin_port) << "\",\n"; - } else { - s << " \"client_IP_address\":\"unknown\",\n"; - s << " \"client_port\":\"unknown\","; - } - - if (vst.binary_data) { - s << " \"format\":\"BINARY\",\n"; - } else { - s << " \"format\":\"ASCII\",\n"; - } - s << " \"update_rate\":" << vst.update_rate << ",\n"; - - s << " \"variables\":[\n"; - - int n_vars = (int)vst.vars.size(); - for (int i=0 ; i1) { - s << "," ; - } - s << "\n"; - } - s << " ]\n"; - s << " }" << std::endl; - return s; -} - -void Trick::VariableServerThread::set_vs_ptr(Trick::VariableServer * in_vs) { - vs = in_vs ; -} - -// Command to turn on log to varserver_log file -int Trick::VariableServerThread::set_log_on() { - log = true; - return(0) ; -} - -// Command to turn off log to varserver_log file -int Trick::VariableServerThread::set_log_off() { - log = false; - return(0) ; -} - -long long Trick::VariableServerThread::get_next_tics() { - if ( ! enabled ) { - return TRICK_MAX_LONG_LONG ; - } - return next_tics ; -} - -long long Trick::VariableServerThread::get_freeze_next_tics() { - if ( ! enabled ) { - return TRICK_MAX_LONG_LONG ; - } - return freeze_next_tics ; -} - -Trick::VariableServer * Trick::VariableServerThread::get_vs() { - return vs ; -} - -TCDevice & Trick::VariableServerThread::get_connection() { - return connection ; -} - - diff --git a/trick_source/sim_services/VariableServer/VariableServerThread_commands.cpp b/trick_source/sim_services/VariableServer/VariableServerThread_commands.cpp deleted file mode 100644 index a96ce6e8..00000000 --- a/trick_source/sim_services/VariableServer/VariableServerThread_commands.cpp +++ /dev/null @@ -1,516 +0,0 @@ - -#include -#include -#include -#include -#include -#include "trick/VariableServer.hh" -#include "trick/variable_server_message_types.h" -#include "trick/memorymanager_c_intf.h" -#include "trick/tc_proto.h" -#include "trick/exec_proto.h" -#include "trick/command_line_protos.h" -#include "trick/message_proto.h" -#include "trick/message_type.h" -#include "trick/TrickConstant.hh" -#include "trick/sie_c_intf.h" -#include "trick/UdUnits.hh" -#include "trick/map_trick_units_to_udunits.hh" - -int Trick::VariableServerThread::bad_ref_int = 0 ; -int Trick::VariableServerThread::do_not_resolve_bad_ref_int = 0 ; - -REF2* Trick::VariableServerThread::make_time_ref() { - REF2* new_ref; - new_ref = (REF2*)calloc(1, sizeof(REF2)); - new_ref->reference = strdup("time") ; - new_ref->units = strdup("s") ; - new_ref->address = (char *)&time ; - new_ref->attr = (ATTRIBUTES*)calloc(1, sizeof(ATTRIBUTES)) ; - new_ref->attr->type = TRICK_DOUBLE ; - new_ref->attr->units = strdup("s") ; - new_ref->attr->size = sizeof(double) ; - return new_ref; -} - -REF2* Trick::VariableServerThread::make_error_ref(std::string in_name) { - REF2* new_ref; - new_ref = (REF2*)calloc(1, sizeof(REF2)); - new_ref->reference = strdup(in_name.c_str()) ; - new_ref->units = NULL ; - new_ref->address = (char *)&bad_ref_int ; - new_ref->attr = (ATTRIBUTES*)calloc(1, sizeof(ATTRIBUTES)) ; - new_ref->attr->type = TRICK_NUMBER_OF_TYPES ; - new_ref->attr->units = (char *)"--" ; - new_ref->attr->size = sizeof(int) ; - return new_ref; -} - -Trick::VariableReference* Trick::VariableServerThread::create_var_reference(std::string in_name) { - REF2 * new_ref ; - - // Time var is treated specially - if ( in_name.compare("time") == 0 ) { - new_ref = make_time_ref() ; - } else { - // otherwise ref_attributes takes care of the hard part - new_ref = ref_attributes(in_name.c_str()) ; - } - - // Check error cases - if ( new_ref == NULL ) { - message_publish(MSG_ERROR, "Variable Server could not find variable %s.\n", in_name.c_str()); - new_ref = make_error_ref(in_name); - } else if ( new_ref->attr ) { - if ( new_ref->attr->type == TRICK_STRUCTURED ) { - message_publish(MSG_ERROR, "Variable Server: var_add cant add \"%s\" because its a composite variable.\n", in_name.c_str()); - // Replace the REF2 object we got from ref_attributes with an error-ref. - free(new_ref); - new_ref = make_error_ref(in_name); - // set the address of the data to the do_not_resolve address. We won't retry resolving the name - new_ref->address = (char *)&do_not_resolve_bad_ref_int ; - } else if ( new_ref->attr->type == TRICK_STL ) { - message_publish(MSG_ERROR, "Variable Server: var_add cant add \"%s\" because its an STL variable.\n", in_name.c_str()); - // Replace the REF2 object we got from ref_attributes with an error-ref. - free(new_ref); - new_ref = make_error_ref(in_name); - // set the address of the data to the do_not_resolve address. We won't retry resolving the name - new_ref->address = (char *)&do_not_resolve_bad_ref_int ; - } - } else { - message_publish(MSG_ERROR, "Variable Server: BAD MOJO - Missing ATTRIBUTES."); - new_ref = make_error_ref(in_name); - } - - // Actually constructs the variable reference in the success case - return new VariableReference(new_ref) ; -} - -int Trick::VariableServerThread::var_add(std::string in_name) { - VariableReference * new_var = create_var_reference(in_name); - vars.push_back(new_var) ; - - return(0) ; -} - -int Trick::VariableServerThread::var_add(std::string var_name, std::string units_name) { - var_add(var_name) ; - var_units(var_name, units_name) ; - return(0) ; -} - -// Helper function for var_send_once -std::vector split (const std::string& str, const char delim) { - std::stringstream ss(str); - std::string s; - std::vector ret; - while (std::getline(ss, s, delim)) { - ret.push_back(s); - } - return ret; -} - -int Trick::VariableServerThread::var_send_once(std::string in_name, int num_vars) { - std::vector var_names = split(in_name, ','); - - if (var_names.size() != num_vars) { - message_publish(MSG_ERROR, "Number of variables sent to var_send_once (%d) does not match num_vars (%d).\n", var_names.size(), num_vars); - return -1; - } - - std::vector given_vars; - for (auto& varName : var_names) { - given_vars.push_back(create_var_reference(varName)); - } - copy_sim_data(given_vars, false); - write_data(given_vars); - - return(0) ; -} - - -int Trick::VariableServerThread::var_remove(std::string in_name) { - - unsigned int ii ; - for ( ii = 0 ; ii < vars.size() ; ii++ ) { - std::string var_name = vars[ii]->ref->reference; - if ( ! var_name.compare(in_name) ) { - delete vars[ii]; - vars.erase(vars.begin() + ii) ; - break ; - } - } - - return(0) ; - -} - -int Trick::VariableServerThread::var_units(std::string var_name, std::string units_name) { - for ( VariableReference* variable : vars ) { - if ( std::string(variable->ref->reference).compare(var_name) ) { - continue; - } - - if (!units_name.compare("xx")) { - units_name = variable->ref->attr->units; - } - - auto publish = [](MESSAGE_TYPE type, const std::string& message) { - std::ostringstream oss; - oss << "Variable Server: " << message << std::endl; - message_publish(type, oss.str().c_str()); - }; - /* if unitless ('--') then do not convert to udunits*/ - if(units_name.compare("--")){ - std::string new_units = map_trick_units_to_udunits(units_name) ; - if ( units_name.compare(new_units) ) { - std::ostringstream oss; - oss << "[" << var_name << "] old-style units converted from [" - << units_name << "] to [" << new_units << "]"; - publish(MSG_WARNING, oss.str()); - } - - auto publishError = [&](const std::string& units) { - std::ostringstream oss; - oss << "units error for [" << var_name << "] [" << units << "]"; - publish(MSG_ERROR, oss.str()); - }; - - ut_unit * from = ut_parse(Trick::UdUnits::get_u_system(), variable->ref->attr->units, UT_ASCII) ; - if ( !from ) { - publishError(variable->ref->attr->units); - ut_free(from) ; - return -1 ; - } - - ut_unit * to = ut_parse(Trick::UdUnits::get_u_system(), new_units.c_str(), UT_ASCII) ; - if ( !to ) { - publishError(new_units); - ut_free(from) ; - ut_free(to) ; - return -1 ; - } - - cv_converter * conversion_factor = ut_get_converter(from, to) ; - ut_free(from) ; - ut_free(to) ; - if ( !conversion_factor ) { - std::ostringstream oss; - oss << "[" << var_name << "] cannot convert units from [" << variable->ref->attr->units - << "] to [" << new_units << "]"; - publish(MSG_ERROR, oss.str()); - return -1 ; - } - - cv_free(variable->conversion_factor); - variable->conversion_factor = conversion_factor ; - free(variable->ref->units); - variable->ref->units = strdup(new_units.c_str()); - } - } - return(0) ; -} - -int Trick::VariableServerThread::var_exists(std::string in_name) { - - char buf1[5] ; - bool error = false ; - - unsigned int msg_type ; - REF2* var_ref = ref_attributes(in_name.c_str()); - - if ( var_ref == (REF2*)NULL ) { - error = true; - } - - if (binary_data) { - /* send binary 1 or 0 */ - msg_type = VS_VAR_EXISTS ; - memcpy(buf1, &msg_type , sizeof(msg_type)) ; - - buf1[4] = (error==false); - - if (debug >= 2) { - message_publish(MSG_DEBUG, "%p tag=<%s> var_server sending 1 binary byte\n", &connection, connection.client_tag); - } - tc_write(&connection, (char *) buf1, 5); - } else { - /* send ascii "1" or "0" */ - snprintf(buf1, sizeof(buf1), "%d\t%d\n", VS_VAR_EXISTS, (error==false)); - if (debug >= 2) { - message_publish(MSG_DEBUG, "%p tag=<%s> var_server sending:\n%s\n", &connection, connection.client_tag, buf1) ; - } - tc_write(&connection, (char *) buf1, strlen(buf1)); - } - - return(0) ; -} - -int Trick::VariableServerThread::var_clear() { - while( !vars.empty() ) { - delete vars.back(); - vars.pop_back(); - } - return(0) ; -} - - -int Trick::VariableServerThread::var_send() { - copy_sim_data(); - write_data(); - return(0) ; -} - -int Trick::VariableServerThread::var_cycle(double in_rate) { - update_rate = in_rate ; - cycle_tics = (long long)(update_rate * exec_get_time_tic_value()) ; - return(0) ; -} - -bool Trick::VariableServerThread::get_pause() { - return pause_cmd ; -} - -void Trick::VariableServerThread::set_pause( bool on_off) { - pause_cmd = on_off ; -} - -int Trick::VariableServerThread::var_exit() { - exit_cmd = true ; - return(0) ; -} - -int Trick::VariableServerThread::var_validate_address(bool on_off) { - validate_address = on_off ; - return(0) ; -} - -int Trick::VariableServerThread::var_debug(int level) { - debug = level ; - return(0) ; -} - -int Trick::VariableServerThread::var_ascii() { - binary_data = 0 ; - return(0) ; -} - -int Trick::VariableServerThread::var_binary() { - binary_data = 1 ; - return(0) ; -} - -int Trick::VariableServerThread::var_binary_nonames() { - binary_data = 1 ; - binary_data_nonames = 1 ; - return(0) ; -} - -int Trick::VariableServerThread::var_set_copy_mode(int mode) { - if ( mode >= VS_COPY_ASYNC and mode <= VS_COPY_TOP_OF_FRAME ) { - copy_mode = (VS_COPY_MODE)mode ; - if ( copy_mode == VS_COPY_SCHEDULED ) { - long long sim_time_tics ; - sim_time_tics = exec_get_time_tics() ; - // round the next call time to a multiple of the cycle - sim_time_tics -= sim_time_tics % cycle_tics ; - next_tics = sim_time_tics + cycle_tics ; - - sim_time_tics = exec_get_freeze_time_tics() ; - // round the next call time to a multiple of the cycle - sim_time_tics -= sim_time_tics % cycle_tics ; - freeze_next_tics = sim_time_tics + cycle_tics ; - - } else { - next_tics = TRICK_MAX_LONG_LONG ; - } - return 0 ; - } - return -1 ; -} - -int Trick::VariableServerThread::var_set_write_mode(int mode) { - if ( mode >= VS_WRITE_ASYNC and mode <= VS_WRITE_WHEN_COPIED ) { - write_mode = (VS_WRITE_MODE)mode ; - return 0 ; - } - return -1 ; -} - -int Trick::VariableServerThread::var_sync(int mode) { - - switch (mode) { - case 1: - var_set_copy_mode(VS_COPY_SCHEDULED) ; - var_set_write_mode(VS_WRITE_ASYNC) ; - break ; - case 2: - var_set_copy_mode(VS_COPY_SCHEDULED) ; - var_set_write_mode(VS_WRITE_WHEN_COPIED) ; - break ; - case 0: - default: - var_set_copy_mode(VS_COPY_ASYNC) ; - var_set_write_mode(VS_WRITE_ASYNC) ; - break ; - } - - return 0 ; -} - -int Trick::VariableServerThread::var_set_frame_multiple(unsigned int mult) { - frame_multiple = mult ; - return 0 ; -} - -int Trick::VariableServerThread::var_set_frame_offset(unsigned int offset) { - frame_offset = offset ; - return 0 ; -} - -int Trick::VariableServerThread::var_set_freeze_frame_multiple(unsigned int mult) { - freeze_frame_multiple = mult ; - return 0 ; -} - -int Trick::VariableServerThread::var_set_freeze_frame_offset(unsigned int offset) { - freeze_frame_offset = offset ; - return 0 ; -} - -int Trick::VariableServerThread::var_byteswap(bool on_off) { - byteswap = on_off ; - return(0) ; -} - -bool Trick::VariableServerThread::get_send_stdio() { - return send_stdio ; -} - -int Trick::VariableServerThread::set_send_stdio(bool on_off) { - send_stdio = on_off ; - return(0) ; -} - -int Trick::VariableServerThread::var_signal() { - - message_publish(MSG_ERROR,"Variable Server Error: var_signal is currently not implemented.\n") ; - - return(0) ; -} - -int Trick::VariableServerThread::var_multicast(bool on_off) { - - multicast = on_off ; - - message_publish(MSG_ERROR, "Variable Server Error: var_multicast is currently not implemented.\n") ; - - return(0) ; -} - -int Trick::VariableServerThread::send_list_size() { - char buf1[12] ; - unsigned int msg_type ; - int var_count; - - // send number of variables - var_count = vars.size(); - if (binary_data) { - // send in the binary message header format: - // - msg_type = VS_LIST_SIZE; - memcpy(buf1, &msg_type , sizeof(msg_type)) ; - - memset(&(buf1[4]), 0, sizeof(int)); // message size = 0 - memcpy(&(buf1[8]), &var_count, sizeof(var_count)); - - if (debug >= 2) { - message_publish(MSG_DEBUG, "%p tag=<%s> var_server sending %d event variables\n", &connection, connection.client_tag, var_count); - } - tc_write(&connection, (char *) buf1, 12); - } else { - // ascii - snprintf(buf1, sizeof(buf1), "%d\t%d\n", VS_LIST_SIZE, var_count); - if (debug >= 2) { - message_publish(MSG_DEBUG, "%p tag=<%s> var_server sending number of event variables:\n%s\n", &connection, connection.client_tag, buf1) ; - } - tc_write(&connection, (char *) buf1, strlen(buf1)); - } - - return 0 ; -} - -int Trick::VariableServerThread::transmit_file(std::string sie_file) { - const unsigned int packet_size = 4095 ; - FILE * fp ; - unsigned int file_size ; - unsigned int current_size = 0 ; - unsigned int bytes_read ; - char buffer[packet_size] ; - int ret ; - - if (debug >= 2) { - message_publish(MSG_DEBUG,"%p tag=<%s> var_server opening %s.\n", &connection, connection.client_tag, sie_file.c_str()) ; - } - - if ((fp = fopen(sie_file.c_str() , "r")) == NULL ) { - message_publish(MSG_ERROR,"Variable Server Error: Cannot open %s.\n", sie_file.c_str()) ; - snprintf(buffer, sizeof(buffer), "%d\t-1\n", VS_SIE_RESOURCE) ; - tc_write(&connection , buffer , strlen(buffer)) ; - return(-1) ; - } - - fseek(fp , 0L, SEEK_END) ; - file_size = ftell(fp) ; - - snprintf(buffer, sizeof(buffer), "%d\t%u\n" , VS_SIE_RESOURCE, file_size) ; - tc_write(&connection , buffer , strlen(buffer)) ; - rewind(fp) ; - - // Switch to blocking writes since this could be a large transfer. - if (tc_blockio(&connection, TC_COMM_BLOCKIO)) { - message_publish(MSG_DEBUG,"Variable Server Error: Failed to set TCDevice to TC_COMM_BLOCKIO.\n"); - } - - while ( current_size < file_size ) { - bytes_read = fread(buffer , 1 , packet_size , fp) ; - ret = tc_write(&connection , buffer , bytes_read ) ; - if (ret != (int)bytes_read) { - message_publish(MSG_ERROR,"Variable Server Error: Failed to send SIE file.\n", sie_file.c_str()) ; - return(-1); - } - current_size += bytes_read ; - } - - // Switch back to non-blocking writes. - if (tc_blockio(&connection, TC_COMM_NOBLOCKIO)) { - message_publish(MSG_ERROR,"Variable Server Error: Failed to set TCDevice to TC_COMM_NOBLOCKIO.\n"); - return(-1); - } - - return(0) ; -} - -int Trick::VariableServerThread::send_file(std::string file_name) { - return transmit_file(file_name) ; -} - -int Trick::VariableServerThread::send_sie_resource() { - sie_append_runtime_objs() ; - return transmit_file(std::string(sie_get_runtime_sie_dir()) + "/S_sie.resource") ; -} - -int Trick::VariableServerThread::send_sie_class() { - sie_class_attr_map_print_xml() ; - return transmit_file(std::string(sie_get_runtime_sie_dir()) + "/" + "S_sie_class.xml") ; -} - -int Trick::VariableServerThread::send_sie_enum() { - sie_enum_attr_map_print_xml() ; - return transmit_file(std::string(sie_get_runtime_sie_dir()) + "/" + "S_sie_enum.xml") ; -} - -int Trick::VariableServerThread::send_sie_top_level_objects() { - sie_top_level_objects_print_xml() ; - return transmit_file(std::string(sie_get_runtime_sie_dir()) + "/" + "S_sie_top_level_objects.xml") ; -} diff --git a/trick_source/sim_services/VariableServer/VariableServerThread_connect.cpp b/trick_source/sim_services/VariableServer/VariableServerThread_connect.cpp deleted file mode 100644 index 3d9b145b..00000000 --- a/trick_source/sim_services/VariableServer/VariableServerThread_connect.cpp +++ /dev/null @@ -1,9 +0,0 @@ - -#include "trick/VariableServer.hh" -#include "trick/release.h" - -void Trick::VariableServerThread::wait_for_accept() { - while ( connection_accepted == false ) { - RELEASE() ; - } -} diff --git a/trick_source/sim_services/VariableServer/VariableServerThread_copy_data.cpp b/trick_source/sim_services/VariableServer/VariableServerThread_copy_data.cpp deleted file mode 100644 index b02e6267..00000000 --- a/trick_source/sim_services/VariableServer/VariableServerThread_copy_data.cpp +++ /dev/null @@ -1,88 +0,0 @@ - -#include -#include - -#include "trick/VariableServer.hh" -#include "trick/variable_server_sync_types.h" -#include "trick/exec_proto.h" -#include "trick/realtimesync_proto.h" - -int Trick::VariableServerThread::copy_data_freeze() { - - int ret = 0 ; - long long curr_frame = exec_get_freeze_frame_count() ; - long long temp_frame ; - - if ( enabled and copy_mode == VS_COPY_TOP_OF_FRAME) { - temp_frame = curr_frame % freeze_frame_multiple ; - if ( temp_frame == freeze_frame_offset ) { - copy_sim_data() ; - if ( !pause_cmd and write_mode == VS_WRITE_WHEN_COPIED and is_real_time()) { - ret = write_data() ; - if ( ret < 0 ) { - exit_cmd = true ; - } - } - } - } - return ret ; -} - -int Trick::VariableServerThread::copy_data_freeze_scheduled(long long curr_tics) { - - int ret = 0 ; - - if ( enabled and copy_mode == VS_COPY_SCHEDULED) { - if ( freeze_next_tics <= curr_tics ) { - copy_sim_data() ; - if ( !pause_cmd and write_mode == VS_WRITE_WHEN_COPIED and is_real_time()) { - ret = write_data() ; - if ( ret < 0 ) { - exit_cmd = true ; - } - } - freeze_next_tics = curr_tics + cycle_tics ; - } - } - return ret ; -} - -int Trick::VariableServerThread::copy_data_scheduled(long long curr_tics) { - - int ret = 0 ; - - if ( enabled and copy_mode == VS_COPY_SCHEDULED) { - if ( next_tics <= curr_tics ) { - copy_sim_data() ; - if ( !pause_cmd and write_mode == VS_WRITE_WHEN_COPIED and is_real_time()) { - ret = write_data() ; - if ( ret < 0 ) { - exit_cmd = true ; - } - } - next_tics = curr_tics + cycle_tics ; - } - } - return ret ; -} - -int Trick::VariableServerThread::copy_data_top() { - - int ret = 0 ; - long long curr_frame = exec_get_frame_count() ; - long long temp_frame ; - - if ( enabled and copy_mode == VS_COPY_TOP_OF_FRAME) { - temp_frame = curr_frame % frame_multiple ; - if ( temp_frame == frame_offset ) { - copy_sim_data() ; - if ( !pause_cmd and write_mode == VS_WRITE_WHEN_COPIED and is_real_time()) { - ret = write_data() ; - if ( ret < 0 ) { - exit_cmd = true ; - } - } - } - } - return ret ; -} diff --git a/trick_source/sim_services/VariableServer/VariableServerThread_copy_sim_data.cpp b/trick_source/sim_services/VariableServer/VariableServerThread_copy_sim_data.cpp deleted file mode 100644 index 611825ea..00000000 --- a/trick_source/sim_services/VariableServer/VariableServerThread_copy_sim_data.cpp +++ /dev/null @@ -1,105 +0,0 @@ - -#include -#include - -#include "trick/VariableServer.hh" -#include "trick/memorymanager_c_intf.h" -#include "trick/exec_proto.h" - -int Trick::VariableServerThread::copy_sim_data() { - return copy_sim_data(vars, true); -} - -int Trick::VariableServerThread::copy_sim_data(std::vector given_vars, bool cyclical) { - - if (given_vars.size() == 0) { - return 0; - } - - if ( pthread_mutex_trylock(©_mutex) == 0 ) { - - // Get the simulation time we start this copy - if (cyclical) { - time = (double)exec_get_time_tics() / exec_get_time_tic_value() ; - } - - for (auto curr_var : given_vars ) { - - if (curr_var->ref->address == &bad_ref_int) { - REF2 *new_ref = ref_attributes(curr_var->ref->reference); - if (new_ref != NULL) { - curr_var->ref = new_ref; - } - } - - // if there's a pointer somewhere in the address path, follow it in case pointer changed - if ( curr_var->ref->pointer_present == 1 ) { - curr_var->address = follow_address_path(curr_var->ref) ; - if (curr_var->address == NULL) { - std::string save_name(curr_var->ref->reference) ; - free(curr_var->ref) ; - curr_var->ref = make_error_ref(save_name) ; - curr_var->address = curr_var->ref->address ; - } else if ( validate_address ) { - // The address is not NULL. - // If validate_address is on, check the memory manager if the address falls into - // any of the memory blocks it knows of. Don't do this if we have a std::string or - // wstring type, or we already are pointing to a bad ref. - if ( (curr_var->string_type != TRICK_STRING) and - (curr_var->string_type != TRICK_WSTRING) and - (curr_var->ref->address != &bad_ref_int) and - (get_alloc_info_of(curr_var->address) == NULL) ) { - std::string save_name(curr_var->ref->reference) ; - free(curr_var->ref) ; - curr_var->ref = make_error_ref(save_name) ; - curr_var->address = curr_var->ref->address ; - } - } else { - curr_var->ref->address = curr_var->address ; - } - - } - - // if this variable is a string we need to get the raw character string out of it. - if (( curr_var->string_type == TRICK_STRING ) && !curr_var->need_deref) { - std::string * str_ptr = (std::string *)curr_var->ref->address ; - curr_var->address = (void *)(str_ptr->c_str()) ; - } - - // if this variable itself is a pointer, dereference it - if ( curr_var->need_deref) { - curr_var->address = *(void**)curr_var->ref->address ; - } - - // handle c++ string and char* - if ( curr_var->string_type == TRICK_STRING ) { - if (curr_var->address == NULL) { - curr_var->size = 0 ; - } else { - curr_var->size = strlen((char*)curr_var->address) + 1 ; - } - } - // handle c++ wstring and wchar_t* - if ( curr_var->string_type == TRICK_WSTRING ) { - if (curr_var->address == NULL) { - curr_var->size = 0 ; - } else { - curr_var->size = wcslen((wchar_t *)curr_var->address) * sizeof(wchar_t); - } - } - if(curr_var->address != NULL) { - memcpy( curr_var->buffer_in , curr_var->address , curr_var->size ) ; - } - } - - // Indicate that sim data has been written and is now ready in the buffer_in's of the vars variable list. - if (cyclical) { - var_data_staged = true; - packets_copied++ ; - } - - pthread_mutex_unlock(©_mutex) ; - } - - return 0; -} diff --git a/trick_source/sim_services/VariableServer/VariableServerThread_create_socket.cpp b/trick_source/sim_services/VariableServer/VariableServerThread_create_socket.cpp deleted file mode 100644 index 1acea03b..00000000 --- a/trick_source/sim_services/VariableServer/VariableServerThread_create_socket.cpp +++ /dev/null @@ -1,44 +0,0 @@ - -#include -#include "trick/VariableServerThread.hh" -#include "trick/tc_proto.h" -#include "trick/message_proto.h" -#include "trick/message_type.h" - -int Trick::VariableServerThread::create_udp_socket(const char * address, unsigned short in_port) { - listen_dev = &connection ; - tc_init_with_connection_info(&connection, AF_INET, SOCK_DGRAM, address, in_port) ; - message_publish(MSG_INFO, "Created udp variable server %s:%d\n", listen_dev->hostname, listen_dev->port) ; - tc_blockio( &connection , TC_COMM_NOBLOCKIO ) ; - conn_type = UDP ; - return 0 ; -} - -int Trick::VariableServerThread::create_mcast_socket(const char * mcast_address, const char * address, unsigned short in_port) { - int ret ; - if ( mcast_address == NULL || mcast_address[0] == '\0' ) { - message_publish(MSG_ERROR, "Multicast address must be defined.\n") ; - return -1 ; - } - create_udp_socket(address, in_port) ; - - conn_type = MCAST ; - - struct ip_mreq mreq; - mreq.imr_multiaddr.s_addr = inet_addr(mcast_address); - mreq.imr_interface.s_addr = htonl(INADDR_ANY); - ret = setsockopt(connection.socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) ; - if ( ret < 0 ) { - perror("ip_add_membership") ; - return ret ; - } - - memset(&connection.remoteServAddr, 0, sizeof(struct sockaddr_in)); - connection.remoteServAddr.sin_family = AF_INET; - connection.remoteServAddr.sin_addr.s_addr = inet_addr(mcast_address); - connection.remoteServAddr.sin_port = htons(in_port); - - message_publish(MSG_INFO, "Multicast variable server output %s:%d\n", mcast_address, listen_dev->port) ; - return 0 ; - -} diff --git a/trick_source/sim_services/VariableServer/VariableServerThread_freeze_init.cpp b/trick_source/sim_services/VariableServer/VariableServerThread_freeze_init.cpp deleted file mode 100644 index 93a491a4..00000000 --- a/trick_source/sim_services/VariableServer/VariableServerThread_freeze_init.cpp +++ /dev/null @@ -1,16 +0,0 @@ - -#include -#include - -#include "trick/VariableServerThread.hh" -#include "trick/TrickConstant.hh" - -int Trick::VariableServerThread::freeze_init() { - if ( enabled and copy_mode == VS_COPY_SCHEDULED) { - freeze_next_tics = cycle_tics ; - } else { - freeze_next_tics = TRICK_MAX_LONG_LONG ; - } - return 0 ; -} - diff --git a/trick_source/sim_services/VariableServer/VariableServerThread_loop.cpp b/trick_source/sim_services/VariableServer/VariableServerThread_loop.cpp deleted file mode 100644 index 572af85a..00000000 --- a/trick_source/sim_services/VariableServer/VariableServerThread_loop.cpp +++ /dev/null @@ -1,204 +0,0 @@ - -#include -#include -#include -#ifdef __linux -#include -#endif -#include - - -#include "trick/VariableServer.hh" -#include "trick/variable_server_sync_types.h" -#include "trick/input_processor_proto.h" -#include "trick/tc_proto.h" -#include "trick/message_proto.h" -#include "trick/message_type.h" -#include "trick/realtimesync_proto.h" -#include "trick/ExecutiveException.hh" -#include "trick/exec_proto.h" - -void exit_var_thread(void *in_vst) ; - -void * Trick::VariableServerThread::thread_body() { - - int ii , jj; - int msg_len; - int ret; - int nbytes = -1; - char *last_newline ; - unsigned int size ; - socklen_t sock_size ; - - // We need to make the thread to VariableServerThread map before we accept the connection. - // Otherwise we have a race where this thread is unknown to the variable server and the - // client gets confirmation that the connection is ready for communication. - vs->add_vst( pthread_self() , this ) ; - - if ( listen_dev->socket_type == SOCK_STREAM ) { - tc_accept(listen_dev, &connection); - tc_blockio(&connection, TC_COMM_ALL_OR_NOTHING); - vs->get_listen_thread().apply_socket_priority(listen_dev, &connection); - } - connection_accepted = true ; - - pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL) ; - pthread_cleanup_push(exit_var_thread, (void *) this); - - /* Save off the host and port information of the source port. We want to ignore messages from - this port if we are a multicast socket */ - struct sockaddr_in self_s_in ; - int s_in_size = sizeof(self_s_in) ; - if ( conn_type == MCAST ) { - getsockname( connection.socket , (struct sockaddr *)&self_s_in, (socklen_t *)&s_in_size) ; - if ( self_s_in.sin_addr.s_addr == 0 ) { - char hname[80]; - struct hostent *ip_host; - gethostname(hname, (size_t) 80); - ip_host = gethostbyname(hname); - memcpy(&(self_s_in.sin_addr.s_addr), ip_host->h_addr, (size_t) ip_host->h_length); - } - } - - // if log is set on for variable server (e.g., in input file), turn log on for each client - if (vs->get_log()) { - log = true ; - } - - try { - while (1) { - - // Pause here if we are in a restart condition - pthread_mutex_lock(&restart_pause) ; - - /* Check the length of the message on the socket */ - nbytes = recvfrom( connection.socket, incoming_msg, MAX_CMD_LEN, MSG_PEEK, NULL, NULL ) ; - if (nbytes == 0 ) { - break ; - } - - if (nbytes != -1) { // -1 means socket is nonblocking and no data to read - /* find the last newline that is present on the socket */ - incoming_msg[nbytes] = '\0' ; - last_newline = rindex( incoming_msg , '\n') ; - - /* if there is a newline then there is a complete command on the socket */ - if ( last_newline != NULL ) { - /* only remove up to (and including) the last newline on the socket */ - size = last_newline - incoming_msg + 1; - if ( conn_type == UDP ) { - // Save the remote host information, that is where we are going to send replies. - sock_size = sizeof(connection.remoteServAddr) ; - nbytes = recvfrom( connection.socket, incoming_msg, size, 0 , - (struct sockaddr *)&connection.remoteServAddr, &sock_size ) ; - } else if ( conn_type == MCAST ) { - // Save the remove host information for test against ourself. - struct sockaddr_in s_in ; - sock_size = sizeof(s_in) ; - nbytes = recvfrom( connection.socket, incoming_msg, size, 0 , - (struct sockaddr *)&s_in, &sock_size ) ; - // If this message is from us, then ignore it. - if ( s_in.sin_addr.s_addr == self_s_in.sin_addr.s_addr and s_in.sin_port == self_s_in.sin_port) { - nbytes = 0 ; - } - } else { - // We know where we are sending information, no need to save it. - nbytes = recvfrom( connection.socket, incoming_msg, size, 0 , NULL, NULL ) ; - } - } else { - nbytes = 0 ; - } - } - - if ( nbytes > 0 ) { - - msg_len = nbytes ; - if (debug >= 3) { - message_publish(MSG_DEBUG, "%p tag=<%s> var_server received bytes = msg_len = %d\n", &connection, connection.client_tag, nbytes); - } - - incoming_msg[msg_len] = '\0' ; - - if (vs->get_info_msg() || (debug >= 1)) { - message_publish(MSG_DEBUG, "%p tag=<%s> var_server received: %s", &connection, connection.client_tag, incoming_msg) ; - } - if (log) { - message_publish(MSG_PLAYBACK, "tag=<%s> time=%f %s", connection.client_tag, exec_get_sim_time(), incoming_msg) ; - } - - for( ii = 0 , jj = 0 ; ii <= msg_len ; ii++ ) { - if ( incoming_msg[ii] != '\r' ) { - stripped_msg[jj++] = incoming_msg[ii] ; - } - } - - ip_parse(stripped_msg); /* returns 0 if no parsing error */ - - } - - /* break out of loop if exit command found */ - if (exit_cmd == true) { - break; - } - - if ( copy_mode == VS_COPY_ASYNC ) { - copy_sim_data() ; - } - - if ( (write_mode == VS_WRITE_ASYNC) or - ((copy_mode == VS_COPY_ASYNC) and (write_mode == VS_WRITE_WHEN_COPIED)) or - (! is_real_time()) ) { - if ( !pause_cmd ) { - ret = write_data() ; - if ( ret < 0 ) { - break ; - } - } - } - pthread_mutex_unlock(&restart_pause) ; - - usleep((unsigned int) (update_rate * 1000000)); - } - } catch (Trick::ExecutiveException & ex ) { - message_publish(MSG_ERROR, "\nVARIABLE SERVER COMMANDED exec_terminate\n ROUTINE: %s\n DIAGNOSTIC: %s\n" , - ex.file.c_str(), ex.message.c_str()) ; - exit(ex.ret_code) ; - } catch (const std::exception &ex) { - message_publish(MSG_ERROR, "\nVARIABLE SERVER caught std::exception\n DIAGNOSTIC: %s\n" , - ex.what()) ; - exit(-1) ; -#ifdef __linux -#ifdef __GNUC__ -#if __GNUC__ >= 4 && __GNUC_MINOR__ >= 2 - // for post gcc 4.1.2 or whatever glibc version is used in RHEL6 and above. - } catch (abi::__forced_unwind&) { - //pthread_exit and pthread_cancel will cause an abi::__forced_unwind to be thrown. Rethrow it. - throw; -#endif -#endif -#endif - } catch (...) { -#ifdef __linux -#ifdef __GNUC__ -#if (__GNUC__ == 4 && __GNUC_MINOR__ == 1) || __GNUC__ == 12 - // for gcc 4.1.2 or whatever glib version in RHEL 5 that does not work with the abi::__forced_unwind - // Also seems to have a problem with gcc 12 - throw; -#else - message_publish(MSG_ERROR, "\nVARIABLE SERVER caught unknown exception\n" ) ; - exit(-1) ; -#endif -#endif -#endif - } - - if (debug >= 3) { - message_publish(MSG_DEBUG, "%p tag=<%s> var_server receive loop exiting\n", &connection, connection.client_tag); - } - - pthread_cleanup_pop(1); - pthread_exit(NULL) ; - return NULL ; - -} - diff --git a/trick_source/sim_services/VariableServer/VariableServerThread_restart.cpp b/trick_source/sim_services/VariableServer/VariableServerThread_restart.cpp deleted file mode 100644 index 9c9f752a..00000000 --- a/trick_source/sim_services/VariableServer/VariableServerThread_restart.cpp +++ /dev/null @@ -1,45 +0,0 @@ - -#include -#include "trick/VariableServerThread.hh" - -void Trick::VariableServerThread::preload_checkpoint() { - - // Stop variable server processing at the top of the processing loop. - pthread_mutex_lock(&restart_pause); - - // Let the thread complete any data copying it has to do - // and then suspend data copying until the checkpoint is reloaded. - pthread_mutex_lock(©_mutex); - - // Save the pause state of this thread. - saved_pause_cmd = pause_cmd; - - // Disallow data writing. - pause_cmd = true ; - - // Temporarily "disconnect" the variable references from Trick Managed Memory - // by tagging each as a "bad reference". - std::vector ::iterator it ; - for (it = vars.begin(); it != vars.end() ; it++) { - (*it)->ref->address = (char*)&bad_ref_int; - (*it)->ref->attr = new ATTRIBUTES() ; - (*it)->ref->attr->type = TRICK_NUMBER_OF_TYPES ; - (*it)->ref->attr->units = (char *)"--" ; - (*it)->ref->attr->size = sizeof(int) ; - } - - // Allow data copying to continue. - pthread_mutex_unlock(©_mutex); - -} - -void Trick::VariableServerThread::restart() { - // Set the pause state of this thread back to its "pre-checkpoint reload" state. - pause_cmd = saved_pause_cmd ; - - // Restart the variable server processing. - pthread_mutex_unlock(&restart_pause); - -} - - diff --git a/trick_source/sim_services/VariableServer/VariableServerThread_write_data.cpp b/trick_source/sim_services/VariableServer/VariableServerThread_write_data.cpp deleted file mode 100644 index 17709b3d..00000000 --- a/trick_source/sim_services/VariableServer/VariableServerThread_write_data.cpp +++ /dev/null @@ -1,322 +0,0 @@ -/* -PURPOSE: (Allows clients to get and set Trick parameters) -PROGRAMMERS: (((Alex Lin) (NASA) (8/06) (--))) -*/ - -#include -#include -#include "trick/VariableServer.hh" -#include "trick/parameter_types.h" -#include "trick/bitfield_proto.h" -#include "trick/trick_byteswap.h" -#include "trick/tc_proto.h" -#include "trick/message_proto.h" -#include "trick/message_type.h" - - -extern "C" { - void *trick_bswap_buffer(void *out, void *in, ATTRIBUTES * attr, int tofrom) ; -} - -#define MAX_MSG_LEN 8192 - - -int Trick::VariableServerThread::write_binary_data( int Start, char *buf1, const std::vector& given_vars, VS_MESSAGE_TYPE message_type) { - int i; - int ret ; - int HeaderSize, MessageSize; - int NumVariablesProcessed; - unsigned int msg_type , offset, len ; - unsigned int size ; - unsigned int swap_int ; - char * address = 0 ; - char* param_name; - - /* start the offset 4 bytes into the message, we'll subtract the sizeof offset at the end */ - offset = sizeof(msg_type) + sizeof(offset) ; - - if (byteswap) { - /* Swap message type bytes */ - msg_type = trick_byteswap_int((int)message_type) ; - } else { - msg_type = message_type; - } - memcpy(buf1, &msg_type , sizeof(msg_type)) ; - HeaderSize = sizeof(msg_type); - - offset += sizeof(unsigned int) ; - HeaderSize += sizeof(unsigned int); - - for (i = Start; i < (int)given_vars.size() ; i++) { - - // data to send was copied to buffer in copy_sim_data - address = (char *)given_vars[i]->buffer_out; - size = given_vars[i]->size ; - - param_name = given_vars[i]->ref->reference; - len = strlen(param_name) ; - // when var_binary_nonames, do not put the variable names into the message to be sent - if (binary_data_nonames) { - MessageSize = sizeof(int) + sizeof(size) + size ; - } else { - MessageSize = sizeof(len) + len + sizeof(int) + sizeof(size) + size ; - } - - /* make sure this message will fit in a packet by itself */ - if ( (HeaderSize + MessageSize) > MAX_MSG_LEN ) { - message_publish(MSG_WARNING, "%p Variable Server buffer[%d] too small (need %d) for symbol %s, SKIPPING IT.\n", - &connection, MAX_MSG_LEN, - (int)(HeaderSize + MessageSize), - given_vars[i]->ref->reference ); - continue; - } - - if ( (offset + MessageSize) < MAX_MSG_LEN ) { - if (byteswap) { - if (!binary_data_nonames) { - swap_int = trick_byteswap_int((int)len) ; - memcpy(&buf1[offset] , &swap_int , sizeof(len)) ; - offset += sizeof(len) ; - - memcpy(&buf1[offset] , param_name , (size_t)len) ; - offset += len ; - } - - swap_int = trick_byteswap_int(given_vars[i]->ref->attr->type) ; - memcpy(&buf1[offset] , &swap_int , sizeof(int)) ; - offset += sizeof(int) ; - - swap_int = trick_byteswap_int((int)size) ; - memcpy(&buf1[offset] , &swap_int , sizeof(size)) ; - offset += sizeof(size) ; - - trick_bswap_single_parameter(&buf1[offset], address, given_vars[i]->ref->attr, 1); - offset += size ; - } - else { - int temp_i ; - unsigned int temp_ui ; - - if (!binary_data_nonames) { - memcpy(&buf1[offset] , &len , sizeof(len)) ; - offset += sizeof(len) ; - - memcpy(&buf1[offset] , param_name , (size_t)len) ; - offset += len ; - } - - memcpy(&buf1[offset] , &given_vars[i]->ref->attr->type , sizeof(int)) ; - offset += sizeof(int) ; - - memcpy(&buf1[offset] , &size , sizeof(size)) ; - offset += sizeof(size) ; - - switch ( given_vars[i]->ref->attr->type ) { - case TRICK_BITFIELD: - temp_i = GET_BITFIELD(address , given_vars[i]->ref->attr->size , - given_vars[i]->ref->attr->index[0].start, given_vars[i]->ref->attr->index[0].size) ; - memcpy(&buf1[offset] , &temp_i , (size_t)size) ; - break ; - case TRICK_UNSIGNED_BITFIELD: - temp_ui = GET_UNSIGNED_BITFIELD(address , given_vars[i]->ref->attr->size , - given_vars[i]->ref->attr->index[0].start, given_vars[i]->ref->attr->index[0].size) ; - memcpy(&buf1[offset] , &temp_ui , (size_t)size) ; - break ; - case TRICK_NUMBER_OF_TYPES: - // TRICK_NUMBER_OF_TYPES is an error case - temp_i = 0 ; - memcpy(&buf1[offset] , &temp_i , (size_t)size) ; - break ; - default: - memcpy(&buf1[offset] , address , (size_t)size) ; - break ; - } - offset += size ; - } - } - else { - /* indicate that we're over the maximum size */ - if (debug >= 2) { - message_publish(MSG_DEBUG, "%p tag=<%s> var_server buffer[%d] too small (need %d), sending multiple binary packets.\n", - &connection, connection.client_tag, MAX_MSG_LEN, - (int)(offset + MessageSize) ); - } - break ; - } - } - - /* adjust the header with the correct information reflecting what has been accomplished */ - NumVariablesProcessed = i - Start; - - offset -= sizeof(offset) ; - if (byteswap) { - swap_int = trick_byteswap_int((int)offset) ; - memcpy(buf1 + sizeof(msg_type) , &swap_int , sizeof(offset)) ; - - swap_int = trick_byteswap_int( NumVariablesProcessed ) ; - memcpy( buf1 + sizeof(msg_type) + sizeof(offset), &swap_int , sizeof(swap_int)) ; - } - else { - memcpy(buf1 + sizeof(msg_type) , &offset , sizeof(offset)) ; - memcpy( buf1 + sizeof(msg_type) + sizeof(offset), &NumVariablesProcessed , sizeof( NumVariablesProcessed )) ; - } - - if (debug >= 2) { - message_publish(MSG_DEBUG, "%p tag=<%s> var_server sending %u binary bytes containing %d variables.\n", &connection, - connection.client_tag, (unsigned int)(offset + sizeof(offset)), NumVariablesProcessed); - } - - len = offset + sizeof(msg_type) ; - ret = tc_write(&connection, (char *) buf1, len); - if ( ret != (int)len ) { - return(-1) ; - } - - /* return the index to the next symbol to send or V->num_vars if all done */ - return i; -} - -int Trick::VariableServerThread::write_ascii_data(char * dest_buf, size_t dest_buf_size, const std::vector& given_vars, VS_MESSAGE_TYPE message_type ) { - - snprintf(dest_buf, dest_buf_size, "%d\t", message_type) ; - - for (unsigned long i = 0; i < given_vars.size(); i++) { - char curr_buf[MAX_MSG_LEN]; - int ret = vs_format_ascii( given_vars[i] , curr_buf, sizeof(curr_buf)); - - if (ret < 0) { - message_publish(MSG_WARNING, "%p Variable Server string buffer[%d] too small for symbol %s, TRUNCATED IT.\n", - &connection, MAX_MSG_LEN, given_vars[i]->ref->reference ); - } - - /* make sure this message will fit in a packet by itself */ - if( strlen( curr_buf ) + 2 > MAX_MSG_LEN ) { - message_publish(MSG_WARNING, "%p Variable Server buffer[%d] too small for symbol %s, TRUNCATED IT.\n", - &connection, MAX_MSG_LEN, given_vars[i]->ref->reference ); - curr_buf[MAX_MSG_LEN - 1] = '\0'; - } - - int len = strlen(dest_buf) ; - - /* make sure there is space for the next tab or next newline and null */ - if( len + strlen( curr_buf ) + 2 > MAX_MSG_LEN ) { - // If there isn't, send incomplete message - if (debug >= 2) { - message_publish(MSG_DEBUG, "%p tag=<%s> var_server sending %d ascii bytes:\n%s\n", - &connection, connection.client_tag, (int)strlen(dest_buf), dest_buf) ; - } - - ret = tc_write(&connection, (char *) dest_buf, len); - if ( ret != len ) { - return(-1) ; - } - dest_buf[0] = '\0'; - } - - strcat(dest_buf, curr_buf); - strcat(dest_buf, "\t"); - } - - int len = strlen(dest_buf) ; - - if ( len > 0 ) { - dest_buf[ strlen(dest_buf) - 1 ] = '\n'; - - if (debug >= 2) { - message_publish(MSG_DEBUG, "%p tag=<%s> var_server sending %d ascii bytes:\n%s\n", - &connection, connection.client_tag, (int)strlen(dest_buf), dest_buf) ; - } - int ret = tc_write(&connection, (char *) dest_buf, (int)strlen(dest_buf)); - if ( ret != (int)strlen(dest_buf) ) { - return(-1) ; - } - } - - return 0; -} - -int Trick::VariableServerThread::write_data() { - - int ret; - unsigned int i ; - char buf1[ MAX_MSG_LEN ]; - int len ; - - // do not send anything when there are no variables! - if ( vars.size() == 0 or packets_copied == 0 ) { - return 0; - } - - /* Acquire sole access to vars[ii]->buffer_in. */ - if ( var_data_staged and pthread_mutex_trylock(©_mutex) == 0 ) { - unsigned int ii; - void * temp_p; - // Swap buffer_in and buffer_out for each vars[ii]. - for ( ii = 0 ; ii < vars.size() ; ii++ ) { - temp_p = vars[ii]->buffer_in; - vars[ii]->buffer_in = vars[ii]->buffer_out; - vars[ii]->buffer_out = temp_p; - } - var_data_staged = false; - - /* Relinquish sole access to vars[ii]->buffer_in. */ - pthread_mutex_unlock(©_mutex) ; - - if (binary_data) { - int index = 0; - - do { - ret = write_binary_data( index, buf1, vars, VS_VAR_LIST ); - if ( ret >= 0 ) { - index = ret ; - } else { - return(-1) ; - } - } while( index < (int)vars.size() ); - - return 0; - - } else { /* ascii mode */ - return write_ascii_data(buf1, sizeof(buf1), vars, VS_VAR_LIST ); - } - } -} - -int Trick::VariableServerThread::write_data(std::vector given_vars) { - // do not send anything when there are no variables! - if ( given_vars.size() == 0) { - return(0); - } - - /* Acquire sole access to vars[ii]->buffer_in. */ - if ( pthread_mutex_trylock(©_mutex) == 0 ) { - // Swap buffer_in and buffer_out for each vars[ii]. - for (int i = 0 ; i < given_vars.size() ; i++ ) { - void *temp_p = given_vars[i]->buffer_in; - given_vars[i]->buffer_in = given_vars[i]->buffer_out; - given_vars[i]->buffer_out = temp_p; - } - /* Relinquish sole access to vars[ii]->buffer_in. */ - pthread_mutex_unlock(©_mutex) ; - - char buf1[ MAX_MSG_LEN ]; - - if (binary_data) { - int index = 0; - - do { - int ret = write_binary_data( index, buf1, given_vars, VS_SEND_ONCE ); - if ( ret >= 0 ) { - index = ret ; - } else { - return(-1) ; - } - } while( index < (int)given_vars.size() ); - - return 0; - - } else { /* ascii mode */ - return write_ascii_data(buf1, sizeof(buf1), given_vars, VS_SEND_ONCE); - } - } -} diff --git a/trick_source/sim_services/VariableServer/VariableServerThread_write_stdio.cpp b/trick_source/sim_services/VariableServer/VariableServerThread_write_stdio.cpp deleted file mode 100644 index 684f44d6..00000000 --- a/trick_source/sim_services/VariableServer/VariableServerThread_write_stdio.cpp +++ /dev/null @@ -1,13 +0,0 @@ - -#include "trick/VariableServer.hh" -#include "trick/variable_server_message_types.h" -#include "trick/tc_proto.h" - -int Trick::VariableServerThread::write_stdio(int stream, std::string text) { - - char header[16] ; - snprintf(header, sizeof(header), "%-2d %1d %8d\n" , VS_STDIO, stream , (int)text.length()) ; - tc_write(&connection , (char *) header , strlen(header)) ; - tc_write(&connection , (char *) text.c_str() , text.length()) ; - return 0 ; -} diff --git a/trick_source/sim_services/VariableServer/VariableServer_copy_and_write_freeze.cpp b/trick_source/sim_services/VariableServer/VariableServer_copy_and_write_freeze.cpp new file mode 100644 index 00000000..81a479d1 --- /dev/null +++ b/trick_source/sim_services/VariableServer/VariableServer_copy_and_write_freeze.cpp @@ -0,0 +1,13 @@ +#include "trick/VariableServer.hh" +#include "trick/exec_proto.h" + +int Trick::VariableServer::copy_and_write_freeze() { + + pthread_mutex_lock(&map_mutex) ; + for ( auto it = var_server_sessions.begin() ; it != var_server_sessions.end() ; it++ ) { + (*it).second->copy_and_write_freeze(exec_get_freeze_frame_count()) ; + } + pthread_mutex_unlock(&map_mutex) ; + + return 0 ; +} diff --git a/trick_source/sim_services/VariableServer/VariableServer_copy_and_write_freeze_scheduled.cpp b/trick_source/sim_services/VariableServer/VariableServer_copy_and_write_freeze_scheduled.cpp new file mode 100644 index 00000000..511afbd2 --- /dev/null +++ b/trick_source/sim_services/VariableServer/VariableServer_copy_and_write_freeze_scheduled.cpp @@ -0,0 +1,28 @@ + +#include +#include + +#include "trick/VariableServer.hh" +#include "trick/TrickConstant.hh" + +int Trick::VariableServer::copy_and_write_freeze_scheduled() { + + long long next_call_tics = TRICK_MAX_LONG_LONG ; + + pthread_mutex_lock(&map_mutex) ; + for ( auto it = var_server_sessions.begin() ; it != var_server_sessions.end() ; it++ ) { + VariableServerSession * session = (*it).second ; + session->copy_and_write_freeze_scheduled(copy_and_write_freeze_job->next_tics) ; + if ( session->get_freeze_next_tics() < next_call_tics ) { + next_call_tics = session->get_freeze_next_tics() ; + } + } + pthread_mutex_unlock(&map_mutex) ; + + //reschedule the current job. TODO: a call needs to be created to do this the OO way + if ( copy_and_write_freeze_job != NULL ) { + copy_and_write_freeze_job->next_tics = next_call_tics ; + } + + return(0) ; +} diff --git a/trick_source/sim_services/VariableServer/VariableServer_copy_and_write_scheduled.cpp b/trick_source/sim_services/VariableServer/VariableServer_copy_and_write_scheduled.cpp new file mode 100644 index 00000000..9ef7fde2 --- /dev/null +++ b/trick_source/sim_services/VariableServer/VariableServer_copy_and_write_scheduled.cpp @@ -0,0 +1,28 @@ + +#include +#include + +#include "trick/VariableServer.hh" +#include "trick/TrickConstant.hh" + +int Trick::VariableServer::copy_and_write_scheduled() { + + long long next_call_tics = TRICK_MAX_LONG_LONG; + + pthread_mutex_lock(&map_mutex) ; + for ( auto it = var_server_sessions.begin() ; it != var_server_sessions.end() ; it++ ) { + VariableServerSession * session = (*it).second ; + session->copy_and_write_scheduled(copy_data_job->next_tics) ; + if ( session->get_next_tics() < next_call_tics ) { + next_call_tics = session->get_next_tics() ; + } + } + pthread_mutex_unlock(&map_mutex) ; + + //reschedule the current job. TODO: a call needs to be created to do this the OO way + if ( copy_data_job != NULL ) { + copy_data_job->next_tics = next_call_tics ; + } + + return(0) ; +} diff --git a/trick_source/sim_services/VariableServer/VariableServer_copy_and_write_top.cpp b/trick_source/sim_services/VariableServer/VariableServer_copy_and_write_top.cpp new file mode 100644 index 00000000..3118098d --- /dev/null +++ b/trick_source/sim_services/VariableServer/VariableServer_copy_and_write_top.cpp @@ -0,0 +1,13 @@ +#include "trick/exec_proto.h" +#include "trick/VariableServer.hh" + +int Trick::VariableServer::copy_and_write_top() { + + pthread_mutex_lock(&map_mutex) ; + for ( auto it = var_server_sessions.begin() ; it != var_server_sessions.end() ; it++ ) { + (*it).second->copy_and_write_top(exec_get_frame_count()) ; + } + pthread_mutex_unlock(&map_mutex) ; + + return 0 ; +} diff --git a/trick_source/sim_services/VariableServer/VariableServer_copy_data_freeze.cpp b/trick_source/sim_services/VariableServer/VariableServer_copy_data_freeze.cpp deleted file mode 100644 index bc90291c..00000000 --- a/trick_source/sim_services/VariableServer/VariableServer_copy_data_freeze.cpp +++ /dev/null @@ -1,18 +0,0 @@ - -#include -#include - -#include "trick/VariableServer.hh" - -int Trick::VariableServer::copy_data_freeze() { - - std::map < pthread_t , VariableServerThread * >::iterator it ; - - pthread_mutex_lock(&map_mutex) ; - for ( it = var_server_threads.begin() ; it != var_server_threads.end() ; it++ ) { - (*it).second->copy_data_freeze() ; - } - pthread_mutex_unlock(&map_mutex) ; - - return 0 ; -} diff --git a/trick_source/sim_services/VariableServer/VariableServer_copy_data_freeze_scheduled.cpp b/trick_source/sim_services/VariableServer/VariableServer_copy_data_freeze_scheduled.cpp deleted file mode 100644 index d770e451..00000000 --- a/trick_source/sim_services/VariableServer/VariableServer_copy_data_freeze_scheduled.cpp +++ /dev/null @@ -1,32 +0,0 @@ - -#include -#include - -#include "trick/VariableServer.hh" -#include "trick/TrickConstant.hh" - -int Trick::VariableServer::copy_data_freeze_scheduled() { - - long long next_call_tics ; - VariableServerThread * vst ; - std::map < pthread_t , VariableServerThread * >::iterator it ; - - next_call_tics = TRICK_MAX_LONG_LONG ; - - pthread_mutex_lock(&map_mutex) ; - for ( it = var_server_threads.begin() ; it != var_server_threads.end() ; it++ ) { - vst = (*it).second ; - vst->copy_data_freeze_scheduled(copy_data_freeze_job->next_tics) ; - if ( vst->get_freeze_next_tics() < next_call_tics ) { - next_call_tics = vst->get_freeze_next_tics() ; - } - } - pthread_mutex_unlock(&map_mutex) ; - - //reschedule the current job. TODO: a call needs to be created to do this the OO way - if ( copy_data_freeze_job != NULL ) { - copy_data_freeze_job->next_tics = next_call_tics ; - } - - return(0) ; -} diff --git a/trick_source/sim_services/VariableServer/VariableServer_copy_data_scheduled.cpp b/trick_source/sim_services/VariableServer/VariableServer_copy_data_scheduled.cpp deleted file mode 100644 index 80f9d967..00000000 --- a/trick_source/sim_services/VariableServer/VariableServer_copy_data_scheduled.cpp +++ /dev/null @@ -1,32 +0,0 @@ - -#include -#include - -#include "trick/VariableServer.hh" -#include "trick/TrickConstant.hh" - -int Trick::VariableServer::copy_data_scheduled() { - - long long next_call_tics ; - VariableServerThread * vst ; - std::map < pthread_t , VariableServerThread * >::iterator it ; - - next_call_tics = TRICK_MAX_LONG_LONG ; - - pthread_mutex_lock(&map_mutex) ; - for ( it = var_server_threads.begin() ; it != var_server_threads.end() ; it++ ) { - vst = (*it).second ; - vst->copy_data_scheduled(copy_data_job->next_tics) ; - if ( vst->get_next_tics() < next_call_tics ) { - next_call_tics = vst->get_next_tics() ; - } - } - pthread_mutex_unlock(&map_mutex) ; - - //reschedule the current job. TODO: a call needs to be created to do this the OO way - if ( copy_data_job != NULL ) { - copy_data_job->next_tics = next_call_tics ; - } - - return(0) ; -} diff --git a/trick_source/sim_services/VariableServer/VariableServer_copy_data_top.cpp b/trick_source/sim_services/VariableServer/VariableServer_copy_data_top.cpp deleted file mode 100644 index 4c4c2623..00000000 --- a/trick_source/sim_services/VariableServer/VariableServer_copy_data_top.cpp +++ /dev/null @@ -1,18 +0,0 @@ - -#include -#include - -#include "trick/VariableServer.hh" - -int Trick::VariableServer::copy_data_top() { - - std::map < pthread_t , VariableServerThread * >::iterator it ; - - pthread_mutex_lock(&map_mutex) ; - for ( it = var_server_threads.begin() ; it != var_server_threads.end() ; it++ ) { - (*it).second->copy_data_top() ; - } - pthread_mutex_unlock(&map_mutex) ; - - return 0 ; -} diff --git a/trick_source/sim_services/VariableServer/VariableServer_freeze_init.cpp b/trick_source/sim_services/VariableServer/VariableServer_freeze_init.cpp index 1a8f3e36..c7a81c04 100644 --- a/trick_source/sim_services/VariableServer/VariableServer_freeze_init.cpp +++ b/trick_source/sim_services/VariableServer/VariableServer_freeze_init.cpp @@ -9,25 +9,21 @@ int Trick::VariableServer::freeze_init() { - long long next_call_tics ; - VariableServerThread * vst ; - std::map < pthread_t , VariableServerThread * >::iterator it ; - - next_call_tics = TRICK_MAX_LONG_LONG ; + long long next_call_tics = TRICK_MAX_LONG_LONG ; pthread_mutex_lock(&map_mutex) ; - for ( it = var_server_threads.begin() ; it != var_server_threads.end() ; it++ ) { - vst = (*it).second ; - vst->freeze_init() ; - if ( vst->get_freeze_next_tics() < next_call_tics ) { - next_call_tics = vst->get_freeze_next_tics() ; + for ( auto it = var_server_sessions.begin() ; it != var_server_sessions.end() ; it++ ) { + VariableServerSession * session = (*it).second ; + session->freeze_init() ; + if ( session->get_freeze_next_tics() < next_call_tics ) { + next_call_tics = session->get_freeze_next_tics() ; } } pthread_mutex_unlock(&map_mutex) ; //reschedule the current job. TODO: a call needs to be created to do this the OO way - if ( copy_data_freeze_job != NULL ) { - copy_data_freeze_job->next_tics = next_call_tics ; + if ( copy_and_write_freeze_job != NULL ) { + copy_and_write_freeze_job->next_tics = next_call_tics ; } return(0) ; diff --git a/trick_source/sim_services/VariableServer/VariableServer_get_next_freeze_call_time.cpp b/trick_source/sim_services/VariableServer/VariableServer_get_next_freeze_call_time.cpp index 1fb12377..f001c76f 100644 --- a/trick_source/sim_services/VariableServer/VariableServer_get_next_freeze_call_time.cpp +++ b/trick_source/sim_services/VariableServer/VariableServer_get_next_freeze_call_time.cpp @@ -6,23 +6,18 @@ int Trick::VariableServer::get_next_freeze_call_time() { - std::map < pthread_t , VariableServerThread * >::iterator it ; - VariableServerThread * vst ; - - long long next_call_tics ; - - next_call_tics = TRICK_MAX_LONG_LONG ; + long long next_call_tics = TRICK_MAX_LONG_LONG; pthread_mutex_lock(&map_mutex) ; - for ( it = var_server_threads.begin() ; it != var_server_threads.end() ; it++ ) { - vst = (*it).second ; - if ( vst->get_freeze_next_tics() < next_call_tics ) { - next_call_tics = vst->get_freeze_next_tics() ; + for ( auto it = var_server_sessions.begin() ; it != var_server_sessions.end() ; it++ ) { + VariableServerSession * session = (*it).second ; + if ( session->get_freeze_next_tics() < next_call_tics ) { + next_call_tics = session->get_freeze_next_tics() ; } } pthread_mutex_unlock(&map_mutex) ; - copy_data_freeze_job->next_tics = next_call_tics ; + copy_and_write_freeze_job->next_tics = next_call_tics ; return(0) ; } diff --git a/trick_source/sim_services/VariableServer/VariableServer_get_next_sync_call_time.cpp b/trick_source/sim_services/VariableServer/VariableServer_get_next_sync_call_time.cpp index d728952d..fd969d64 100644 --- a/trick_source/sim_services/VariableServer/VariableServer_get_next_sync_call_time.cpp +++ b/trick_source/sim_services/VariableServer/VariableServer_get_next_sync_call_time.cpp @@ -6,18 +6,13 @@ int Trick::VariableServer::get_next_sync_call_time() { - std::map < pthread_t , VariableServerThread * >::iterator it ; - VariableServerThread * vst ; - - long long next_call_tics ; - - next_call_tics = TRICK_MAX_LONG_LONG ; + long long next_call_tics = TRICK_MAX_LONG_LONG ; pthread_mutex_lock(&map_mutex) ; - for ( it = var_server_threads.begin() ; it != var_server_threads.end() ; it++ ) { - vst = (*it).second ; - if ( vst->get_next_tics() < next_call_tics ) { - next_call_tics = vst->get_next_tics() ; + for ( auto it = var_server_sessions.begin() ; it != var_server_sessions.end() ; it++ ) { + VariableServerSession * session = (*it).second ; + if ( session->get_next_tics() < next_call_tics ) { + next_call_tics = session->get_next_tics() ; } } pthread_mutex_unlock(&map_mutex) ; diff --git a/trick_source/sim_services/VariableServer/VariableServer_get_var_server_port.cpp b/trick_source/sim_services/VariableServer/VariableServer_get_var_server_port.cpp deleted file mode 100644 index a3093af1..00000000 --- a/trick_source/sim_services/VariableServer/VariableServer_get_var_server_port.cpp +++ /dev/null @@ -1,42 +0,0 @@ - -#include -#include "trick/VariableServer.hh" - -int Trick::VariableServer::create_tcp_socket(const char * address, unsigned short in_port ) { - Trick::VariableServerListenThread * new_listen_thread = new Trick::VariableServerListenThread ; - new_listen_thread->create_tcp_socket(address, in_port) ; - new_listen_thread->copy_cpus(listen_thread.get_cpus()) ; - new_listen_thread->create_thread() ; - additional_listen_threads[new_listen_thread->get_pthread_id()] = new_listen_thread ; - - return 0 ; -} - -int Trick::VariableServer::create_udp_socket(const char * address, unsigned short in_port ) { - int ret ; - Trick::VariableServerThread * vst ; - vst = new Trick::VariableServerThread(NULL) ; - ret = vst->create_udp_socket(address, in_port) ; - if ( ret == 0 ) { - vst->copy_cpus(listen_thread.get_cpus()) ; - vst->create_thread() ; - } - //vst->var_debug(3) ; - - return ret ; -} - -int Trick::VariableServer::create_multicast_socket(const char * mcast_address, const char * address, unsigned short in_port ) { - int ret ; - Trick::VariableServerThread * vst ; - vst = new Trick::VariableServerThread(NULL) ; - ret = vst->create_mcast_socket(mcast_address, address, in_port) ; - if ( ret == 0 ) { - vst->copy_cpus(listen_thread.get_cpus()) ; - vst->create_thread() ; - } - //vst->var_debug(3) ; - - return ret ; -} - diff --git a/trick_source/sim_services/VariableServer/VariableServer_init.cpp b/trick_source/sim_services/VariableServer/VariableServer_init.cpp index 9a6bfa31..0b224b22 100644 --- a/trick_source/sim_services/VariableServer/VariableServer_init.cpp +++ b/trick_source/sim_services/VariableServer/VariableServer_init.cpp @@ -4,11 +4,9 @@ int Trick::VariableServer::init() { - int ret ; - /* start up a thread for the input processor variable server */ if ( enabled ) { - ret = listen_thread.check_and_move_listen_device() ; + int ret = listen_thread.check_and_move_listen_device() ; if ( ret != 0 ) { return ret ; } diff --git a/trick_source/sim_services/VariableServer/VariableServer_open_additional_servers.cpp b/trick_source/sim_services/VariableServer/VariableServer_open_additional_servers.cpp new file mode 100644 index 00000000..06f560f7 --- /dev/null +++ b/trick_source/sim_services/VariableServer/VariableServer_open_additional_servers.cpp @@ -0,0 +1,96 @@ + +#include +#include "trick/VariableServer.hh" +#include "trick/message_proto.h" +#include "trick/message_type.h" + +#include "trick/TCPConnection.hh" +#include "trick/UDPConnection.hh" +#include "trick/TCPClientListener.hh" + +int Trick::VariableServer::create_tcp_socket(const char * address, unsigned short in_port ) { + // Open a VariableServerListenThread to manage this server + + TCPClientListener * listener = new TCPClientListener(); + int status = listener->initialize(address, in_port); + + if (status != 0) { + message_publish(MSG_ERROR, "ERROR: Could not establish additional listen port at address %s and port %d for Variable Server.\n", address, in_port); + delete listener; + return 0; + } + + std::string set_address = listener->getHostname(); + int set_port = listener->getPort(); + + Trick::VariableServerListenThread * new_listen_thread = new Trick::VariableServerListenThread(listener) ; + + new_listen_thread->copy_cpus(listen_thread.get_cpus()) ; + new_listen_thread->create_thread() ; + additional_listen_threads[new_listen_thread->get_pthread_id()] = new_listen_thread ; + + message_publish(MSG_INFO, "Created TCP variable server %s: %d\n", set_address.c_str(), set_port); + + return 0 ; +} + +int Trick::VariableServer::create_udp_socket(const char * address, unsigned short in_port ) { + // UDP sockets are created without a listen thread, and represent only 1 session + // Create a VariableServerSessionThread to manage this session + + UDPConnection * udp_conn = new UDPConnection(); + int status = udp_conn->initialize(address, in_port); + if ( status != 0 ) { + message_publish(MSG_ERROR, "ERROR: Could not establish UDP port at address %s and port %d for Variable Server.\n", address, in_port); + delete udp_conn; + return 0; + } + + std::string set_address = udp_conn->getHostname(); + int set_port = udp_conn->getPort(); + + Trick::VariableServerSessionThread * vst = new Trick::VariableServerSessionThread() ; + + vst->set_connection(udp_conn); + vst->copy_cpus(listen_thread.get_cpus()) ; + vst->create_thread() ; + + message_publish(MSG_INFO, "Created UDP variable server %s: %d\n", set_address.c_str(), set_port); + + return 0 ; +} + +int Trick::VariableServer::create_multicast_socket(const char * mcast_address, const char * address, unsigned short in_port ) { + + // Multicast sockets are created without a listen thread, and represent only 1 session + // Create a VariableServerSessionThread to manage this session + + if (mcast_address == NULL || mcast_address[0] == '\0') { + message_publish(MSG_ERROR, "Multicast address must be defined.\n"); + return -1; + } + + MulticastGroup * multicast = new MulticastGroup(); + message_publish(MSG_INFO, "Created UDP variable server %s: %d\n", address, in_port); + + int status = multicast->initialize_with_receiving(address, mcast_address, in_port); + if ( status != 0 ) { + message_publish(MSG_ERROR, "ERROR: Could not establish Multicast port at address %s and port %d for Variable Server.\n", address, in_port); + delete multicast; + return 0; + } + + std::string set_address = multicast->getHostname(); + int set_port = multicast->getPort(); + + Trick::VariableServerSessionThread * vst = new Trick::VariableServerSessionThread() ; + + vst->set_connection(multicast); + vst->copy_cpus(listen_thread.get_cpus()) ; + vst->create_thread() ; + + message_publish(MSG_INFO, "Multicast variable server output %s:%d\n", mcast_address, set_port) ; + + return 0 ; +} + diff --git a/trick_source/sim_services/VariableServer/VariableServer_restart.cpp b/trick_source/sim_services/VariableServer/VariableServer_restart.cpp index 2dfae9fe..46b62ac0 100644 --- a/trick_source/sim_services/VariableServer/VariableServer_restart.cpp +++ b/trick_source/sim_services/VariableServer/VariableServer_restart.cpp @@ -9,9 +9,12 @@ int Trick::VariableServer::restart() { if ( listen_thread.get_pthread_id() == 0 ) { listen_thread.create_thread() ; } - std::map < pthread_t , VariableServerListenThread * >::iterator it ; - for( it = additional_listen_threads.begin() ; it != additional_listen_threads.end() ; it++ ) { - (*it).second->restart() ; + + for (const auto& listen_it : additional_listen_threads) { + listen_it.second->restart(); + if ( listen_it.second->get_pthread_id() == 0 ) { + listen_it.second->create_thread() ; + } } return 0 ; } @@ -23,14 +26,17 @@ int Trick::VariableServer::restart() { // Suspend variable server processing prior to reloading a checkpoint. int Trick::VariableServer::suspendPreCheckpointReload() { - std::map::iterator pos ; + // Pause listening on all listening threads listen_thread.pause_listening() ; + for (const auto& listen_it : additional_listen_threads) { + listen_it.second->pause_listening(); + } + // Suspend session threads pthread_mutex_lock(&map_mutex) ; - for ( pos = var_server_threads.begin() ; pos != var_server_threads.end() ; pos++ ) { - VariableServerThread* vst = (*pos).second ; - vst->preload_checkpoint() ; + for (const auto& vst_it : var_server_threads ) { + vst_it.second->preload_checkpoint() ; } pthread_mutex_unlock(&map_mutex) ; @@ -39,17 +45,20 @@ int Trick::VariableServer::suspendPreCheckpointReload() { // Resume variable server processing after reloading a MemoryManager (ASCII) checkpoint. int Trick::VariableServer::resumePostCheckpointReload() { - std::map::iterator pos ; + std::map::iterator pos ; + // Resume all session threads pthread_mutex_lock(&map_mutex) ; - // For each Variable Server Thread ... - for ( pos = var_server_threads.begin() ; pos != var_server_threads.end() ; pos++ ) { - VariableServerThread* vst = (*pos).second ; - vst->restart() ; + for (const auto& vst_it : var_server_threads ) { + vst_it.second->restart() ; } pthread_mutex_unlock(&map_mutex) ; + // Restart listening on all listening threads listen_thread.restart_listening() ; + for (const auto& listen_it : additional_listen_threads) { + listen_it.second->restart_listening(); + } return 0; } diff --git a/trick_source/sim_services/VariableServer/VariableServer_shutdown.cpp b/trick_source/sim_services/VariableServer/VariableServer_shutdown.cpp index bb99db07..499da7c9 100644 --- a/trick_source/sim_services/VariableServer/VariableServer_shutdown.cpp +++ b/trick_source/sim_services/VariableServer/VariableServer_shutdown.cpp @@ -2,12 +2,17 @@ #include "trick/VariableServer.hh" int Trick::VariableServer::shutdown() { + + // Shutdown all listen threads listen_thread.cancel_thread() ; - std::map < pthread_t , VariableServerThread * >::iterator it ; + for (auto& listen_it : additional_listen_threads) { + listen_it.second->cancel_thread(); + } + + // Shutdown all session threads pthread_mutex_lock(&map_mutex) ; - for ( it = var_server_threads.begin() ; it != var_server_threads.end() ; it++ ) { - (*it).second->cancel_thread() ; - // cancelling causes each var_server_thread map element to be erased by the exit_var_thread function + for (auto& it : var_server_threads) { + it.second->cancel_thread() ; } pthread_mutex_unlock(&map_mutex) ; diff --git a/trick_source/sim_services/VariableServer/exit_var_thread.cpp b/trick_source/sim_services/VariableServer/exit_var_thread.cpp index 95b30e82..80210980 100644 --- a/trick_source/sim_services/VariableServer/exit_var_thread.cpp +++ b/trick_source/sim_services/VariableServer/exit_var_thread.cpp @@ -1,20 +1,17 @@ #include "trick/VariableServer.hh" -#include "trick/tc_proto.h" +// This should only be called from the VST itself void exit_var_thread(void *in_vst) { - Trick::VariableServerThread * vst = (Trick::VariableServerThread *) in_vst ; - Trick::VariableServer * vs = vst->get_vs() ; + Trick::VariableServerSessionThread * vst = (Trick::VariableServerSessionThread *) in_vst ; - tc_disconnect(&vst->get_connection()); + Trick::VariableServer * vs = vst->get_vs() ; + + vs->delete_session(vst->get_pthread_id()); // Tell the variable server that this thread is exiting. vs->delete_vst(vst->get_pthread_id()) ; - // This will deleting the vst object from within the object itself. exit_var_thread - // is called from within Trick::VariableServerThread::thread_body. - // I am claiming this is safe as this is the last thing routing is the last thing - // that touches vst. The C++ FAQ says it's safe if you can say this. (Alex 6/9/14) - delete vst ; + vst->cleanup(); } diff --git a/trick_source/sim_services/VariableServer/test/.gitignore b/trick_source/sim_services/VariableServer/test/.gitignore new file mode 100644 index 00000000..ea339cfd --- /dev/null +++ b/trick_source/sim_services/VariableServer/test/.gitignore @@ -0,0 +1,4 @@ +lcov_html/* +*.o +*_test +*.info \ No newline at end of file diff --git a/trick_source/sim_services/VariableServer/test/Makefile b/trick_source/sim_services/VariableServer/test/Makefile new file mode 100644 index 00000000..7d7fdbff --- /dev/null +++ b/trick_source/sim_services/VariableServer/test/Makefile @@ -0,0 +1,96 @@ + +#SYNOPSIS: +# +# make [all] - makes everything. +# make TARGET - makes the given target. +# make clean - removes all files generated by make. + +include $(dir $(lastword $(MAKEFILE_LIST)))../../../../share/trick/makefiles/Makefile.common + +# Replace -isystem with -I so ICG doesn't skip Trick headers +TRICK_SYSTEM_CXXFLAGS := $(subst -isystem,-I,$(TRICK_SYSTEM_CXXFLAGS)) -Wno-unused-command-line-argument + +# Flags passed to the preprocessor. +TRICK_CXXFLAGS += -I$(GTEST_HOME)/include -I$(TRICK_HOME)/include -g -Wall -Wextra -Wno-sign-compare -std=c++11 ${TRICK_SYSTEM_CXXFLAGS} ${TRICK_TEST_FLAGS} +TRICK_LIBS = -L${TRICK_LIB_DIR} -ltrick_mm -ltrick_units -ltrick_comm -ltrick_pyip -ltrick -ltrick_mm -ltrick_units -ltrick_comm -ltrick_pyip -ltrick -ltrick_var_binary_parser -ltrick_connection_handlers -ltrick_comm +TRICK_EXEC_LINK_LIBS += -L${GTEST_HOME}/lib64 -L${GTEST_HOME}/lib -lgtest -lgtest_main -lgmock -lpthread + +# All tests produced by this Makefile. Remember to add new tests you +# created to the list. + +VARIABLE_REFERENCE_TESTS = VariableReference_test \ + VariableReference_writeValueAscii_test \ + VariableReference_writeValueBinary_test + +VARIABLE_SESSION_TESTS = VariableServerSession_test + +TESTS = $(VARIABLE_REFERENCE_TESTS) $(VARIABLE_SESSION_TESTS) VariableServerSessionThread_test VariableServerListenThread_test VariableServer_test + +TEST_OBJS = $(addprefix $(OBJ_DIR)/, $(addsuffix .o, $(TESTS))) + +TO_ICG = VariableReference_test + +ICG_OBJS = $(addprefix $(OBJ_DIR)/io_, $(addsuffix .o, $(TO_ICG))) + +OBJ_DIR = obj + +print: + echo $(ICG_OBJS) + +# House-keeping build targets. + +all : test + +test: $(TESTS) + for TEST in $(TESTS) ; do \ + ./$$TEST --gtest_output=xml:${TRICK_HOME}/trick_test/$$TEST.xml ; \ + done + +$(OBJ_DIR): + mkdir $(OBJ_DIR) + +$(TEST_OBJS): $(OBJ_DIR)/%.o: %.cc $(OBJ_DIR) + $(TRICK_CXX) $(TRICK_CXXFLAGS) -c $< -o $@ -L${TRICK_HOME}/lib_${TRICK_HOST_CPU} $(TRICK_LIBS) $(TRICK_EXEC_LINK_LIBS) + +$(ICG_OBJS): $(OBJ_DIR)/io_%.o : %.hh $(OBJ_DIR) + ${TRICK_HOME}/bin/trick-ICG -sim_services -o ./io_src $(TRICK_CXXFLAGS) $< + $(TRICK_CXX) $(TRICK_CXXFLAGS) -c io_src/io_$*.cpp -o $@ -L${TRICK_HOME}/lib_${TRICK_HOST_CPU} $(TRICK_LIBS) $(TRICK_EXEC_LINK_LIBS) + +$(VARIABLE_REFERENCE_TESTS): %: $(OBJ_DIR)/%.o $(OBJ_DIR)/io_VariableReference_test.o + $(TRICK_CXX) $(TRICK_SYSTEM_LDFLAGS) $(TRICK_CXXFLAGS) -o $@ $^ -L${TRICK_HOME}/lib_${TRICK_HOST_CPU} $(TRICK_LIBS) $(TRICK_EXEC_LINK_LIBS) + +$(VARIABLE_SESSION_TESTS): %: $(OBJ_DIR)/%.o + $(TRICK_CXX) $(TRICK_SYSTEM_LDFLAGS) $(TRICK_CXXFLAGS) $^ -L${TRICK_HOME}/lib_${TRICK_HOST_CPU} $(TRICK_LIBS) $(TRICK_EXEC_LINK_LIBS) -o $@ + +VariableServerSessionThread_test: %: $(OBJ_DIR)/%.o + $(TRICK_CXX) $(TRICK_SYSTEM_LDFLAGS) $(TRICK_CXXFLAGS) -o $@ $^ -L${TRICK_HOME}/lib_${TRICK_HOST_CPU} $(TRICK_LIBS) $(TRICK_EXEC_LINK_LIBS) + +VariableServerListenThread_test: %: $(OBJ_DIR)/%.o + $(TRICK_CXX) $(TRICK_SYSTEM_LDFLAGS) $(TRICK_CXXFLAGS) -o $@ $^ -L${TRICK_HOME}/lib_${TRICK_HOST_CPU} $(TRICK_LIBS) $(TRICK_EXEC_LINK_LIBS) + +VariableServer_test: %: $(OBJ_DIR)/%.o + $(TRICK_CXX) $(TRICK_SYSTEM_LDFLAGS) $(TRICK_CXXFLAGS) -o $@ $^ -L${TRICK_HOME}/lib_${TRICK_HOST_CPU} $(TRICK_LIBS) $(TRICK_EXEC_LINK_LIBS) + + +code-coverage: test + # Give rid of any old code-coverage HTML we may have. + rm -rf lcov_html + # Gather coverage information about the src code. + lcov --capture \ + --directory ../object_${TRICK_HOST_CPU} \ + --base-directory ../ \ + --output-file src_coverage.info + # Filter out information about directories that we don't care about. + lcov --remove src_coverage.info '/Applications/*' '/usr/include/*' '/Library/*' \ + --output-file VariableServer_code_coverage.info + # Generate HTML + genhtml VariableServer_code_coverage.info \ + --output-directory lcov_html + # Clean up + # rm *.info + lcov --list VariableServer_code_coverage.info + +clean : + rm -f $(TESTS) + rm -f *.gcno *.gcda + rm -rf io_src xml obj \ No newline at end of file diff --git a/trick_source/sim_services/VariableServer/test/VariableReference_test.cc b/trick_source/sim_services/VariableServer/test/VariableReference_test.cc new file mode 100644 index 00000000..9c616441 --- /dev/null +++ b/trick_source/sim_services/VariableServer/test/VariableReference_test.cc @@ -0,0 +1,512 @@ +#include "VariableReference_test.hh" + +TEST_F(VariableReference_test, getName) { + // ARRANGE + // Create a variable to make a reference for + int test_int = 5; + (void) memmgr->declare_extern_var(&test_int, "int test_int"); + Trick::VariableReference ref("test_int"); + + // ACT + // ASSERT + EXPECT_EQ(ref.getName(), std::string("test_int")); +} + +TEST_F(VariableReference_test, validateAddress) { + // ARRANGE + // Create a variable to make a reference for + int test_a = 5; + (void) memmgr->declare_extern_var(&test_a, "int test_a"); + Trick::VariableReference ref("test_a"); + + float test_b = 5.0; + (void) memmgr->declare_extern_var(&test_b, "float test_b"); + Trick::VariableReference ref_broken("test_b"); + memmgr->delete_var("test_b"); + + float test_c = 5.0; + (void) memmgr->declare_extern_var(&test_c, "float test_c"); + Trick::VariableReference ref_tagged("test_c"); + ref_tagged.tagAsInvalid(); + + // ACT + // ASSERT + EXPECT_EQ(ref.validate(), true); + EXPECT_EQ(ref_broken.validate(), false); + // A reference already tagged as invalid is considered ok by this check + EXPECT_EQ(ref_tagged.validate(), true); +} + + +TEST_F(VariableReference_test, stageValue_set) { + // ARRANGE + // Create a variable to make a reference for + int test_a = 5; + (void) memmgr->declare_extern_var(&test_a, "int test_a"); + Trick::VariableReference ref("test_a"); + + // ACT + ref.stageValue(); + + // ASSERT + EXPECT_EQ(ref.isStaged(), true); + EXPECT_EQ(ref.isWriteReady(), false); +} + +TEST_F(VariableReference_test, prepareForWrite_set) { + // ARRANGE + // Create a variable to make a reference for + int test_a = 5; + (void) memmgr->declare_extern_var(&test_a, "int test_a"); + Trick::VariableReference ref("test_a"); + + // ACT + ref.stageValue(); + ref.prepareForWrite(); + + // ASSERT + EXPECT_EQ(ref.isStaged(), false); + EXPECT_EQ(ref.isWriteReady(), true); +} + +TEST_F(VariableReference_test, prepareForWrite_fails_if_not_staged) { + // ARRANGE + // Create a variable to make a reference for + int test_a = 5; + (void) memmgr->declare_extern_var(&test_a, "int test_a"); + Trick::VariableReference ref("test_a"); + + // ACT + ref.prepareForWrite(); + + // ASSERT + EXPECT_EQ(ref.isStaged(), false); + EXPECT_EQ(ref.isWriteReady(), false); +} + +TEST_F(VariableReference_test, writeValueAscii_fails_if_not_write_ready) { + // ARRANGE + // Create a variable to make a reference for + int test_a = 5; + (void) memmgr->declare_extern_var(&test_a, "int test_a"); + Trick::VariableReference ref("test_a"); + std::stringstream ss; + + // ACT + // ASSERT + EXPECT_EQ(ref.writeValueAscii(ss), -1); +} + +TEST_F(VariableReference_test, var_does_not_exist) { + // ARRANGE + Trick::VariableReference ref("no_such_var"); + std::stringstream ss; + + // ACT + ref.stageValue(); + ref.prepareForWrite(); + ref.writeValueAscii(ss); + + // ASSERT + EXPECT_EQ(ref.getType(), TRICK_NUMBER_OF_TYPES); + EXPECT_EQ(ss.str(), "BAD_REF"); +} + +TEST_F(VariableReference_test, stl_var) { + // ARRANGE + TestObject my_test; + my_test.vec.push_back(5); + (void) memmgr->declare_extern_var(&my_test, "TestObject my_test"); + + Trick::VariableReference ref("my_test.vec"); + std::stringstream ss; + + // ACT + ref.stageValue(); + ref.prepareForWrite(); + ref.writeValueAscii(ss); + + // ASSERT + EXPECT_EQ(ref.isWriteReady(), true); + EXPECT_EQ(ref.getType(), TRICK_NUMBER_OF_TYPES); + EXPECT_EQ(ss.str(), "BAD_REF"); +} + +TEST_F(VariableReference_test, structured_var) { + // ARRANGE + TestObject my_test; + my_test.obj.a = 5; + (void) memmgr->declare_extern_var(&my_test, "TestObject my_test"); + + Trick::VariableReference ref("my_test.obj"); + std::stringstream ss; + + // ACT + ref.stageValue(); + ref.prepareForWrite(); + ref.writeValueAscii(ss); + + // ASSERT + EXPECT_EQ(ref.isWriteReady(), true); + EXPECT_EQ(ref.getType(), TRICK_NUMBER_OF_TYPES); + EXPECT_EQ(ss.str(), "BAD_REF"); +} + +TEST_F(VariableReference_test, printWithoutUnits) { + // ARRANGE + // Create a variable to make a reference for + TestObject obj; + obj.length = 5000; + (void) memmgr->declare_extern_var(&obj, "TestObject obj"); + Trick::VariableReference ref("obj.length"); + std::stringstream ss; + + // ACT + EXPECT_EQ(ref.getBaseUnits(), std::string("m")); + + // ASSERT + // Doesn't actually print with units unless set + ref.stageValue(); + ref.prepareForWrite(); + ref.writeValueAscii(ss); + EXPECT_EQ(ss.str(), "5000"); +} + +TEST_F(VariableReference_test, setUnits) { + // ARRANGE + // Create a variable to make a reference for + TestObject obj; + obj.length = 5000; + (void) memmgr->declare_extern_var(&obj, "TestObject obj"); + Trick::VariableReference ref("obj.length"); + std::stringstream ss; + + // ACT + ref.setRequestedUnits("km"); + + // ASSERT + ref.stageValue(); + ref.prepareForWrite(); + ref.writeValueAscii(ss); + EXPECT_EQ(ss.str(), "5 {km}"); +} + +TEST_F(VariableReference_test, setUnitsTwice) { + // ARRANGE + // Create a variable to make a reference for + TestObject obj; + obj.length = 5000; + (void) memmgr->declare_extern_var(&obj, "TestObject obj"); + Trick::VariableReference ref("obj.length"); + std::stringstream ss; + + // ACT + ref.setRequestedUnits("km"); + + // ASSERT + ref.stageValue(); + ref.prepareForWrite(); + ref.writeValueAscii(ss); + EXPECT_EQ(ss.str(), "5 {km}"); + ss.str(""); + + // ACT + ref.setRequestedUnits("mm"); + + // ASSERT + ref.stageValue(); + ref.prepareForWrite(); + ref.writeValueAscii(ss); + EXPECT_EQ(ss.str(), "5000000 {mm}"); +} + + +TEST_F(VariableReference_test, setUnitsBadFromUnits) { + // ARRANGE + // Create a variable to make a reference for + TestObject obj; + obj.error_units = 50; + (void) memmgr->declare_extern_var(&obj, "TestObject obj"); + Trick::VariableReference ref("obj.error_units"); + std::stringstream ss; + + // ACT + ref.setRequestedUnits("mm"); + + // ASSERT + ref.stageValue(); + ref.prepareForWrite(); + ref.writeValueAscii(ss); + EXPECT_EQ(ss.str(), "50"); +} + +TEST_F(VariableReference_test, setUnitsBadToUnits) { + // ARRANGE + // Create a variable to make a reference for + TestObject obj; + obj.a = 0.5; + (void) memmgr->declare_extern_var(&obj, "TestObject obj"); + Trick::VariableReference ref("obj.a"); + std::stringstream ss; + + // ACT + ref.setRequestedUnits("asldfjks"); + + // ASSERT + ref.stageValue(); + ref.prepareForWrite(); + ref.writeValueAscii(ss); + EXPECT_EQ(ss.str(), "0.5"); +} + +TEST_F(VariableReference_test, setUnitsDouble) { + // ARRANGE + // Create a variable to make a reference for + TestObject obj; + obj.a = 0.5; + (void) memmgr->declare_extern_var(&obj, "TestObject obj"); + Trick::VariableReference ref("obj.a"); + std::stringstream ss; + + // ACT + ref.setRequestedUnits("ms"); + + // ASSERT + ref.stageValue(); + ref.prepareForWrite(); + ref.writeValueAscii(ss); + EXPECT_EQ(ss.str(), "500 {ms}"); +} + +TEST_F(VariableReference_test, setUnitsLong) { + // ARRANGE + // Create a variable to make a reference for + TestObject obj; + obj.b = 1; + (void) memmgr->declare_extern_var(&obj, "TestObject obj"); + Trick::VariableReference ref("obj.b"); + std::stringstream ss; + + // ACT + ref.setRequestedUnits("m"); + + // ASSERT + ref.stageValue(); + ref.prepareForWrite(); + ref.writeValueAscii(ss); + EXPECT_EQ(ss.str(), "1852 {m}"); +} + +TEST_F(VariableReference_test, setUnitsLongLong) { + // ARRANGE + // Create a variable to make a reference for + TestObject obj; + obj.c = 10000000; + (void) memmgr->declare_extern_var(&obj, "TestObject obj"); + Trick::VariableReference ref("obj.c"); + std::stringstream ss; + + // ACT + ref.setRequestedUnits("m"); + + // ASSERT + ref.stageValue(); + ref.prepareForWrite(); + ref.writeValueAscii(ss); + EXPECT_EQ(ss.str(), "254000 {m}"); +} + +TEST_F(VariableReference_test, setUnitsUnsignedLong) { + // ARRANGE + // Create a variable to make a reference for + TestObject obj; + obj.d = 1; + (void) memmgr->declare_extern_var(&obj, "TestObject obj"); + Trick::VariableReference ref("obj.d"); + std::stringstream ss; + + // ACT + ref.setRequestedUnits("mi"); + + // ASSERT + ref.stageValue(); + ref.prepareForWrite(); + ref.writeValueAscii(ss); + EXPECT_EQ(ss.str(), "5878625079535 {mi}"); +} + +TEST_F(VariableReference_test, setUnitsUnsignedLongLong) { + // ARRANGE + // Create a variable to make a reference for + TestObject obj; + obj.e = 1; + (void) memmgr->declare_extern_var(&obj, "TestObject obj"); + Trick::VariableReference ref("obj.e"); + std::stringstream ss; + + // ACT + ref.setRequestedUnits("mm"); + + // ASSERT + ref.stageValue(); + ref.prepareForWrite(); + ref.writeValueAscii(ss); + EXPECT_EQ(ss.str(), "1609344 {mm}"); +} + +// Byteswap tests + +TEST_F(VariableReference_test, byteswap_chars) { + // ARRANGE + char in = 'a'; + char out; + + memmgr->declare_extern_var(&in, "char a"); + Trick::VariableReference ref("a"); + + // ACT + Trick::VariableReference::byteswap_var(&out, &in, ref); + + // ASSERT + EXPECT_EQ(in, 'a'); + EXPECT_EQ(out, 'a'); +} + +TEST_F(VariableReference_test, byteswap_char_array) { + // ARRANGE + char in[7] = "Jackie"; + char out[7]; + + memmgr->declare_extern_var(&in, "char a[7]"); + Trick::VariableReference ref("a"); + + // ACT + Trick::VariableReference::byteswap_var(out, in, ref); + + // ASSERT + for (int i = 0; i < 7; i++) { + EXPECT_EQ(out[i], in[i]); + } +} + +// Pointers to the beginning of the variables +bool check_that_val_is_byteswapped (char * expected, char * byteswap, int val_size) { + for (int i = 0; i < val_size; i++) { + if (expected[i] != byteswap[val_size-i-1]) return false; + } + return true; +} + +TEST_F(VariableReference_test, byteswap_short) { + // ARRANGE + short in = 100; + short out; + + memmgr->declare_extern_var(&in, "short a"); + Trick::VariableReference ref("a"); + + // ACT + Trick::VariableReference::byteswap_var((char *)(&out),(char *)(&in), ref); + + // ASSERT + EXPECT_TRUE(check_that_val_is_byteswapped((char *)(&out),(char *)(&in), sizeof(short))); +} + +TEST_F(VariableReference_test, byteswap_int) { + // ARRANGE + int in = 123456; + int out; + + memmgr->declare_extern_var(&in, "int a"); + Trick::VariableReference ref("a"); + + // ACT + Trick::VariableReference::byteswap_var((char *)(&out),(char *)(&in), ref); + + // ASSERT + EXPECT_TRUE(check_that_val_is_byteswapped((char *)(&out),(char *)(&in), sizeof(int))); +} + +TEST_F(VariableReference_test, byteswap_long) { + // ARRANGE + long in = 123456789; + long out; + + memmgr->declare_extern_var(&in, "long a"); + Trick::VariableReference ref("a"); + + // ACT + Trick::VariableReference::byteswap_var((char *)(&out),(char *)(&in), ref); + + // ASSERT + EXPECT_TRUE(check_that_val_is_byteswapped((char *)(&out),(char *)(&in), sizeof(long))); +} + +TEST_F(VariableReference_test, byteswap_long_arr) { + // ARRANGE + long in[5] = {123456789, 123456780, __LONG_MAX__, -100000000, 0}; + long out[5]; + + memmgr->declare_extern_var(&in, "long a[5]"); + Trick::VariableReference ref("a"); + + // ACT + Trick::VariableReference::byteswap_var((char *)(out),(char *)(in), ref); + + // ASSERT + for (int i = 0; i < 5; i++) { + EXPECT_TRUE(check_that_val_is_byteswapped((char *)(&out[i]),(char *)(&in[i]), sizeof(long))); + } +} + +TEST_F(VariableReference_test, byteswap_int_arr) { + // ARRANGE + int in[5] = {20945, -29384293, INT32_MAX, INT32_MIN, 0}; + int out[5]; + + memmgr->declare_extern_var(&in, "int a[5]"); + Trick::VariableReference ref("a"); + + // ACT + Trick::VariableReference::byteswap_var((char *)(out),(char *)(in), ref); + + // ASSERT + for (int i = 0; i < 5; i++) { + EXPECT_TRUE(check_that_val_is_byteswapped((char *)(&out[i]),(char *)(&in[i]), sizeof(int))); + } +} + +TEST_F(VariableReference_test, byteswap_int_multidimensional_arr) { + // ARRANGE + int multidim_arr[5][4][3][2]; + int out[5][4][3][2]; + + + int counter = 500; + for (int i = 0; i < 5; i++) { + for (int j = 0; j < 4; j++) { + for (int k = 0; k < 3; k++) { + for (int l = 0; l < 2; l++) { + multidim_arr[i][j][k][l] = counter++; + } + } + } + } + + memmgr->declare_extern_var(&multidim_arr, "int multidim_arr[5][4][3][2]"); + Trick::VariableReference ref("multidim_arr"); + + // ACT + Trick::VariableReference::byteswap_var((char *)(out),(char *)(multidim_arr), ref); + + // ASSERT + for (int i = 0; i < 5; i++) { + for (int j = 0; j < 4; j++) { + for (int k = 0; k < 3; k++) { + for (int l = 0; l < 2; l++) { + EXPECT_TRUE(check_that_val_is_byteswapped((char *)(&(out[i][j][k][l])),(char *)(&(multidim_arr[i][j][k][l])), sizeof(int))); + } + } + } + } +} \ No newline at end of file diff --git a/trick_source/sim_services/VariableServer/test/VariableReference_test.hh b/trick_source/sim_services/VariableServer/test/VariableReference_test.hh new file mode 100644 index 00000000..ac532bc8 --- /dev/null +++ b/trick_source/sim_services/VariableServer/test/VariableReference_test.hh @@ -0,0 +1,56 @@ +/******************************TRICK HEADER************************************* +PURPOSE: ( Common definitions for testing the VariableReference class ) +*******************************************************************************/ + +#include +#include +#include +#include +#include + +#include "trick/MemoryManager.hh" +#include "trick/VariableReference.hh" +#include "trick/UdUnits.hh" + +#include + +class NestedObject { + public: + int a; + int b; +}; + +class TestObject { + public: + TestObject() {} + + int length; /* m xy-position */ + double a; /* s time */ + long b; /* nautical_mile miles */ + long long c; /* in inches */ + unsigned long d; /* light_year miles */ + unsigned long long e; /* mi miles */ + int error_units; /* asvdsfkj fake units */ + wchar_t * wchar_str; + std::vector vec; + + NestedObject obj; +}; + +/* + Test Fixture. + */ +class VariableReference_test : public ::testing::Test { + protected: + Trick::MemoryManager *memmgr; + Trick::UdUnits * udunits; + VariableReference_test() { + memmgr = new Trick::MemoryManager; + udunits = new Trick::UdUnits; + + udunits->read_default_xml(); + } + ~VariableReference_test() { delete memmgr; } + void SetUp() {} + void TearDown() {} +}; \ No newline at end of file diff --git a/trick_source/sim_services/VariableServer/test/VariableReference_writeValueAscii_test.cc b/trick_source/sim_services/VariableServer/test/VariableReference_writeValueAscii_test.cc new file mode 100644 index 00000000..814e54b8 --- /dev/null +++ b/trick_source/sim_services/VariableServer/test/VariableReference_writeValueAscii_test.cc @@ -0,0 +1,252 @@ +#include "VariableReference_test.hh" + + +TEST_F(VariableReference_test, writeValueAscii_int) { + // ARRANGE + // Create a variable to make a reference for + int test_a = 5; + (void) memmgr->declare_extern_var(&test_a, "int test_a"); + Trick::VariableReference ref("test_a"); + std::stringstream ss; + + // ACT + ref.stageValue(); + ref.prepareForWrite(); + ref.writeValueAscii(ss); + + // ASSERT + EXPECT_EQ(ss.str(), "5"); +} + +TEST_F(VariableReference_test, writeValueAscii_int_arr) { + // ARRANGE + // Create a variable to make a reference for + int test_a[5] = {1, 2, 3, 4, 5}; + (void) memmgr->declare_extern_var(&test_a, "int test_a[5]"); + Trick::VariableReference ref("test_a"); + std::stringstream ss; + + // ACT + ref.stageValue(); + ref.prepareForWrite(); + ref.writeValueAscii(ss); + + // ASSERT + EXPECT_EQ(ss.str(), "1,2,3,4,5"); +} + +TEST_F(VariableReference_test, writeValueAscii_double) { + // ARRANGE + // Create a variable to make a reference for + double test_a = 867.309; + (void) memmgr->declare_extern_var(&test_a, "double test_a"); + Trick::VariableReference ref_a("test_a"); + std::stringstream ss_a; + + double test_b = std::numeric_limits::max(); + (void) memmgr->declare_extern_var(&test_b, "double test_b"); + Trick::VariableReference ref_b("test_b"); + std::stringstream ss_b; + + double test_c = std::numeric_limits::min(); + (void) memmgr->declare_extern_var(&test_c, "double test_c"); + Trick::VariableReference ref_c("test_c"); + std::stringstream ss_c; + + // ACT + ref_a.stageValue(); + ref_a.prepareForWrite(); + ref_a.writeValueAscii(ss_a); + + ref_b.stageValue(); + ref_b.prepareForWrite(); + ref_b.writeValueAscii(ss_b); + + ref_c.stageValue(); + ref_c.prepareForWrite(); + ref_c.writeValueAscii(ss_c); + + // ASSERT + EXPECT_EQ(ss_a.str(), "867.309"); + EXPECT_EQ(ss_b.str(), "1.797693134862316e+308"); + EXPECT_EQ(ss_c.str(), "2.225073858507201e-308"); +} + +TEST_F(VariableReference_test, writeValueAscii_char) { + // ARRANGE + // Create a variable to make a reference for + char test_a = 'j'; + (void) memmgr->declare_extern_var(&test_a, "char test_a"); + Trick::VariableReference ref_a("test_a"); + std::stringstream ssa; + + char test_b[7] = "jackie"; + (void) memmgr->declare_extern_var(&test_b, "char test_b[7]"); + Trick::VariableReference ref_b("test_b"); + std::stringstream ssb; + + // ACT + ref_a.stageValue(); + ref_b.stageValue(); + + ref_a.prepareForWrite(); + ref_b.prepareForWrite(); + + ref_a.writeValueAscii(ssa); + ref_b.writeValueAscii(ssb); + + + // ASSERT + EXPECT_EQ(ssa.str(), "106"); + EXPECT_EQ(ssb.str(), "jackie"); +} + + +TEST_F(VariableReference_test, writeValueAscii_unsigned_char) { + // ARRANGE + // Create a variable to make a reference for + unsigned char test_a = 'j'; + (void) memmgr->declare_extern_var(&test_a, "unsigned char test_a"); + Trick::VariableReference ref_a("test_a"); + std::stringstream ssa; + + unsigned char test_b[8] = "jackie\n"; + (void) memmgr->declare_extern_var(&test_b, "unsigned char test_b[8]"); + Trick::VariableReference ref_b("test_b"); + std::stringstream ssb; + + // ACT + ref_a.stageValue(); + ref_b.stageValue(); + + ref_a.prepareForWrite(); + ref_b.prepareForWrite(); + + ref_a.writeValueAscii(ssa); + ref_b.writeValueAscii(ssb); + + // ASSERT + EXPECT_EQ(ssa.str(), "106"); + EXPECT_EQ(ssb.str(), "jackie\\n"); +} + + +TEST_F(VariableReference_test, writeValueAscii_wide_char) { + // ARRANGE + // Create a variable to make a reference for + wchar_t test_a = L'J'; + (void) memmgr->declare_extern_var(&test_a, "wchar_t test_a"); + Trick::VariableReference ref_a("test_a"); + std::stringstream ssa; + + wchar_t test_b[15] = L"jackiebutwider"; + (void) memmgr->declare_extern_var(&test_b, "wchar_t test_b[15]"); + Trick::VariableReference ref_b("test_b"); + std::stringstream ssb; + + // ACT + ref_a.stageValue(); + ref_b.stageValue(); + + ref_a.prepareForWrite(); + ref_b.prepareForWrite(); + + ref_a.writeValueAscii(ssa); + ref_b.writeValueAscii(ssb); + + // ASSERT + // Original variable server behavior prints individual wchar as its ascii value + EXPECT_EQ(ssa.str(), "74"); + EXPECT_EQ(ssb.str(), "jackiebutwider"); +} + +TEST_F(VariableReference_test, DISABLED_writeValueAscii_wide_char_unconstrained) { + TestObject obj; + obj.wchar_str = (wchar_t *) malloc (sizeof(wchar_t) * 7); + for (int i = 0; i < 6; i++) { + obj.wchar_str[i] = L'j'; + } + obj.wchar_str[6] = L'\0'; + (void) memmgr->declare_extern_var(&obj, "TestObject obj"); + // (void) memmgr->declare_extern_var(&obj.wchar_str, "wchar_t * obj.wchar_str"); + + Trick::VariableReference ref("obj.wchar_str"); + std::stringstream ss; + + // ACT + ref.stageValue(); + ref.prepareForWrite(); + ref.writeValueAscii(ss); + + // ASSERT + EXPECT_EQ(ss.str(), "jjjjjj"); + +} + +TEST_F(VariableReference_test, writeValueAscii_std_string) { + // ARRANGE + // Create a variable to make a reference for + std::string test_a = "jackiebutstringy"; + (void) memmgr->declare_extern_var(&test_a, "std::string test_a"); + Trick::VariableReference ref("test_a"); + std::stringstream ss; + + // ACT + ref.stageValue(); + ref.prepareForWrite(); + ref.writeValueAscii(ss); + + // ASSERT + EXPECT_EQ(ss.str(), "jackiebutstringy"); +} + +TEST_F(VariableReference_test, writeValueAscii_escape_characters) { + // ARRANGE + // Create a variable to make a reference for + std::string test_a = "\n\t\b\a\"\f\r\v"; + (void) memmgr->declare_extern_var(&test_a, "std::string test_a"); + Trick::VariableReference ref("test_a"); + std::stringstream ss; + + // ACT + ref.stageValue(); + ref.prepareForWrite(); + ref.writeValueAscii(ss); + + // ASSERT + EXPECT_EQ(ss.str(), "\\n\\t\\b\\a\"\\f\\n\\v"); +} + +TEST_F(VariableReference_test, writeValueAscii_boolean) { + // ARRANGE + // Create a variable to make a reference for + bool test_a = true; + (void) memmgr->declare_extern_var(&test_a, "bool test_a"); + Trick::VariableReference ref("test_a"); + std::stringstream ss; + + // ACT + ref.stageValue(); + ref.prepareForWrite(); + ref.writeValueAscii(ss); + + // ASSERT + EXPECT_EQ(ss.str(), "1"); +} + +TEST_F(VariableReference_test, writeValueAscii_short) { + // ARRANGE + // Create a variable to make a reference for + short test_a = 255; + (void) memmgr->declare_extern_var(&test_a, "short test_a"); + Trick::VariableReference ref("test_a"); + std::stringstream ss; + + // ACT + ref.stageValue(); + ref.prepareForWrite(); + ref.writeValueAscii(ss); + + // ASSERT + EXPECT_EQ(ss.str(), "255"); +} \ No newline at end of file diff --git a/trick_source/sim_services/VariableServer/test/VariableReference_writeValueBinary_test.cc b/trick_source/sim_services/VariableServer/test/VariableReference_writeValueBinary_test.cc new file mode 100644 index 00000000..4ae9a5e5 --- /dev/null +++ b/trick_source/sim_services/VariableServer/test/VariableReference_writeValueBinary_test.cc @@ -0,0 +1,201 @@ +#include "VariableReference_test.hh" + + +TEST_F(VariableReference_test, writeValueBinary_int) { + // ARRANGE + // Create a variable to make a reference for + int test_a = 4095; + (void) memmgr->declare_extern_var(&test_a, "int test_a"); + Trick::VariableReference ref("test_a"); + std::stringstream ss; + + // ACT + ref.stageValue(); + ref.prepareForWrite(); + ref.writeValueBinary(ss); + + + unsigned char expected_bytes[4] = {0xFF, 0x0F, 0x00, 0x00}; + char * actual_bytes = (char *) malloc (sizeof(int)); + + ss.read(actual_bytes, 4); + + // ASSERT + for (int i = 0; i < 4; i++) { + EXPECT_EQ(static_cast(actual_bytes[i]), expected_bytes[i]); + } +} + +TEST_F(VariableReference_test, writeValueBinary_float) { + // ARRANGE + // Create a variable to make a reference for + float test_a = 40.95; + (void) memmgr->declare_extern_var(&test_a, "float test_a"); + Trick::VariableReference ref("test_a"); + std::stringstream ss; + + // ACT + ref.stageValue(); + ref.prepareForWrite(); + ref.writeValueBinary(ss); + + unsigned char expected_bytes[4] = {0xcd, 0xcc, 0x23, 0x42}; + + char * actual_bytes = (char *) malloc (sizeof(float)); + + ss.read(actual_bytes, 4); + + // ASSERT + for (int i = 0; i < 4; i++) { + EXPECT_EQ(static_cast(actual_bytes[i]), expected_bytes[i]); + } +} + +TEST_F(VariableReference_test, writeValueBinary_int_arr) { + // ARRANGE + // Create a variable to make a reference for + int test_a[3] = {1, 2, 3}; + (void) memmgr->declare_extern_var(&test_a, "int test_a[3]"); + Trick::VariableReference ref("test_a"); + std::stringstream ss; + + // ACT + ref.stageValue(); + ref.prepareForWrite(); + ref.writeValueBinary(ss); + + // ASSERT + char * actual_bytes = (char *) malloc (sizeof(int )* 3); + ss.read(actual_bytes, sizeof(int) * 3); + unsigned char expected_bytes[12] = {0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00}; + + + // ASSERT + for (int i = 0; i < 12; i++) { + EXPECT_EQ(static_cast(actual_bytes[i]), expected_bytes[i]); + } +} + + +TEST_F(VariableReference_test, writeValueBinary_double_arr) { + // ARRANGE + // Create a variable to make a reference for + double test_a[3] = {1.0, 10.0, 100.0}; + (void) memmgr->declare_extern_var(&test_a, "double test_a[3]"); + Trick::VariableReference ref("test_a"); + std::stringstream ss; + + // ACT + ref.stageValue(); + ref.prepareForWrite(); + ref.writeValueBinary(ss); + + // ASSERT + char * actual_bytes = (char *) malloc (sizeof(double )* 3); + ss.read(actual_bytes, sizeof(double) * 3); + unsigned char expected_bytes[24] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x40}; + + // ASSERT + for (int i = 0; i < 12; i++) { + EXPECT_EQ(static_cast(actual_bytes[i]), expected_bytes[i]); + } +} + + +TEST_F(VariableReference_test, writeValueBinary_string) { + // ARRANGE + // Create a variable to make a reference for + std::string test_a = "abcdef"; + (void) memmgr->declare_extern_var(&test_a, "std::string test_a"); + Trick::VariableReference ref("test_a"); + std::stringstream ss; + + // ACT + ref.stageValue(); + ref.prepareForWrite(); + ref.writeValueBinary(ss); + + // ASSERT + char * actual_bytes = (char *) malloc (test_a.length()); + ss.read(actual_bytes, test_a.length()); + unsigned char expected_bytes[6] = {0x61, 0x62, 0x63, 0x64, 0x65, 0x66}; + + // ASSERT + for (unsigned int i = 0; i < test_a.length(); i++) { + EXPECT_EQ(static_cast(actual_bytes[i]), expected_bytes[i]); + } +} + +TEST_F(VariableReference_test, writeNameBinary) { + // ARRANGE + // Create a variable to make a reference for + std::string test_a = "abcdef"; + (void) memmgr->declare_extern_var(&test_a, "std::string test_a"); + Trick::VariableReference ref("test_a"); + std::stringstream ss; + + // ACT + ref.stageValue(); + ref.prepareForWrite(); + ref.writeNameBinary(ss); + + // ASSERT + char * actual_bytes = (char *) malloc (ref.getName().size()); + ss.read(actual_bytes, 6); + unsigned char expected_bytes[6] = {0x74, 0x65, 0x73, 0x74, 0x5f, 0x61}; + + // ASSERT + for (int i = 0; i < ref.getName().size(); i++) { + EXPECT_EQ(static_cast(actual_bytes[i]), expected_bytes[i]); + } +} + +TEST_F(VariableReference_test, writeNameLengthBinary) { + // ARRANGE + // Create a variable to make a reference for + std::string test_a = "abcdef"; + (void) memmgr->declare_extern_var(&test_a, "std::string test_a"); + Trick::VariableReference ref("test_a"); + std::stringstream ss; + + // ACT + ref.stageValue(); + ref.prepareForWrite(); + ref.writeNameLengthBinary(ss); + + // ASSERT + char * actual_bytes = (char *) malloc (sizeof(int)); + ss.read(actual_bytes, sizeof(int)); + unsigned char expected_bytes[sizeof(int)] = {0x06, 0x00, 0x00, 0x00}; + + // ASSERT + for (int i = 0; i < sizeof(int); i++) { + EXPECT_EQ(static_cast(actual_bytes[i]), expected_bytes[i]); + } +} + + +TEST_F(VariableReference_test, writeValueBinaryByteswap_int) { + // ARRANGE + // Create a variable to make a reference for + int test_a = 4095; + (void) memmgr->declare_extern_var(&test_a, "int test_a"); + Trick::VariableReference ref("test_a"); + std::stringstream ss; + + // ACT + ref.stageValue(); + ref.prepareForWrite(); + ref.writeValueBinary(ss, true); + + + unsigned char expected_bytes[4] = {0x00, 0x00, 0x0F, 0xFF}; + char * actual_bytes = (char *) malloc (sizeof(int)); + + ss.read(actual_bytes, 4); + + // ASSERT + for (int i = 0; i < 4; i++) { + EXPECT_EQ(static_cast(actual_bytes[i]), expected_bytes[i]); + } +} \ No newline at end of file diff --git a/trick_source/sim_services/VariableServer/test/VariableServerListenThread_test.cc b/trick_source/sim_services/VariableServer/test/VariableServerListenThread_test.cc new file mode 100644 index 00000000..3d934b71 --- /dev/null +++ b/trick_source/sim_services/VariableServer/test/VariableServerListenThread_test.cc @@ -0,0 +1,399 @@ +/******************************TRICK HEADER************************************* +PURPOSE: ( Tests for the VariableServerListenThread class ) +*******************************************************************************/ + +#include +#include + +#include "trick/CommandLineArguments.hh" +#include "trick/VariableServer.hh" + +#include "trick/message_type.h" + +#include "trick/Mock/MockExecutive.hh" +#include "trick/Mock/MockMessagePublisher.hh" + +#include "trick/Mock/MockTCPClientListener.hh" +#include "trick/Mock/MockTCPConnection.hh" +#include "trick/Mock/MockMulticastGroup.hh" + +#include "trick/VariableServerListenThread.hh" + + +using ::testing::Return; +using ::testing::_; +using ::testing::AtLeast; + + +/* + Test Fixture. + */ +class VariableServerListenThread_test : public ::testing::Test { + protected: + // Static global dependencies that I would like to eventually mock out + Trick::CommandLineArguments cmd_args; + Trick::VariableServer varserver; + + // Static global dependencies that I have mocked out + MockExecutive executive; + MockMessagePublisher message_publisher; + + MockTCPClientListener * listener; + MockMulticastGroup * mcast; + + VariableServerListenThread_test() { + // Set up dependencies that haven't been broken + Trick::VariableServerSessionThread::set_vs_ptr(&varserver); + + // Set up mocks + listener = new MockTCPClientListener; + mcast = new MockMulticastGroup; + } + + ~VariableServerListenThread_test() { + } + + void SetUp() {} + void TearDown() {} +}; + +void setup_normal_listener_expectations (MockTCPClientListener * listener) { + // Starting the connection succeeds + EXPECT_CALL(*listener, getHostname()) + .WillRepeatedly(Return("MyHostname")); + EXPECT_CALL(*listener, getPort()) + .WillRepeatedly(Return(1234)); +} + +void setup_normal_mcast_expectations (MockMulticastGroup * mcast) { + EXPECT_CALL(*mcast, initialize()) + .WillOnce(Return(0)); + + EXPECT_CALL(*mcast, isInitialized()) + .WillRepeatedly(Return(1)); + + EXPECT_CALL(*mcast, addAddress(_, _)) + .Times(2); + + EXPECT_CALL(*mcast, broadcast(_)) + .Times(AtLeast(1)); +} + +TEST_F(VariableServerListenThread_test, init_listen_device) { + // ARRANGE + setup_normal_listener_expectations(listener); + EXPECT_CALL(*listener, initialize()) + .WillOnce(Return(0)); + + Trick::VariableServerListenThread listen_thread (listener); + + // ACT + listen_thread.init_listen_device(); + + // ASSERT +} + +TEST_F(VariableServerListenThread_test, init_listen_device_fails) { + // ARRANGE + setup_normal_listener_expectations(listener); + EXPECT_CALL(*listener, initialize()) + .WillOnce(Return(-1)); + + Trick::VariableServerListenThread listen_thread (listener); + + // ACT + int status = listen_thread.init_listen_device(); + + // ASSERT + EXPECT_EQ(status, -1); +} + + + +TEST_F(VariableServerListenThread_test, get_hostname) { + // ARRANGE + setup_normal_listener_expectations(listener); + + Trick::VariableServerListenThread listen_thread (listener); + + // ACT + const char * hostname = listen_thread.get_hostname(); + + // ASSERT + EXPECT_STREQ(hostname, "MyHostname"); +} + +TEST_F(VariableServerListenThread_test, get_port) { + // ARRANGE + setup_normal_listener_expectations(listener); + + Trick::VariableServerListenThread listen_thread (listener); + + // ACT + int port = listen_thread.get_port(); + + // ASSERT + EXPECT_EQ(port, 1234); +} + +TEST_F(VariableServerListenThread_test, get_port_returns_requested) { + // ARRANGE + setup_normal_listener_expectations(listener); + + Trick::VariableServerListenThread listen_thread (listener); + + // ACT + listen_thread.set_port(4321); + int port = listen_thread.get_port(); + + // ASSERT + EXPECT_EQ(port, 4321); +} + +TEST_F(VariableServerListenThread_test, check_and_move_listen_device_init_fails) { + // ARRANGE + setup_normal_listener_expectations(listener); + Trick::VariableServerListenThread listen_thread (listener); + listen_thread.set_port(4321); + + EXPECT_CALL(*listener, disconnect()); + EXPECT_CALL(*listener, initialize(_, _)) + .WillOnce(Return(1)); + + EXPECT_CALL(message_publisher, publish(MSG_ERROR, _)); + + // ACT + int status = listen_thread.check_and_move_listen_device(); + + // ASSERT + EXPECT_EQ(status, -1); +} + +TEST_F(VariableServerListenThread_test, check_and_move_listen_device) { + // ARRANGE + setup_normal_listener_expectations(listener); + Trick::VariableServerListenThread listen_thread (listener); + listen_thread.set_port(4321); + + EXPECT_CALL(*listener, disconnect()); + EXPECT_CALL(*listener, initialize(_, _)) + .WillOnce(Return(0)); + + // ACT + int status = listen_thread.check_and_move_listen_device(); + + // ASSERT + EXPECT_EQ(status, 0); +} + +TEST_F(VariableServerListenThread_test, run_thread) { + // ARRANGE + setup_normal_listener_expectations(listener); + setup_normal_mcast_expectations(mcast); + + EXPECT_CALL(*listener, setBlockMode(true)) + .Times(1); + + EXPECT_CALL(*listener, checkForNewConnections()) + .WillRepeatedly(Return(false)); + + Trick::VariableServerListenThread listen_thread (listener); + listen_thread.set_multicast_group(mcast); + + // ACT + listen_thread.create_thread(); + + sleep(3); + + listen_thread.cancel_thread(); + listen_thread.join_thread(); + + // ASSERT +} + + +TEST_F(VariableServerListenThread_test, run_thread_no_broadcast) { + // ARRANGE + setup_normal_listener_expectations(listener); + + // Expect no calls to mcast + EXPECT_CALL (*mcast, initialize()) + .Times(0); + + EXPECT_CALL (*mcast, broadcast(_)) + .Times(0); + + EXPECT_CALL(*listener, setBlockMode(true)) + .Times(1); + + EXPECT_CALL(*listener, checkForNewConnections()) + .WillRepeatedly(Return(false)); + + Trick::VariableServerListenThread listen_thread (listener); + listen_thread.set_broadcast(false); + listen_thread.set_multicast_group(mcast); + + // ACT + listen_thread.create_thread(); + + sleep(3); + + listen_thread.cancel_thread(); + listen_thread.join_thread(); + + // ASSERT +} + + + +TEST_F(VariableServerListenThread_test, run_thread_turn_on_broadcast) { + // ARRANGE + setup_normal_listener_expectations(listener); + setup_normal_mcast_expectations(mcast); + + EXPECT_CALL(*listener, setBlockMode(true)) + .Times(1); + + EXPECT_CALL(*listener, checkForNewConnections()) + .WillRepeatedly(Return(false)); + + EXPECT_CALL(*mcast, isInitialized()) + .WillOnce(Return(0)) + .WillRepeatedly(Return(1)); + + Trick::VariableServerListenThread listen_thread (listener); + listen_thread.set_broadcast(false); + listen_thread.set_multicast_group(mcast); + + // ACT + listen_thread.create_thread(); + + EXPECT_EQ(listen_thread.get_broadcast(), false); + + sleep(3); + listen_thread.set_broadcast(true); + sleep(3); + EXPECT_EQ(listen_thread.get_broadcast(), true); + + + listen_thread.cancel_thread(); + listen_thread.join_thread(); + + // ASSERT +} + +TEST_F(VariableServerListenThread_test, accept_connection) { + // ARRANGE + setup_normal_listener_expectations(listener); + + // Expect no calls to mcast + EXPECT_CALL (*mcast, initialize()) + .Times(0); + + EXPECT_CALL (*mcast, broadcast(_)) + .Times(0); + + EXPECT_CALL(*listener, setBlockMode(true)) + .Times(1); + + EXPECT_CALL(*listener, checkForNewConnections()) + .WillOnce(Return(true)) + .WillRepeatedly(Return(false)); + + MockTCPConnection connection; + EXPECT_CALL(connection, start()) + .WillOnce(Return(0)); + + EXPECT_CALL(connection, read(_, _)) + .WillOnce(Return(-1)); + + EXPECT_CALL(connection, disconnect()); + + EXPECT_CALL(*listener, setUpNewConnection()) + .WillOnce(Return(&connection)); + + Trick::VariableServerListenThread listen_thread (listener); + listen_thread.set_broadcast(false); + listen_thread.set_multicast_group(mcast); + + // ACT + listen_thread.create_thread(); + + std::stringstream stream; + listen_thread.dump(stream); + ASSERT_TRUE(stream.str().size() > 0); + + sleep(3); + + listen_thread.cancel_thread(); + listen_thread.join_thread(); + + // ASSERT +} + + +TEST_F(VariableServerListenThread_test, connection_fails) { + // ARRANGE + setup_normal_listener_expectations(listener); + + // Expect no calls to mcast + EXPECT_CALL (*mcast, initialize()) + .Times(0); + + EXPECT_CALL (*mcast, broadcast(_)) + .Times(0); + + EXPECT_CALL(*listener, setBlockMode(true)) + .Times(1); + + EXPECT_CALL(*listener, checkForNewConnections()) + .WillOnce(Return(true)) + .WillRepeatedly(Return(false)); + + MockTCPConnection connection; + EXPECT_CALL(connection, start()) + .WillOnce(Return(-1)); + + EXPECT_CALL(connection, disconnect()); + + EXPECT_CALL(*listener, setUpNewConnection()) + .WillOnce(Return(&connection)); + + Trick::VariableServerListenThread listen_thread (listener); + listen_thread.set_broadcast(false); + listen_thread.set_multicast_group(mcast); + + // ACT + listen_thread.create_thread(); + + sleep(3); + + listen_thread.cancel_thread(); + listen_thread.join_thread(); + + // ASSERT +} + + +TEST_F(VariableServerListenThread_test, restart_fails) { + // ARRANGE + + setup_normal_listener_expectations(listener); + Trick::VariableServerListenThread listen_thread (listener); + + EXPECT_CALL(*listener, restart()); + EXPECT_CALL(*listener, validateSourceAddress(_)) + .WillOnce(Return(false)); + + EXPECT_CALL(*listener, initialize(_, _)) + .WillOnce(Return(-1)); + + EXPECT_CALL(*listener, disconnect()); + + EXPECT_CALL(message_publisher, publish(MSG_ERROR, _)); + EXPECT_CALL(message_publisher, publish(MSG_INFO, _)); + + // ACT + // ASSERT + EXPECT_EQ(listen_thread.restart(), -1); +} \ No newline at end of file diff --git a/trick_source/sim_services/VariableServer/test/VariableServerSessionThread_test.cc b/trick_source/sim_services/VariableServer/test/VariableServerSessionThread_test.cc new file mode 100644 index 00000000..9150f8a5 --- /dev/null +++ b/trick_source/sim_services/VariableServer/test/VariableServerSessionThread_test.cc @@ -0,0 +1,344 @@ +/******************************TRICK HEADER************************************* +PURPOSE: ( Tests for the VariableServerSessionThread class ) +*******************************************************************************/ + +#include +#include +#include + +#include "trick/VariableServer.hh" +#include "trick/ExecutiveException.hh" +#include "trick/message_type.h" + +#include "trick/VariableServerSessionThread.hh" + +#include "trick/Mock/MockMessagePublisher.hh" +#include "trick/Mock/MockVariableServerSession.hh" +#include "trick/Mock/MockClientConnection.hh" + +using ::testing::Return; +using ::testing::_; +using ::testing::AtLeast; +using ::testing::DoAll; +using ::testing::Throw; +using ::testing::Const; +using ::testing::NiceMock; + + +// Set up default mock behavior + +void setup_default_connection_mocks (MockClientConnection * connection) { + // Starting the connection succeeds + ON_CALL(*connection, start()) + .WillByDefault(Return(0)); + + // We should always get a disconnect call + ON_CALL(*connection, disconnect()) + .WillByDefault(Return(0)); +} + +void setup_default_session_mocks (MockVariableServerSession * session, bool command_exit = true) { + ON_CALL(*session, handle_message()) + .WillByDefault(Return(0)); + + ON_CALL(*session, copy_and_write_async()) + .WillByDefault(Return(0)); + + ON_CALL(*session, get_pause()) + .WillByDefault(Return(false)); + + ON_CALL(Const(*session), get_update_rate()) + .WillByDefault(Return(0.001)); + + ON_CALL(*session, get_exit_cmd()) + .WillByDefault(Return(false)); +} + +/* + Test Fixture. + */ +class VariableServerSessionThread_test : public ::testing::Test { + protected: + Trick::VariableServer * varserver; + + MockClientConnection connection; + NiceMock * session; + + MockMessagePublisher message_publisher; + + VariableServerSessionThread_test() { + // Set up dependencies that haven't been broken + varserver = new Trick::VariableServer; + Trick::VariableServerSessionThread::set_vs_ptr(varserver); + + // Set up mocks + session = new NiceMock; + setup_default_connection_mocks(&connection); + setup_default_session_mocks(session); + } + + ~VariableServerSessionThread_test() { + delete varserver; + } + + void SetUp() {} + void TearDown() {} +}; + + +// Helper functions for common cases of Mock expectations + +void setup_normal_connection_expectations (MockClientConnection * connection) { + // Starting the connection succeeds + EXPECT_CALL(*connection, start()) + .Times(1) + .WillOnce(Return(0)); + + // We should always get a disconnect call + EXPECT_CALL(*connection, disconnect()) + .Times(1); +} + +void set_session_exit_after_some_loops(MockVariableServerSession * session) { + EXPECT_CALL(*session, get_exit_cmd()) + .WillOnce(Return(false)) + .WillOnce(Return(false)) + .WillOnce(Return(false)) + .WillOnce(Return(false)) + .WillOnce(Return(false)) + .WillOnce(Return(true)); +} + +TEST_F(VariableServerSessionThread_test, connection_failure) { + // ARRANGE + + // Starting the connection fails + EXPECT_CALL(connection, start()) + .Times(1) + .WillOnce(Return(1)); + + // Set up VariableServerSessionThread + Trick::VariableServerSessionThread * vst = new Trick::VariableServerSessionThread(session) ; + vst->set_connection(&connection); + + // ACT + vst->create_thread(); + pthread_t id = vst->get_pthread_id(); + Trick::ConnectionStatus status = vst->wait_for_accept(); + vst->join_thread(); + + // ASSERT + EXPECT_EQ(status, Trick::ConnectionStatus::CONNECTION_FAIL); + + // There should be nothing in the VariableServer's thread list + EXPECT_EQ(varserver->get_vst(id), (Trick::VariableServerSessionThread *) NULL); + EXPECT_EQ(varserver->get_session(id), (Trick::VariableServerSession *) NULL); + + delete session; +} + + +TEST_F(VariableServerSessionThread_test, exit_if_handle_message_fails) { + // ARRANGE + setup_normal_connection_expectations(&connection); + + // Handle a message, but it fails + EXPECT_CALL(*session, handle_message()) + .Times(1) + .WillOnce(Return(-1)); + + + // Set up VariableServerSessionThread + Trick::VariableServerSessionThread * vst = new Trick::VariableServerSessionThread(session) ; + vst->set_connection(&connection); + + // ACT + vst->create_thread(); + pthread_t id = vst->get_pthread_id(); + Trick::ConnectionStatus status = vst->wait_for_accept(); + ASSERT_EQ(status, Trick::ConnectionStatus::CONNECTION_SUCCESS); + + vst->join_thread(); + + // ASSERT + + // There should be nothing in the VariableServer's thread list + EXPECT_EQ(varserver->get_vst(id), (Trick::VariableServerSessionThread *) NULL); + EXPECT_EQ(varserver->get_session(id), (Trick::VariableServerSession *) NULL); +} + + +TEST_F(VariableServerSessionThread_test, exit_if_write_fails) { + // ARRANGE + setup_normal_connection_expectations(&connection); + + // Write out data + EXPECT_CALL(*session, copy_and_write_async()) + .WillOnce(Return(-1)); + + + // Set up VariableServerSessionThread + Trick::VariableServerSessionThread * vst = new Trick::VariableServerSessionThread(session) ; + vst->set_connection(&connection); + + // ACT + vst->create_thread(); + pthread_t id = vst->get_pthread_id(); + Trick::ConnectionStatus status = vst->wait_for_accept(); + ASSERT_EQ(status, Trick::ConnectionStatus::CONNECTION_SUCCESS); + + vst->join_thread(); + + // ASSERT + + // There should be nothing in the VariableServer's thread list + EXPECT_EQ(varserver->get_vst(id), (Trick::VariableServerSessionThread *) NULL); + EXPECT_EQ(varserver->get_session(id), (Trick::VariableServerSession *) NULL); +} + +TEST_F(VariableServerSessionThread_test, exit_commanded) { + // ARRANGE + setup_normal_connection_expectations(&connection); + set_session_exit_after_some_loops(session); + + // Set up VariableServerSessionThread + Trick::VariableServerSessionThread * vst = new Trick::VariableServerSessionThread(session) ; + vst->set_connection(&connection); + + // ACT + vst->create_thread(); + pthread_t id = vst->get_pthread_id(); + Trick::ConnectionStatus status = vst->wait_for_accept(); + ASSERT_EQ(status, Trick::ConnectionStatus::CONNECTION_SUCCESS); + + // Runs for a few loops, then exits + + // Thread should shut down + vst->join_thread(); + + // ASSERT + // There should be nothing in the VariableServer's thread list + EXPECT_EQ(varserver->get_vst(id), (Trick::VariableServerSessionThread *) NULL); + EXPECT_EQ(varserver->get_session(id), (Trick::VariableServerSession *) NULL); +} + +TEST_F(VariableServerSessionThread_test, thread_cancelled) { + // ARRANGE + setup_normal_connection_expectations(&connection); + + // Set up VariableServerSessionThread + Trick::VariableServerSessionThread * vst = new Trick::VariableServerSessionThread(session) ; + vst->set_connection(&connection); + vst->create_thread(); + pthread_t id = vst->get_pthread_id(); + Trick::ConnectionStatus status = vst->wait_for_accept(); + ASSERT_EQ(status, Trick::ConnectionStatus::CONNECTION_SUCCESS); + + // Confirm that the session has been created + Trick::VariableServerSession * vs_session = varserver->get_session(id); + ASSERT_TRUE(vs_session == session); + + // ACT + vst->cancel_thread(); + + // Thread should shut down + vst->join_thread(); + + // ASSERT + // There should be nothing in the VariableServer's thread list + EXPECT_EQ(varserver->get_vst(id), (Trick::VariableServerSessionThread *) NULL); + EXPECT_EQ(varserver->get_session(id), (Trick::VariableServerSession *) NULL); +} + + +TEST_F(VariableServerSessionThread_test, turn_session_log_on) { + // ARRANGE + setup_normal_connection_expectations(&connection); + set_session_exit_after_some_loops(session); + + varserver->set_var_server_log_on(); + + // We expect a the session's log to be turned on + EXPECT_CALL(*session, set_log(true)) + .Times(1); + + // Set up VariableServerSessionThread + Trick::VariableServerSessionThread * vst = new Trick::VariableServerSessionThread(session) ; + vst->set_connection(&connection); + + // ACT + vst->create_thread(); + pthread_t id = vst->get_pthread_id(); + Trick::ConnectionStatus status = vst->wait_for_accept(); + ASSERT_EQ(status, Trick::ConnectionStatus::CONNECTION_SUCCESS); + + // Thread should shut down + vst->join_thread(); + + // ASSERT + // There should be nothing in the VariableServer's thread list + EXPECT_EQ(varserver->get_vst(id), (Trick::VariableServerSessionThread *) NULL); + EXPECT_EQ(varserver->get_session(id), (Trick::VariableServerSession *) NULL); +} + +TEST_F(VariableServerSessionThread_test, throw_trick_executive_exception) { + // ARRANGE + setup_normal_connection_expectations(&connection); + + EXPECT_CALL(*session, get_exit_cmd()) + .WillRepeatedly(Return(false)); + + // Set up VariableServerSessionThread + Trick::VariableServerSessionThread * vst = new Trick::VariableServerSessionThread(session) ; + vst->set_connection(&connection); + + EXPECT_CALL(*session, handle_message()) + .WillOnce(Throw(Trick::ExecutiveException(-1, __FILE__, __LINE__, "Trick::ExecutiveException Error message for testing"))); + + EXPECT_CALL(message_publisher, publish(MSG_ERROR, _)); + + // ACT + vst->create_thread(); + pthread_t id = vst->get_pthread_id(); + Trick::ConnectionStatus status = vst->wait_for_accept(); + ASSERT_EQ(status, Trick::ConnectionStatus::CONNECTION_SUCCESS); + + // Thread should shut down + vst->join_thread(); + + // ASSERT + // There should be nothing in the VariableServer's thread list + EXPECT_EQ(varserver->get_vst(id), (Trick::VariableServerSessionThread *) NULL); + EXPECT_EQ(varserver->get_session(id), (Trick::VariableServerSession *) NULL); +} + +TEST_F(VariableServerSessionThread_test, throw_exception) { + // ARRANGE + setup_normal_connection_expectations(&connection); + + EXPECT_CALL(*session, get_exit_cmd()) + .WillRepeatedly(Return(false)); + + // Set up VariableServerSessionThread + Trick::VariableServerSessionThread * vst = new Trick::VariableServerSessionThread(session) ; + vst->set_connection(&connection); + + EXPECT_CALL(*session, handle_message()) + .WillOnce(Throw(std::logic_error("Error message for testing"))); + EXPECT_CALL(message_publisher, publish(MSG_ERROR, _)); + + + // ACT + vst->create_thread(); + pthread_t id = vst->get_pthread_id(); + Trick::ConnectionStatus status = vst->wait_for_accept(); + ASSERT_EQ(status, Trick::ConnectionStatus::CONNECTION_SUCCESS); + + // Thread should shut down + vst->join_thread(); + + // ASSERT + // There should be nothing in the VariableServer's thread list + EXPECT_EQ(varserver->get_vst(id), (Trick::VariableServerSessionThread *) NULL); + EXPECT_EQ(varserver->get_session(id), (Trick::VariableServerSession *) NULL); +} \ No newline at end of file diff --git a/trick_source/sim_services/VariableServer/test/VariableServerSession_test.cc b/trick_source/sim_services/VariableServer/test/VariableServerSession_test.cc new file mode 100644 index 00000000..103ec112 --- /dev/null +++ b/trick_source/sim_services/VariableServer/test/VariableServerSession_test.cc @@ -0,0 +1,1397 @@ +/******************************TRICK HEADER************************************* +PURPOSE: ( Tests for the VariableServerSession class ) +*******************************************************************************/ + +#include +#include + +#include +#include +#include +#include + +#include "trick/MemoryManager.hh" +#include "trick/UdUnits.hh" + + + +#include "trick/message_type.h" +#include "trick/VariableServerSession.hh" +#include "trick/var_binary_parser.hh" + +#include "trick/Mock/MockExecutive.hh" +#include "trick/Mock/MockRealtimeSync.hh" +#include "trick/Mock/MockMessagePublisher.hh" +#include "trick/Mock/MockMessageCustomManager.hh" +#include "trick/Mock/MockInputProcessor.hh" +#include "trick/Mock/MockClientConnection.hh" +#include "trick/Mock/MockVariableServerSession.hh" + + + +using ::testing::AtLeast; +using ::testing::_; +using ::testing::Truly; +using ::testing::Args; +using ::testing::Return; +using ::testing::Invoke; +using ::testing::DoAll; +using ::testing::SetArgReferee; + + + +/* + Test Fixture. + */ +class VariableServerSession_test : public ::testing::Test { + protected: + // MemoryManager isn't actually a dependency, VariableReference is + // But using the memory manage is much easier than mocking VariableReference all the way + // Which is bad design @me + // shame + Trick::MemoryManager memmgr; + + // Dependencies + MockRealtimeSync realtime_sync; + MockExecutive executive; + MockMessagePublisher message_publisher; + MockMessageCustomManager message_custom; + MockInputProcessor input_processor; + + MockClientConnection connection; + + VariableServerSession_test() {} + + ~VariableServerSession_test() {} + + void SetUp() {} + void TearDown() {} +}; + +TEST_F(VariableServerSession_test, toString) { + + int a = 5; + double b = 6; + std::string c = "Hello"; + (void) memmgr.declare_extern_var(&a, "int a"); + (void) memmgr.declare_extern_var(&b, "double b"); + (void) memmgr.declare_extern_var(&c, "std::string c"); + + Trick::VariableServerSession session; + + session.var_add("a"); + session.var_add("b"); + session.var_add("c"); + session.var_binary(); + + std::stringstream ss; + ss << session; + + std::string expected = " \"format\":\"BINARY\",\n \"update_rate\":0.1,\n \"variables\":[\n \"a\",\n \"b\",\n \"c\"\n ]\n"; + EXPECT_EQ(ss.str(), expected); + + session.var_ascii(); + expected = " \"format\":\"ASCII\",\n \"update_rate\":0.1,\n \"variables\":[\n \"a\",\n \"b\",\n \"c\"\n ]\n"; + ss.str(""); + ss << session; + EXPECT_EQ(ss.str(), expected); +} + +TEST_F(VariableServerSession_test, var_sync) { + // ARRANGE + Trick::VariableServerSession session; + + // ACT + session.var_sync(0); + // ASSERT + ASSERT_EQ(session.get_copy_mode(), VS_COPY_ASYNC); + ASSERT_EQ(session.get_write_mode(), VS_WRITE_ASYNC); + + + // ACT + session.var_sync(1); + // ASSERT + ASSERT_EQ(session.get_copy_mode(), VS_COPY_SCHEDULED); + ASSERT_EQ(session.get_write_mode(), VS_WRITE_ASYNC); + + // ACT + session.var_sync(2); + // ASSERT + ASSERT_EQ(session.get_copy_mode(), VS_COPY_SCHEDULED); + ASSERT_EQ(session.get_write_mode(), VS_WRITE_WHEN_COPIED); +} + + +TEST_F(VariableServerSession_test, large_message_ascii) { + // ARRANGE + Trick::VariableServerSession session; + session.set_connection(&connection); + + const static int big_arr_size = 4000; + // Make an array too big to fit in a single message + + int big_arr[big_arr_size]; + for (int i = 0; i < big_arr_size; i++) { + big_arr[i] = i; + } + + // Set it up with the memory manager + (void) memmgr.declare_extern_var(&big_arr, "int big_arr[4000]"); + + // Create references for all of them + std::vector vars; + for (int i = 0; i < big_arr_size; i++) { + std::string var_name = "big_arr[" + std::to_string(i) + "]"; + Trick::VariableReference * var = new Trick::VariableReference(var_name); + var->stageValue(); + vars.push_back(var); + } + + // Make a matcher for the Mock + // This will check that the messages that are passed into connection.write() are correct + int val_counter = 0; + int message_counter = 0; + int final_val = 0; + auto constructedCorrectly = [&] (std::string s) -> bool { + std::stringstream ss(s); + std::string token; + + if (message_counter == 0) { + // First val in first message should be the message type, 0 + std::getline(ss, token, '\t'); + int message_type = stoi(token); + if (message_type != 0) return false; + } else { + // First val in all other message should be (last val in previous message)+1 + std::getline(ss, token, '\t'); + int first_num = stoi(token); + if (first_num != final_val+1) return false; + val_counter++; + } + + int num = 0; + + // The rest of the message should just be sequential + while (std::getline(ss, token, '\t')) { + if (token == "\n") { break; } + + num = stoi(token); + if (val_counter != num) return false; + val_counter++; + } + + // Save the final value so that we can verify the next message starts correctly + final_val = num; + message_counter++; + + return true; + }; + + // Set up the mock connection - it should make 3 calls to write + // The constructedCorrectly matcher will ensure that the values passed in are what we want them to be + EXPECT_CALL(connection, write(Truly(constructedCorrectly))) + .Times(3); + + // ACT + session.write_data(vars, (VS_MESSAGE_TYPE) 0); + + // ASSERT + // Make sure that the entire message was written out + ASSERT_EQ(val_counter, big_arr_size); +} + +TEST_F(VariableServerSession_test, large_message_binary) { + // ARRANGE + Trick::VariableServerSession session; + session.set_connection(&connection); + session.var_binary(); + + const static int big_arr_size = 4000; + // Make an array too big to fit in a single message + + int big_arr[big_arr_size]; + for (int i = 0; i < big_arr_size; i++) { + big_arr[i] = i; + } + + // Set it up with the memory manager + (void) memmgr.declare_extern_var(&big_arr, "int big_arr[4000]"); + + // Create references for all of them + std::vector vars; + for (int i = 0; i < big_arr_size; i++) { + std::string var_name = "big_arr[" + std::to_string(i) + "]"; + Trick::VariableReference * var = new Trick::VariableReference(var_name); + var->stageValue(); + vars.push_back(var); + } + + // Create a matcher that parses and collects the arguments into a ParsedBinaryMessage + ParsedBinaryMessage full_message; + auto binaryConstructedCorrectly = [&] (std::tuple msg_tuple) -> bool { + char * message; + int size; + std::tie(message, size) = msg_tuple; + ParsedBinaryMessage partial_message; + + // Put the message into a vector the parser + std::vector bytes; + for (int i = 0; i < size; i++) { + bytes.push_back(message[i]); + } + + // Parse and add it to the full message + try { + partial_message.parse(bytes); + full_message.combine(partial_message); + } catch (const MalformedMessageException& ex) { + std::cout << "Parser failed with message: " << ex.what(); + return false; + } + + return true; + }; + + // Set up the mock connection - it should make a bunch of calls to write + // The constructedCorrectly matcher will ensure that the values passed in are what we want them to be + EXPECT_CALL(connection, write(_, _)).With(Args<0,1>(Truly(binaryConstructedCorrectly))).Times(AtLeast(3)); + + // ACT + session.write_data(vars, (VS_MESSAGE_TYPE) 0); + + // ASSERT + + // Make sure we got all of the values + ASSERT_EQ(full_message.getNumVars(), big_arr_size); + + // Make sure all of the values are correct and have names + for (int i = 0; i < big_arr_size; i++) { + try { + std::string var_name = "big_arr[" + std::to_string(i) + "]"; + Var variable = full_message.getVariable(var_name); + + EXPECT_EQ(variable.getValue(), i); + } catch (std::exception& ex) { + FAIL() << ex.what(); + } + } +} + +TEST_F(VariableServerSession_test, log_on) { + // ARRANGE + int fake_logstream = 200; + + Trick::VariableServerSession session; + session.set_connection(&connection); + + // Expect the per-session logfile to be opened + EXPECT_CALL(message_custom, open_custom_message_file(_, _, _)) + .WillOnce(Return(fake_logstream)); + + // Expect a write to the log via message_publish + EXPECT_CALL(message_publisher, publish(MSG_PLAYBACK,_)); + EXPECT_CALL(message_publisher, publish(fake_logstream,_)); + + // Just get whatever from the client + EXPECT_CALL(connection, read(_, _)) + .WillOnce(DoAll(SetArgReferee<0>("some_python_command"), Return(10))); + EXPECT_CALL(connection, getClientTag()) + .WillRepeatedly(Return("ClientTag")); + EXPECT_CALL(input_processor, parse(_)); + + // ACT + session.set_log(true); + session.set_session_log(true); + + session.handle_message(); + + // ASSERT +} + +TEST_F(VariableServerSession_test, no_log_by_default) { + // ARRANGE + Trick::VariableServerSession session; + session.set_connection(&connection); + + // Should not get any write to the log + EXPECT_CALL(message_publisher, publish(MSG_PLAYBACK,_)) + .Times(0); + // Just get whatever from the client + EXPECT_CALL(connection, read(_, _)) + .WillOnce(DoAll(SetArgReferee<0>("some_python_command"), Return(10))); + EXPECT_CALL(input_processor, parse(_)); + + // ACT + session.handle_message(); + // ASSERT +} + + +TEST_F(VariableServerSession_test, info_msg_on) { + // ARRANGE + Trick::VariableServerSession session; + session.set_connection(&connection); + + // Expect a write to info message_publish + EXPECT_CALL(message_publisher, publish(MSG_DEBUG,_)); + // Just get whatever from the client + EXPECT_CALL(connection, read(_, _)) + .WillOnce(DoAll(SetArgReferee<0>("some_python_command"), Return(10))); + EXPECT_CALL(connection, getClientTag()) + .WillOnce(Return("ClientTag")); + EXPECT_CALL(input_processor, parse(_)); + + + // ACT + session.set_info_message(true); + session.handle_message(); + + // ASSERT +} + +TEST_F(VariableServerSession_test, info_msg_off_by_default) { + // ARRANGE + Trick::VariableServerSession session; + session.set_connection(&connection); + + // Expect no write to info message_publish + EXPECT_CALL(message_publisher, publish(MSG_DEBUG,_)) + .Times(0); + // Just get whatever from the client + EXPECT_CALL(connection, read(_, _)) + .WillOnce(DoAll(SetArgReferee<0>("some_python_command"), Return(10))); + EXPECT_CALL(input_processor, parse(_)); + + // ACT + session.handle_message(); + + // ASSERT +} + +TEST_F(VariableServerSession_test, debug_on) { + // ARRANGE + Trick::VariableServerSession session; + session.set_connection(&connection); + + // Expect 2 writes to info message_publish + EXPECT_CALL(message_publisher, publish(MSG_DEBUG,_)) + .Times(2); + // Just get whatever from the client + EXPECT_CALL(connection, read(_, _)) + .WillOnce(DoAll(SetArgReferee<0>("some_python_command"), Return(10))); + EXPECT_CALL(input_processor, parse(_)); + EXPECT_CALL(connection, getClientTag()) + .WillRepeatedly(Return("ClientTag")); + + // ACT + session.var_debug(3); + session.handle_message(); + + // ASSERT +} + +/**************************************************************************/ +/* Mode tests */ +/**************************************************************************/ + +void setup_partial_session_mock(MockVariableServerSession& session) { + EXPECT_CALL(session, copy_and_write_async()) + .WillOnce(Invoke(&session, &MockVariableServerSession::copy_and_write_async_concrete)); +} + +void set_session_modes(MockVariableServerSession& session, VS_COPY_MODE copy_mode, VS_WRITE_MODE write_mode, bool pause) { + EXPECT_CALL(session, get_copy_mode()) + .WillRepeatedly(Return(copy_mode)); + EXPECT_CALL(session, get_write_mode()) + .WillRepeatedly(Return(write_mode)); + EXPECT_CALL(session, get_pause()) + .WillOnce(Return(pause)); +} + + +TEST_F(VariableServerSession_test, copy_async_disabled) { + // ARRANGE + MockVariableServerSession session; + setup_partial_session_mock(session); + + // set disabled + EXPECT_CALL(session, get_enabled()) + .WillOnce(Return(false)); + + // Nothing else should be called + EXPECT_CALL(session, copy_sim_data()) + .Times(0); + + EXPECT_CALL(session, write_data()) + .Times(0); + + session.copy_and_write_async(); +} + + +TEST_F(VariableServerSession_test, copy_async_copy_and_write) { + // ARRANGE + MockVariableServerSession session; + setup_partial_session_mock(session); + + // set enabled + EXPECT_CALL(session, get_enabled()) + .WillOnce(Return(true)); + + // Copy async, write when copied, not paused + set_session_modes(session, VS_COPY_ASYNC, VS_WRITE_ASYNC, false); + + // Copy and write should be called + EXPECT_CALL(session, copy_sim_data()) + .Times(1); + + EXPECT_CALL(session, write_data()) + .Times(1); + + // ACT + int result = session.copy_and_write_async(); + + // ASSERT + ASSERT_GE(result, 0); +} + +TEST_F(VariableServerSession_test, copy_async_copy_write_when_copied) { + // ARRANGE + MockVariableServerSession session; + setup_partial_session_mock(session); + + // set enabled + EXPECT_CALL(session, get_enabled()) + .WillOnce(Return(true)); + + // Copy async, write when copied, not paused + set_session_modes(session, VS_COPY_ASYNC, VS_WRITE_WHEN_COPIED, false); + + // Copy and write should be called + EXPECT_CALL(session, copy_sim_data()) + .Times(1); + + EXPECT_CALL(session, write_data()) + .Times(1); + + // ACT + int result = session.copy_and_write_async(); + + // ASSERT + ASSERT_GE(result, 0); +} + +TEST_F(VariableServerSession_test, copy_async_no_copy_or_write) { + // ARRANGE + MockVariableServerSession session; + setup_partial_session_mock(session); + + // set enabled + EXPECT_CALL(session, get_enabled()) + .WillOnce(Return(true)); + + // Copy async, write when copied, not paused + set_session_modes(session, VS_COPY_SCHEDULED, VS_WRITE_WHEN_COPIED, false); + EXPECT_CALL(realtime_sync, is_active()) + .WillRepeatedly(Return(true)); + + + // Copy and write should not be called + EXPECT_CALL(session, copy_sim_data()) + .Times(0); + + EXPECT_CALL(session, write_data()) + .Times(0); + + // ACT + int result = session.copy_and_write_async(); + + // ASSERT + ASSERT_GE(result, 0); +} + +TEST_F(VariableServerSession_test, copy_async_non_realtime) { + // ARRANGE + MockVariableServerSession session; + setup_partial_session_mock(session); + + // set enabled + EXPECT_CALL(session, get_enabled()) + .WillOnce(Return(true)); + + // Copy async, write async, paused + set_session_modes(session, VS_COPY_SCHEDULED, VS_WRITE_WHEN_COPIED, false); + EXPECT_CALL(realtime_sync, is_active()) + .WillRepeatedly(Return(false)); + + // Copy and write should be called + EXPECT_CALL(session, copy_sim_data()) + .Times(0); + + EXPECT_CALL(session, write_data()) + .Times(1); + + // ACT + int result = session.copy_and_write_async(); + + // ASSERT + ASSERT_GE(result, 0); +} + +TEST_F(VariableServerSession_test, copy_async_paused) { + // ARRANGE + MockVariableServerSession session; + setup_partial_session_mock(session); + + // set enabled + EXPECT_CALL(session, get_enabled()) + .WillOnce(Return(true)); + + // Copy async, write async, paused + set_session_modes(session, VS_COPY_ASYNC, VS_WRITE_ASYNC, true); + + // Copy and write should be called + EXPECT_CALL(session, copy_sim_data()) + .Times(1); + + EXPECT_CALL(session, write_data()) + .Times(0); + + // ACT + int result = session.copy_and_write_async(); + + // ASSERT + ASSERT_GE(result, 0); +} + + +TEST_F(VariableServerSession_test, copy_async_write_fails) { + // ARRANGE + MockVariableServerSession session; + setup_partial_session_mock(session); + + // set enabled + EXPECT_CALL(session, get_enabled()) + .WillOnce(Return(true)); + + // Copy async, write async, paused + set_session_modes(session, VS_COPY_ASYNC, VS_WRITE_ASYNC, false); + + // Copy and write should be called + EXPECT_CALL(session, copy_sim_data()) + .Times(1); + + EXPECT_CALL(session, write_data()) + .WillOnce(Return(-1)); + + // Exit should be called + EXPECT_CALL(session, set_exit_cmd()) + .Times(1); + + // ACT + int result = session.copy_and_write_async(); + + // ASSERT + ASSERT_EQ(result, -1); +} + + +TEST_F(VariableServerSession_test, copy_and_write_top_disabled) { + // ARRANGE + MockVariableServerSession session; + + // set disabled + EXPECT_CALL(session, get_enabled()) + .WillOnce(Return(false)); + + // Nothing else should be called + EXPECT_CALL(session, copy_sim_data()) + .Times(0); + + EXPECT_CALL(session, write_data()) + .Times(0); + + session.copy_and_write_top(100); +} + +TEST_F(VariableServerSession_test, copy_and_write_top_copy_top_write_async) { + // ARRANGE + MockVariableServerSession session; + + // set enabled + EXPECT_CALL(session, get_enabled()) + .WillOnce(Return(true)); + + set_session_modes(session, VS_COPY_TOP_OF_FRAME, VS_WRITE_ASYNC, false); + EXPECT_CALL(realtime_sync, is_active()) + .WillRepeatedly(Return(true)); + + EXPECT_CALL(session, get_frame_multiple()) + .WillRepeatedly(Return(10)); + EXPECT_CALL(session, get_frame_offset()) + .WillRepeatedly(Return(0)); + + // Should only copy + EXPECT_CALL(session, copy_sim_data()) + .Times(1); + + EXPECT_CALL(session, write_data()) + .Times(0); + + // ACT + int result = session.copy_and_write_top(100); + + // ASSERT + EXPECT_EQ(result, 0); +} + +TEST_F(VariableServerSession_test, copy_and_write_top_copy_top_write_when_copied) { + // ARRANGE + MockVariableServerSession session; + + // set enabled + EXPECT_CALL(session, get_enabled()) + .WillOnce(Return(true)); + + set_session_modes(session, VS_COPY_TOP_OF_FRAME, VS_WRITE_WHEN_COPIED, false); + EXPECT_CALL(realtime_sync, is_active()) + .WillRepeatedly(Return(true)); + + EXPECT_CALL(session, get_frame_multiple()) + .WillRepeatedly(Return(10)); + EXPECT_CALL(session, get_frame_offset()) + .WillRepeatedly(Return(0)); + + // Should only copy + EXPECT_CALL(session, copy_sim_data()) + .Times(1); + + EXPECT_CALL(session, write_data()) + .Times(1); + + // ACT + int result = session.copy_and_write_top(100); + + // ASSERT + EXPECT_EQ(result, 0); +} + +TEST_F(VariableServerSession_test, copy_and_write_top_copy_top_dont_write_if_non_realtime) { + // ARRANGE + MockVariableServerSession session; + + // set enabled + EXPECT_CALL(session, get_enabled()) + .WillOnce(Return(true)); + + set_session_modes(session, VS_COPY_TOP_OF_FRAME, VS_WRITE_WHEN_COPIED, false); + EXPECT_CALL(realtime_sync, is_active()) + .WillRepeatedly(Return(false)); + EXPECT_CALL(session, get_frame_multiple()) + .WillRepeatedly(Return(10)); + EXPECT_CALL(session, get_frame_offset()) + .WillRepeatedly(Return(0)); + + // Should only copy + EXPECT_CALL(session, copy_sim_data()) + .Times(1); + + EXPECT_CALL(session, write_data()) + .Times(0); + + // ACT + int result = session.copy_and_write_top(100); + + // ASSERT + EXPECT_EQ(result, 0); +} + +TEST_F(VariableServerSession_test, copy_and_write_top_copy_top_write_paused) { + // ARRANGE + MockVariableServerSession session; + + // set enabled + EXPECT_CALL(session, get_enabled()) + .WillOnce(Return(true)); + + set_session_modes(session, VS_COPY_TOP_OF_FRAME, VS_WRITE_WHEN_COPIED, true); + EXPECT_CALL(realtime_sync, is_active()) + .WillRepeatedly(Return(true)); + + EXPECT_CALL(session, get_frame_multiple()) + .WillRepeatedly(Return(10)); + EXPECT_CALL(session, get_frame_offset()) + .WillRepeatedly(Return(0)); + + // Should only copy + EXPECT_CALL(session, copy_sim_data()) + .Times(1); + + EXPECT_CALL(session, write_data()) + .Times(0); + + // ACT + int result = session.copy_and_write_top(100); + + // ASSERT + EXPECT_EQ(result, 0); +} + + +TEST_F(VariableServerSession_test, copy_and_write_top_wrong_offset) { + // ARRANGE + MockVariableServerSession session; + + // set enabled + EXPECT_CALL(session, get_enabled()) + .WillOnce(Return(true)); + + EXPECT_CALL(session, get_copy_mode()) + .WillRepeatedly(Return(VS_COPY_TOP_OF_FRAME)); + EXPECT_CALL(session, get_write_mode()) + .WillRepeatedly(Return(VS_WRITE_WHEN_COPIED)); + + EXPECT_CALL(realtime_sync, is_active()) + .WillRepeatedly(Return(true)); + + EXPECT_CALL(session, get_frame_multiple()) + .WillRepeatedly(Return(10)); + EXPECT_CALL(session, get_frame_offset()) + .WillRepeatedly(Return(0)); + + // Should only copy + EXPECT_CALL(session, copy_sim_data()) + .Times(0); + + EXPECT_CALL(session, write_data()) + .Times(0); + + // ACT + int result = session.copy_and_write_top(101); + + // ASSERT + EXPECT_EQ(result, 0); +} + + + +TEST_F(VariableServerSession_test, copy_and_write_freeze_disabled) { + // ARRANGE + MockVariableServerSession session; + + // set disabled + EXPECT_CALL(session, get_enabled()) + .WillOnce(Return(false)); + + // Nothing else should be called + EXPECT_CALL(session, copy_sim_data()) + .Times(0); + + EXPECT_CALL(session, write_data()) + .Times(0); + + session.copy_and_write_freeze(100); +} + +TEST_F(VariableServerSession_test, copy_and_write_freeze_copy_top_write_async) { + // ARRANGE + MockVariableServerSession session; + + // set enabled + EXPECT_CALL(session, get_enabled()) + .WillOnce(Return(true)); + + set_session_modes(session, VS_COPY_TOP_OF_FRAME, VS_WRITE_ASYNC, false); + EXPECT_CALL(realtime_sync, is_active()) + .WillRepeatedly(Return(true)); + + EXPECT_CALL(session, get_freeze_frame_multiple()) + .WillRepeatedly(Return(10)); + EXPECT_CALL(session, get_freeze_frame_offset()) + .WillRepeatedly(Return(0)); + + // Should only copy + EXPECT_CALL(session, copy_sim_data()) + .Times(1); + + EXPECT_CALL(session, write_data()) + .Times(0); + + // ACT + int result = session.copy_and_write_freeze(100); + + // ASSERT + EXPECT_EQ(result, 0); +} + +TEST_F(VariableServerSession_test, copy_and_write_freeze_copy_top_write_when_copied) { + // ARRANGE + MockVariableServerSession session; + + // set enabled + EXPECT_CALL(session, get_enabled()) + .WillOnce(Return(true)); + + set_session_modes(session, VS_COPY_TOP_OF_FRAME, VS_WRITE_WHEN_COPIED, false); + EXPECT_CALL(realtime_sync, is_active()) + .WillRepeatedly(Return(true)); + + EXPECT_CALL(session, get_freeze_frame_multiple()) + .WillRepeatedly(Return(10)); + EXPECT_CALL(session, get_freeze_frame_offset()) + .WillRepeatedly(Return(0)); + + // Should only copy + EXPECT_CALL(session, copy_sim_data()) + .Times(1); + + EXPECT_CALL(session, write_data()) + .Times(1); + + // ACT + int result = session.copy_and_write_freeze(100); + + // ASSERT + EXPECT_EQ(result, 0); +} + +TEST_F(VariableServerSession_test, copy_and_write_freeze_copy_top_dont_write_if_non_realtime) { + // ARRANGE + MockVariableServerSession session; + + // set enabled + EXPECT_CALL(session, get_enabled()) + .WillOnce(Return(true)); + + set_session_modes(session, VS_COPY_TOP_OF_FRAME, VS_WRITE_WHEN_COPIED, false); + EXPECT_CALL(realtime_sync, is_active()) + .WillRepeatedly(Return(false)); + + EXPECT_CALL(session, get_freeze_frame_multiple()) + .WillRepeatedly(Return(10)); + EXPECT_CALL(session, get_freeze_frame_offset()) + .WillRepeatedly(Return(0)); + + // Should only copy + EXPECT_CALL(session, copy_sim_data()) + .Times(1); + + EXPECT_CALL(session, write_data()) + .Times(0); + + // ACT + int result = session.copy_and_write_freeze(100); + + // ASSERT + EXPECT_EQ(result, 0); +} + +TEST_F(VariableServerSession_test, copy_and_write_freeze_copy_top_write_paused) { + // ARRANGE + MockVariableServerSession session; + + // set enabled + EXPECT_CALL(session, get_enabled()) + .WillOnce(Return(true)); + + set_session_modes(session, VS_COPY_TOP_OF_FRAME, VS_WRITE_WHEN_COPIED, true); + EXPECT_CALL(realtime_sync, is_active()) + .WillRepeatedly(Return(true)); + + EXPECT_CALL(session, get_freeze_frame_multiple()) + .WillRepeatedly(Return(10)); + EXPECT_CALL(session, get_freeze_frame_offset()) + .WillRepeatedly(Return(0)); + + // Should only copy + EXPECT_CALL(session, copy_sim_data()) + .Times(1); + + EXPECT_CALL(session, write_data()) + .Times(0); + + // ACT + int result = session.copy_and_write_freeze(100); + + // ASSERT + EXPECT_EQ(result, 0); +} + + +TEST_F(VariableServerSession_test, copy_and_write_freeze_wrong_offset) { + // ARRANGE + MockVariableServerSession session; + + // set enabled + EXPECT_CALL(session, get_enabled()) + .WillOnce(Return(true)); + + EXPECT_CALL(session, get_copy_mode()) + .WillRepeatedly(Return(VS_COPY_TOP_OF_FRAME)); + EXPECT_CALL(session, get_write_mode()) + .WillRepeatedly(Return(VS_WRITE_WHEN_COPIED)); + + EXPECT_CALL(realtime_sync, is_active()) + .WillRepeatedly(Return(true)); + + EXPECT_CALL(session, get_freeze_frame_multiple()) + .WillRepeatedly(Return(10)); + EXPECT_CALL(session, get_freeze_frame_offset()) + .WillRepeatedly(Return(0)); + + // Should only copy + EXPECT_CALL(session, copy_sim_data()) + .Times(0); + + EXPECT_CALL(session, write_data()) + .Times(0); + + // ACT + int result = session.copy_and_write_freeze(101); + + // ASSERT + EXPECT_EQ(result, 0); +} + +TEST_F(VariableServerSession_test, copy_and_write_scheduled_disabled) { + // ARRANGE + MockVariableServerSession session; + + // set enabled + EXPECT_CALL(session, get_enabled()) + .WillOnce(Return(false)); + + // Shouldn't do anything + EXPECT_CALL(session, copy_sim_data()) + .Times(0); + + EXPECT_CALL(session, write_data()) + .Times(0); + + // ACT + int result = session.copy_and_write_scheduled(100); + + // ASSERT + EXPECT_EQ(result, 0); +} + +TEST_F(VariableServerSession_test, copy_and_write_scheduled_wrong_mode) { + // ARRANGE + MockVariableServerSession session; + + // set enabled + EXPECT_CALL(session, get_enabled()) + .WillOnce(Return(false)); + EXPECT_CALL(session, get_copy_mode()) + .WillRepeatedly(Return(VS_COPY_TOP_OF_FRAME)); + + // Shouldn't do anything + EXPECT_CALL(session, copy_sim_data()) + .Times(0); + + EXPECT_CALL(session, write_data()) + .Times(0); + + // ACT + int result = session.copy_and_write_scheduled(100); + + // ASSERT + EXPECT_EQ(result, 0); +} + +TEST_F(VariableServerSession_test, copy_and_write_scheduled_wrong_tics) { + // ARRANGE + MockVariableServerSession session; + + // set enabled + EXPECT_CALL(session, get_enabled()) + .WillOnce(Return(false)); + EXPECT_CALL(session, get_copy_mode()) + .WillRepeatedly(Return(VS_COPY_TOP_OF_FRAME)); + EXPECT_CALL(session, get_next_tics()) + .WillRepeatedly(Return(10)); + + // Shouldn't do anything + EXPECT_CALL(session, copy_sim_data()) + .Times(0); + + EXPECT_CALL(session, write_data()) + .Times(0); + + // ACT + int result = session.copy_and_write_scheduled(100); + + // ASSERT + EXPECT_EQ(result, 0); +} + + +TEST_F(VariableServerSession_test, copy_and_write_scheduled_copy_and_write) { + // ARRANGE + MockVariableServerSession session; + + // set enabled + EXPECT_CALL(session, get_enabled()) + .WillOnce(Return(true)); + + set_session_modes(session, VS_COPY_SCHEDULED, VS_WRITE_WHEN_COPIED, false); + EXPECT_CALL(realtime_sync, is_active()) + .WillRepeatedly(Return(true)); + + EXPECT_CALL(session, get_next_tics()) + .WillRepeatedly(Return(100)); + + // Shouldn't do anything + EXPECT_CALL(session, copy_sim_data()) + .Times(1); + + EXPECT_CALL(session, write_data()) + .Times(1); + + // ACT + int result = session.copy_and_write_scheduled(100); + + // ASSERT + EXPECT_EQ(result, 0); +} + +TEST_F(VariableServerSession_test, copy_and_write_scheduled_copy_scheduled_write_async) { + // ARRANGE + MockVariableServerSession session; + + // set enabled + EXPECT_CALL(session, get_enabled()) + .WillOnce(Return(true)); + + set_session_modes(session, VS_COPY_SCHEDULED, VS_WRITE_ASYNC, false); + EXPECT_CALL(realtime_sync, is_active()) + .WillRepeatedly(Return(true)); + + EXPECT_CALL(session, get_next_tics()) + .WillRepeatedly(Return(100)); + + // Shouldn't do anything + EXPECT_CALL(session, copy_sim_data()) + .Times(1); + + EXPECT_CALL(session, write_data()) + .Times(0); + + // ACT + int result = session.copy_and_write_scheduled(100); + + // ASSERT + EXPECT_EQ(result, 0); +} + +TEST_F(VariableServerSession_test, copy_and_write_scheduled_paused) { + // ARRANGE + MockVariableServerSession session; + + // set enabled + EXPECT_CALL(session, get_enabled()) + .WillOnce(Return(true)); + + set_session_modes(session, VS_COPY_SCHEDULED, VS_WRITE_ASYNC, true); + EXPECT_CALL(realtime_sync, is_active()) + .WillRepeatedly(Return(true)); + + EXPECT_CALL(session, get_next_tics()) + .WillRepeatedly(Return(100)); + + // Shouldn't do anything + EXPECT_CALL(session, copy_sim_data()) + .Times(1); + + EXPECT_CALL(session, write_data()) + .Times(0); + + // ACT + int result = session.copy_and_write_scheduled(100); + + // ASSERT + EXPECT_EQ(result, 0); +} + +TEST_F(VariableServerSession_test, copy_and_write_scheduled_non_realtime) { + // ARRANGE + MockVariableServerSession session; + + // set enabled + EXPECT_CALL(session, get_enabled()) + .WillOnce(Return(true)); + + set_session_modes(session, VS_COPY_SCHEDULED, VS_WRITE_ASYNC, false); + EXPECT_CALL(realtime_sync, is_active()) + .WillRepeatedly(Return(false)); + + EXPECT_CALL(session, get_next_tics()) + .WillRepeatedly(Return(100)); + + // Shouldn't do anything + EXPECT_CALL(session, copy_sim_data()) + .Times(1); + + EXPECT_CALL(session, write_data()) + .Times(0); + + // ACT + int result = session.copy_and_write_scheduled(100); + + // ASSERT + EXPECT_EQ(result, 0); +} + + +TEST_F(VariableServerSession_test, copy_and_write_scheduled_write_fails) { + // ARRANGE + MockVariableServerSession session; + + // set enabled + EXPECT_CALL(session, get_enabled()) + .WillOnce(Return(true)); + + set_session_modes(session, VS_COPY_SCHEDULED, VS_WRITE_WHEN_COPIED, false); + EXPECT_CALL(realtime_sync, is_active()) + .WillRepeatedly(Return(true)); + + EXPECT_CALL(session, get_next_tics()) + .WillRepeatedly(Return(100)); + + EXPECT_CALL(session, copy_sim_data()) + .Times(1); + + EXPECT_CALL(session, write_data()) + .WillOnce(Return(-1)); + + EXPECT_CALL(session, set_exit_cmd()) + .Times(1); + + // ACT + int result = session.copy_and_write_scheduled(100); + + // ASSERT + EXPECT_EQ(result, -1); +} + + +TEST_F(VariableServerSession_test, copy_and_write_freeze_scheduled_disabled) { + // ARRANGE + MockVariableServerSession session; + + // set enabled + EXPECT_CALL(session, get_enabled()) + .WillOnce(Return(false)); + + // Shouldn't do anything + EXPECT_CALL(session, copy_sim_data()) + .Times(0); + + EXPECT_CALL(session, write_data()) + .Times(0); + + // ACT + int result = session.copy_and_write_freeze_scheduled(100); + + // ASSERT + EXPECT_EQ(result, 0); +} + +TEST_F(VariableServerSession_test, copy_and_write_freeze_scheduled_wrong_mode) { + // ARRANGE + MockVariableServerSession session; + + // set enabled + EXPECT_CALL(session, get_enabled()) + .WillOnce(Return(false)); + EXPECT_CALL(session, get_copy_mode()) + .WillRepeatedly(Return(VS_COPY_TOP_OF_FRAME)); + + // Shouldn't do anything + EXPECT_CALL(session, copy_sim_data()) + .Times(0); + + EXPECT_CALL(session, write_data()) + .Times(0); + + // ACT + int result = session.copy_and_write_freeze_scheduled(100); + + // ASSERT + EXPECT_EQ(result, 0); +} + +TEST_F(VariableServerSession_test, copy_and_write_freeze_scheduled_wrong_tics) { + // ARRANGE + MockVariableServerSession session; + + // set enabled + EXPECT_CALL(session, get_enabled()) + .WillOnce(Return(false)); + EXPECT_CALL(session, get_copy_mode()) + .WillRepeatedly(Return(VS_COPY_TOP_OF_FRAME)); + EXPECT_CALL(session, get_freeze_next_tics()) + .WillRepeatedly(Return(10)); + + // Shouldn't do anything + EXPECT_CALL(session, copy_sim_data()) + .Times(0); + + EXPECT_CALL(session, write_data()) + .Times(0); + + // ACT + int result = session.copy_and_write_freeze_scheduled(100); + + // ASSERT + EXPECT_EQ(result, 0); +} + + +TEST_F(VariableServerSession_test, copy_and_write_freeze_scheduled_copy_and_write) { + // ARRANGE + MockVariableServerSession session; + + // set enabled + EXPECT_CALL(session, get_enabled()) + .WillOnce(Return(true)); + + set_session_modes(session, VS_COPY_SCHEDULED, VS_WRITE_WHEN_COPIED, false); + EXPECT_CALL(realtime_sync, is_active()) + .WillRepeatedly(Return(true)); + + EXPECT_CALL(session, get_freeze_next_tics()) + .WillRepeatedly(Return(100)); + + // Shouldn't do anything + EXPECT_CALL(session, copy_sim_data()) + .Times(1); + + EXPECT_CALL(session, write_data()) + .Times(1); + + // ACT + int result = session.copy_and_write_freeze_scheduled(100); + + // ASSERT + EXPECT_EQ(result, 0); +} + +TEST_F(VariableServerSession_test, copy_and_write_freeze_scheduled_copy_scheduled_write_async) { + // ARRANGE + MockVariableServerSession session; + + // set enabled + EXPECT_CALL(session, get_enabled()) + .WillOnce(Return(true)); + + set_session_modes(session, VS_COPY_SCHEDULED, VS_WRITE_ASYNC, false); + EXPECT_CALL(realtime_sync, is_active()) + .WillRepeatedly(Return(true)); + + EXPECT_CALL(session, get_freeze_next_tics()) + .WillRepeatedly(Return(100)); + + // Shouldn't do anything + EXPECT_CALL(session, copy_sim_data()) + .Times(1); + + EXPECT_CALL(session, write_data()) + .Times(0); + + // ACT + int result = session.copy_and_write_freeze_scheduled(100); + + // ASSERT + EXPECT_EQ(result, 0); +} + +TEST_F(VariableServerSession_test, copy_and_write_freeze_scheduled_paused) { + // ARRANGE + MockVariableServerSession session; + + // set enabled + EXPECT_CALL(session, get_enabled()) + .WillOnce(Return(true)); + + set_session_modes(session, VS_COPY_SCHEDULED, VS_WRITE_ASYNC, true); + EXPECT_CALL(realtime_sync, is_active()) + .WillRepeatedly(Return(true)); + + EXPECT_CALL(session, get_freeze_next_tics()) + .WillRepeatedly(Return(100)); + + // Shouldn't do anything + EXPECT_CALL(session, copy_sim_data()) + .Times(1); + + EXPECT_CALL(session, write_data()) + .Times(0); + + // ACT + int result = session.copy_and_write_freeze_scheduled(100); + + // ASSERT + EXPECT_EQ(result, 0); +} + +TEST_F(VariableServerSession_test, copy_and_write_freeze_scheduled_non_realtime) { + // ARRANGE + MockVariableServerSession session; + + // set enabled + EXPECT_CALL(session, get_enabled()) + .WillOnce(Return(true)); + + set_session_modes(session, VS_COPY_SCHEDULED, VS_WRITE_ASYNC, false); + EXPECT_CALL(realtime_sync, is_active()) + .WillRepeatedly(Return(false)); + + EXPECT_CALL(session, get_freeze_next_tics()) + .WillRepeatedly(Return(100)); + + // Shouldn't do anything + EXPECT_CALL(session, copy_sim_data()) + .Times(1); + + EXPECT_CALL(session, write_data()) + .Times(0); + + // ACT + int result = session.copy_and_write_freeze_scheduled(100); + + // ASSERT + EXPECT_EQ(result, 0); +} + + +TEST_F(VariableServerSession_test, copy_and_write_freeze_scheduled_write_fails) { + // ARRANGE + MockVariableServerSession session; + + // set enabled + EXPECT_CALL(session, get_enabled()) + .WillOnce(Return(true)); + + set_session_modes(session, VS_COPY_SCHEDULED, VS_WRITE_WHEN_COPIED, false); + EXPECT_CALL(realtime_sync, is_active()) + .WillRepeatedly(Return(true)); + + EXPECT_CALL(session, get_freeze_next_tics()) + .WillRepeatedly(Return(100)); + + EXPECT_CALL(session, copy_sim_data()) + .Times(1); + + EXPECT_CALL(session, write_data()) + .WillOnce(Return(-1)); + + EXPECT_CALL(session, set_exit_cmd()) + .Times(1); + + // ACT + int result = session.copy_and_write_freeze_scheduled(100); + + // ASSERT + EXPECT_EQ(result, -1); +} \ No newline at end of file diff --git a/trick_source/sim_services/VariableServer/test/VariableServer_test.cc b/trick_source/sim_services/VariableServer/test/VariableServer_test.cc new file mode 100644 index 00000000..ec0b51d7 --- /dev/null +++ b/trick_source/sim_services/VariableServer/test/VariableServer_test.cc @@ -0,0 +1,146 @@ +/******************************TRICK HEADER************************************* +PURPOSE: ( Tests for the VariableServer class ) +*******************************************************************************/ + +#include +#include + + +#include "trick/VariableServer.hh" +#include "trick/Mock/MockVariableServerSession.hh" + + +/* + Test Fixture. + */ +class VariableServer_test : public ::testing::Test { + protected: + Trick::VariableServer vs; + + VariableServer_test() { + + } + + ~VariableServer_test() { + } + + void SetUp() {} + void TearDown() {} +}; + +TEST_F(VariableServer_test, set_log_on) { + // ARRANGE + MockVariableServerSession session; + EXPECT_CALL(session, set_log(true)) + .Times(1); + + pthread_t id = (pthread_t) 5; + vs.add_session(id, &session); + + // ACT + vs.set_var_server_log_on(); + + // ASSERT + EXPECT_EQ(vs.get_log(), true); +} + +TEST_F(VariableServer_test, set_log_off) { + // ARRANGE + MockVariableServerSession session; + EXPECT_CALL(session, set_log(false)) + .Times(1); + + pthread_t id = (pthread_t) 5; + vs.add_session(id, &session); + + // ACT + vs.set_var_server_log_off(); + + // ASSERT + EXPECT_EQ(vs.get_log(), false); +} + + +TEST_F(VariableServer_test, set_session_log_on) { + // ARRANGE + MockVariableServerSession session; + EXPECT_CALL(session, set_session_log(true)) + .Times(1); + + pthread_t id = (pthread_t) 5; + vs.add_session(id, &session); + + // ACT + vs.set_var_server_session_log_on(); + + // ASSERT + EXPECT_EQ(vs.get_session_log(), true); +} + +TEST_F(VariableServer_test, set_session_log_off) { + // ARRANGE + MockVariableServerSession session; + EXPECT_CALL(session, set_session_log(false)) + .Times(1); + + pthread_t id = (pthread_t) 5; + vs.add_session(id, &session); + + // ACT + vs.set_var_server_session_log_off(); + + // ASSERT + EXPECT_EQ(vs.get_session_log(), false); +} + + +TEST_F(VariableServer_test, enabled_by_default) { + // ARRANGE + // ACT + // ASSERT + EXPECT_EQ(vs.get_enabled(), true); +} + +TEST_F(VariableServer_test, set_enabled) { + // ARRANGE + // ACT + vs.set_enabled(false); + + // ASSERT + EXPECT_EQ(vs.get_enabled(), false); +} + +TEST_F(VariableServer_test, info_msg_off_by_default) { + // ARRANGE + // ACT + // ASSERT + EXPECT_EQ(vs.get_info_msg(), false); +} + +TEST_F(VariableServer_test, set_info_msg) { + // ARRANGE + // ACT + vs.set_var_server_info_msg_on(); + + // ASSERT + EXPECT_EQ(vs.get_info_msg(), true); +} + +TEST_F(VariableServer_test, set_info_msg_off) { + // ARRANGE + // ACT + vs.set_var_server_info_msg_off(); + + // ASSERT + EXPECT_EQ(vs.get_info_msg(), false); +} + +TEST_F(VariableServer_test, info_dump) { + + MockVariableServerSession session; + pthread_t id = (pthread_t) 5; + vs.add_session(id, &session); + + std::cout << vs << std::endl; + +} \ No newline at end of file diff --git a/trick_source/sim_services/VariableServer/var_server_ext.cpp b/trick_source/sim_services/VariableServer/var_server_ext.cpp index 9d39e3d2..cc47d281 100644 --- a/trick_source/sim_services/VariableServer/var_server_ext.cpp +++ b/trick_source/sim_services/VariableServer/var_server_ext.cpp @@ -11,161 +11,167 @@ extern Trick::VariableServer * the_vs ; -Trick::VariableServerThread * get_vst() { +Trick::VariableServerSessionThread * get_vst() { return the_vs->get_vst(pthread_self()) ; } +Trick::VariableServerSession * get_session() { + return the_vs->get_session(pthread_self()) ; +} + int var_add(std::string in_name) { - Trick::VariableServerThread * vst ; - vst = get_vst() ; - if (vst != NULL ) { - vst->var_add(in_name) ; + Trick::VariableServerSession * session = get_session(); + + if (session != NULL ) { + session->var_add(in_name) ; } return(0) ; } int var_add(std::string in_name, std::string in_units) { - Trick::VariableServerThread * vst ; - vst = get_vst() ; - if (vst != NULL ) { - vst->var_add(in_name, in_units) ; + Trick::VariableServerSession * session = get_session(); + + if (session != NULL ) { + session->var_add(in_name, in_units) ; } return(0) ; } int var_remove(std::string in_name) { - Trick::VariableServerThread * vst ; - vst = get_vst() ; - if (vst != NULL ) { - vst->var_remove(in_name) ; + Trick::VariableServerSession * session = get_session(); + + if (session != NULL ) { + session->var_remove(in_name) ; } return(0) ; } int var_units(std::string var_name , std::string units_name) { - Trick::VariableServerThread * vst ; - vst = get_vst() ; - if (vst != NULL ) { - vst->var_units(var_name , units_name) ; + Trick::VariableServerSession * session = get_session(); + + if (session != NULL ) { + session->var_units(var_name , units_name) ; } return(0) ; } int var_exists(std::string in_name) { - Trick::VariableServerThread * vst ; - vst = get_vst() ; - if (vst != NULL ) { - vst->var_exists(in_name) ; + Trick::VariableServerSession * session = get_session(); + + if (session != NULL ) { + session->var_exists(in_name) ; } return(0) ; } int var_send_once(std::string in_name) { - Trick::VariableServerThread * vst ; - vst = get_vst() ; - if (vst != NULL ) { - vst->var_send_once(in_name, 1) ; + Trick::VariableServerSession * session = get_session(); + + if (session != NULL ) { + session->var_send_once(in_name, 1) ; } return(0) ; } int var_send_once(std::string in_name, int num) { - Trick::VariableServerThread * vst ; - vst = get_vst() ; - if (vst != NULL ) { - vst->var_send_once(in_name, num) ; + Trick::VariableServerSession * session = get_session(); + + if (session != NULL ) { + session->var_send_once(in_name, num) ; } return(0) ; } int var_send() { - Trick::VariableServerThread * vst ; - vst = get_vst() ; - if (vst != NULL ) { - vst->var_send() ; + Trick::VariableServerSession * session = get_session(); + + if (session != NULL ) { + session->var_send() ; } return(0) ; } int var_clear() { - Trick::VariableServerThread * vst ; - vst = get_vst() ; - if (vst != NULL ) { - vst->var_clear() ; + Trick::VariableServerSession * session = get_session(); + + if (session != NULL ) { + session->var_clear() ; } + + // std::cout << "Done with var_clear" << std::endl; return(0) ; } int var_cycle(double in_rate) { - Trick::VariableServerThread * vst ; - vst = get_vst() ; - if (vst != NULL ) { - vst->var_cycle(in_rate) ; + Trick::VariableServerSession * session = get_session(); + + if (session != NULL ) { + session->var_cycle(in_rate) ; } return(0) ; } int var_pause() { - Trick::VariableServerThread * vst ; - vst = get_vst() ; - if (vst != NULL ) { - vst->set_pause(true) ; + Trick::VariableServerSession * session = get_session(); + + if (session != NULL ) { + session->set_pause(true) ; } return(0) ; } int var_unpause() { - Trick::VariableServerThread * vst ; - vst = get_vst() ; - if (vst != NULL ) { - vst->set_pause(false) ; + Trick::VariableServerSession * session = get_session(); + + if (session != NULL ) { + session->set_pause(false) ; } return(0) ; } int var_exit() { - Trick::VariableServerThread * vst ; - vst = get_vst() ; - if (vst != NULL ) { - vst->var_exit() ; + Trick::VariableServerSession * session = get_session(); + + if (session != NULL ) { + session->var_exit() ; } return(0) ; } int var_validate_address(int on_off) { - Trick::VariableServerThread * vst ; - vst = get_vst() ; - if (vst != NULL ) { - vst->var_validate_address((bool)on_off) ; + Trick::VariableServerSession * session = get_session(); + + if (session != NULL ) { + session->var_validate_address((bool)on_off) ; } return(0) ; } int var_debug(int level) { - Trick::VariableServerThread * vst ; - vst = get_vst() ; - if (vst != NULL ) { - vst->var_debug(level) ; + Trick::VariableServerSession * session = get_session(); + + if (session != NULL ) { + session->var_debug(level) ; } return(0) ; } int var_ascii() { - Trick::VariableServerThread * vst ; - vst = get_vst() ; - if (vst != NULL ) { - vst->var_ascii() ; + Trick::VariableServerSession * session = get_session(); + + if (session != NULL ) { + session->var_ascii() ; } return(0) ; } int var_binary() { - Trick::VariableServerThread * vst ; - vst = get_vst() ; - if (vst != NULL ) { - vst->var_binary() ; + Trick::VariableServerSession * session = get_session(); + + if (session != NULL ) { + session->var_binary() ; } return(0) ; } @@ -176,19 +182,19 @@ int var_retry_bad_ref() { } int var_binary_nonames() { - Trick::VariableServerThread * vst ; - vst = get_vst() ; - if (vst != NULL ) { - vst->var_binary_nonames() ; + Trick::VariableServerSession * session = get_session(); + + if (session != NULL ) { + session->var_binary_nonames() ; } return(0) ; } int var_set_copy_mode(int mode) { - Trick::VariableServerThread * vst ; - vst = get_vst() ; - if (vst != NULL ) { - vst->var_set_copy_mode(mode) ; + Trick::VariableServerSession * session = get_session(); + + if (session != NULL ) { + session->var_set_copy_mode(mode) ; if ( mode == VS_COPY_SCHEDULED ) { the_vs->get_next_sync_call_time() ; the_vs->get_next_freeze_call_time() ; @@ -198,28 +204,25 @@ int var_set_copy_mode(int mode) { } int var_set_write_mode(int mode) { - Trick::VariableServerThread * vst ; - vst = get_vst() ; - if (vst != NULL ) { - vst->var_set_write_mode(mode) ; +Trick::VariableServerSession * session = get_session(); + if (session != NULL ) { + session->var_set_write_mode(mode) ; } return 0 ; } int var_set_send_stdio(int mode) { - Trick::VariableServerThread * vst ; - vst = get_vst() ; - if (vst != NULL ) { - vst->set_send_stdio((bool)mode) ; +Trick::VariableServerSession * session = get_session(); + if (session != NULL ) { + session->set_send_stdio((bool)mode) ; } return 0 ; } int var_sync(int mode) { - Trick::VariableServerThread * vst ; - vst = get_vst() ; - if (vst != NULL ) { - vst->var_sync(mode) ; +Trick::VariableServerSession * session = get_session(); + if (session != NULL ) { + session->var_sync(mode) ; if ( mode ) { the_vs->get_next_sync_call_time() ; the_vs->get_next_freeze_call_time() ; @@ -229,73 +232,50 @@ int var_sync(int mode) { } int var_set_frame_multiple(unsigned int mult) { - Trick::VariableServerThread * vst ; - vst = get_vst() ; - if (vst != NULL ) { - vst->var_set_frame_multiple(mult) ; +Trick::VariableServerSession * session = get_session(); + if (session != NULL ) { + session->var_set_frame_multiple(mult) ; } return 0 ; } int var_set_frame_offset(unsigned int offset) { - Trick::VariableServerThread * vst ; - vst = get_vst() ; - if (vst != NULL ) { - vst->var_set_frame_offset(offset) ; +Trick::VariableServerSession * session = get_session(); + if (session != NULL ) { + session->var_set_frame_offset(offset) ; } return 0 ; } int var_set_freeze_frame_multiple(unsigned int mult) { - Trick::VariableServerThread * vst ; - vst = get_vst() ; - if (vst != NULL ) { - vst->var_set_freeze_frame_multiple(mult) ; +Trick::VariableServerSession * session = get_session(); + if (session != NULL ) { + session->var_set_freeze_frame_multiple(mult) ; } return 0 ; } int var_set_freeze_frame_offset(unsigned int offset) { - Trick::VariableServerThread * vst ; - vst = get_vst() ; - if (vst != NULL ) { - vst->var_set_freeze_frame_offset(offset) ; +Trick::VariableServerSession * session = get_session(); + if (session != NULL ) { + session->var_set_freeze_frame_offset(offset) ; } return 0 ; } int var_byteswap(bool on_off) { - Trick::VariableServerThread * vst ; - vst = get_vst() ; - if (vst != NULL ) { - vst->var_byteswap(on_off) ; - } - return(0) ; -} - -int var_signal() { - Trick::VariableServerThread * vst ; - vst = get_vst() ; - if (vst != NULL ) { - vst->var_signal() ; - } - return(0) ; -} - -int var_multicast(bool on_off) { - Trick::VariableServerThread * vst ; - vst = get_vst() ; - if (vst != NULL ) { - vst->var_multicast(on_off) ; +Trick::VariableServerSession * session = get_session(); + if (session != NULL ) { + session->var_byteswap(on_off) ; } return(0) ; } int var_write_stdio(int stream , std::string text ) { - Trick::VariableServerThread * vst ; - vst = get_vst() ; - if (vst != NULL and vst->get_send_stdio() == true) { - vst->write_stdio(stream, text) ; + // std::cout << "Executing var_write_stdio" << std::endl; + Trick::VariableServerSession * session = get_session(); + if (session != NULL and session->get_send_stdio() == true) { + session->write_stdio(stream, text) ; } else { if ( stream == 1 ) { fprintf( stdout , "%s" , text.c_str() ) ; @@ -307,14 +287,11 @@ int var_write_stdio(int stream , std::string text ) { } int var_set_client_tag( std::string text ) { - Trick::VariableServerThread * vst ; - vst = get_vst() ; + Trick::VariableServerSessionThread * vst = get_vst(); if (vst != NULL) { - // tag char declared length is 80 - if (text.length()>=80) { - text.resize(79); - } - strcpy(vst->get_connection().client_tag, text.c_str()); + + vst->set_client_tag(text); + #if __linux #ifdef __GNUC__ #if __GNUC__ >= 4 && __GNUC_MINOR__ >= 2 @@ -328,55 +305,50 @@ int var_set_client_tag( std::string text ) { } int var_send_list_size() { - Trick::VariableServerThread * vst ; - vst = get_vst() ; - if (vst != NULL ) { - vst->send_list_size() ; + + Trick::VariableServerSession * session = get_session(); + if (session != NULL ) { + session->send_list_size() ; } return(0) ; } int send_sie_resource() { - Trick::VariableServerThread * vst ; - vst = get_vst() ; - if (vst != NULL ) { - vst->send_sie_resource() ; +Trick::VariableServerSession * session = get_session(); + if (session != NULL ) { + session->send_sie_resource() ; } return 0 ; } int send_sie_class() { - Trick::VariableServerThread * vst ; - vst = get_vst() ; - if (vst != NULL ) { - vst->send_sie_class() ; +Trick::VariableServerSession * session = get_session(); + if (session != NULL ) { + session->send_sie_class() ; } return 0 ; } int send_sie_enum() { - Trick::VariableServerThread * vst ; - vst = get_vst() ; - if (vst != NULL ) { - vst->send_sie_enum() ; +Trick::VariableServerSession * session = get_session(); + if (session != NULL ) { + session->send_sie_enum() ; } return 0 ; } int send_sie_top_level_objects() { - Trick::VariableServerThread * vst ; - vst = get_vst() ; - if (vst != NULL ) { - return vst->send_sie_top_level_objects() ; +Trick::VariableServerSession * session = get_session(); + if (session != NULL ) { + return session->send_sie_top_level_objects() ; } return 0 ; } int send_file(std::string file_name) { - Trick::VariableServerThread * vst ; - vst = get_vst() ; - if (vst != NULL ) { - return vst->send_file(file_name) ; +Trick::VariableServerSession * session = get_session(); + if (session != NULL ) { + return session->send_file(file_name) ; } return 0 ; } @@ -384,20 +356,36 @@ int send_file(std::string file_name) { // Command to turn on log to playback file int var_server_log_on() { - Trick::VariableServerThread * vst ; - vst = get_vst() ; - if (vst != NULL ) { - return vst->set_log_on() ; +Trick::VariableServerSession * session = get_session(); + if (session != NULL ) { + return session->set_log(true) ; } return 0 ; } // Command to turn off log to playback file int var_server_log_off() { - Trick::VariableServerThread * vst ; - vst = get_vst() ; - if (vst != NULL ) { - return vst->set_log_off() ; +Trick::VariableServerSession * session = get_session(); + if (session != NULL ) { + return session->set_log(false) ; + } + return 0 ; +} + +// Command to turn on log to playback file +int var_server_session_log_on() { +Trick::VariableServerSession * session = get_session(); + if (session != NULL ) { + return session->set_session_log(true) ; + } + return 0 ; +} + +// Command to turn off log to playback file +int var_server_session_log_off() { +Trick::VariableServerSession * session = get_session(); + if (session != NULL ) { + return session->set_session_log(false) ; } return 0 ; } @@ -416,7 +404,8 @@ extern "C" void var_server_list_connections(void) { * C wrapper Trick::VariableServer::get_hostname */ extern "C" const char * var_server_get_hostname(void) { - return(the_vs->get_hostname()) ; + const char * ret = (the_vs->get_hostname()) ; + return ret; } /** @@ -443,11 +432,7 @@ extern "C" void var_server_set_port(unsigned short port) { * C wrapper Trick::VariableServer::set_socket_priority */ extern "C" void var_server_set_socket_priority(unsigned short socket_priority) { -#ifndef __APPLE__ the_vs->get_listen_thread().set_socket_priority(socket_priority); -#else - message_publish(MSG_ERROR,"var_server_set_socket_priority not available on MacOS\n") ; -#endif } /** @@ -569,8 +554,10 @@ int var_set_base( const char * var , T value , const char * units ) { ref_assignment(ref , &v_tree) ; if(ref->units != NULL) { free(ref->units) ; + ref->units = NULL; } free(ref) ; + ref = NULL; } else { message_publish(MSG_WARNING,"Cannot assign to %s because io_spec does not allow input\n", var) ; } diff --git a/trick_source/sim_services/VariableServer/vs_format_ascii.cpp b/trick_source/sim_services/VariableServer/vs_format_ascii.cpp deleted file mode 100644 index 932f2f4d..00000000 --- a/trick_source/sim_services/VariableServer/vs_format_ascii.cpp +++ /dev/null @@ -1,265 +0,0 @@ -/* -PURPOSE: (Allows clients to get and set Trick parameters) -PROGRAMMERS: (((Keith Vetter) (LinCom) (September 2001) (--))) -*/ - -#include -#include -#include -#include -#include -#include -#include - -#include "trick/parameter_types.h" -#include "trick/attributes.h" -#include "trick/bitfield_proto.h" -#include "trick/wcs_ext.h" -#include "trick/VariableServer.hh" -#include "trick/TrickConstant.hh" - -/* PROTO */ -size_t escape_str(const char *in_s, char *out_s); - -#define MAX_VAL_STRLEN 2048 - -int vs_format_ascii(Trick::VariableReference * var, char *value, size_t value_size) { - - /* for string types, return -1 if string is too big to fit in buffer (MAX_VAL_STRLEN) */ - REF2 * ref ; - ref = var->ref ; - std::string var_name = ref->reference; - - // handle returning an array - int size = 0 ; - value[0] = '\0' ; - - // data to send was copied to buffer in copy_sim_data - void * buf_ptr = var->buffer_out ; - while (size < var->size) { - size += var->ref->attr->size ; - char temp_buf[MAX_VAL_STRLEN]; - - switch (ref->attr->type) { - - case TRICK_CHARACTER: - if (ref->attr->num_index == ref->num_index) { - snprintf(temp_buf, value_size, "%d",(char)cv_convert_double(var->conversion_factor, *(char *)buf_ptr)); - } else { - /* All but last dim specified, leaves a char array */ - escape_str((char *) buf_ptr, temp_buf); - size = var->size ; - } - break; - case TRICK_UNSIGNED_CHARACTER: - if (ref->attr->num_index == ref->num_index) { - snprintf(temp_buf, value_size, "%u",(unsigned char)cv_convert_double(var->conversion_factor,*(unsigned char *)buf_ptr)); - } else { - /* All but last dim specified, leaves a char array */ - escape_str((char *) buf_ptr, temp_buf); - size = var->size ; - } - break; - - case TRICK_WCHAR:{ - if (ref->attr->num_index == ref->num_index) { - snprintf(temp_buf, value_size, "%d",*(wchar_t *) buf_ptr); - } else { - // convert wide char string char string - size_t len = wcs_to_ncs_len((wchar_t *)buf_ptr) + 1 ; - if (len > MAX_VAL_STRLEN) { - return (-1); - } - wcs_to_ncs((wchar_t *) buf_ptr, temp_buf, len); - size = var->size ; - } - } - break; - - case TRICK_STRING: - if ((char *) buf_ptr != NULL) { - escape_str((char *) buf_ptr, temp_buf); - size = var->size ; - } else { - temp_buf[0] = '\0'; - } - break; - - case TRICK_WSTRING: - if ((wchar_t *) buf_ptr != NULL) { - // convert wide char string char string - size_t len = wcs_to_ncs_len((wchar_t *)buf_ptr) + 1 ; - if (len > MAX_VAL_STRLEN) { - return (-1); - } - wcs_to_ncs((wchar_t *) buf_ptr, temp_buf, len); - size = var->size ; - } else { - temp_buf[0] = '\0'; - } - break; - -#if ( __linux | __sgi ) - case TRICK_BOOLEAN: - snprintf(temp_buf, value_size, "%d",(unsigned char)cv_convert_double(var->conversion_factor,*(unsigned char *)buf_ptr)); - break; -#endif - - case TRICK_SHORT: - snprintf(temp_buf, value_size, "%d", (short)cv_convert_double(var->conversion_factor,*(short *)buf_ptr)); - break; - - case TRICK_UNSIGNED_SHORT: - snprintf(temp_buf, value_size, "%u",(unsigned short)cv_convert_double(var->conversion_factor,*(unsigned short *)buf_ptr)); - break; - - case TRICK_INTEGER: - case TRICK_ENUMERATED: -#if ( __sun | __APPLE__ ) - case TRICK_BOOLEAN: -#endif - snprintf(temp_buf, value_size, "%d", (int)cv_convert_double(var->conversion_factor,*(int *)buf_ptr)); - break; - - case TRICK_BITFIELD: - snprintf(temp_buf, value_size, "%d", GET_BITFIELD(buf_ptr, ref->attr->size, ref->attr->index[0].start, ref->attr->index[0].size)); - break; - - case TRICK_UNSIGNED_BITFIELD: - snprintf(temp_buf, value_size, "%u", GET_UNSIGNED_BITFIELD(buf_ptr, ref->attr->size, ref->attr->index[0].start, ref->attr->index[0].size)); - break; - case TRICK_UNSIGNED_INTEGER: - snprintf(temp_buf, value_size, "%u", (unsigned int)cv_convert_double(var->conversion_factor,*(unsigned int *)buf_ptr)); - break; - - case TRICK_LONG: { - long l = *(long *)buf_ptr; - if (var->conversion_factor != cv_get_trivial()) { - l = (long)cv_convert_double(var->conversion_factor, l); - } - snprintf(temp_buf, value_size, "%ld", l); - break; - } - - case TRICK_UNSIGNED_LONG: { - unsigned long ul = *(unsigned long *)buf_ptr; - if (var->conversion_factor != cv_get_trivial()) { - ul = (unsigned long)cv_convert_double(var->conversion_factor, ul); - } - snprintf(temp_buf, value_size, "%lu", ul); - break; - } - - case TRICK_FLOAT: - snprintf(temp_buf, value_size, "%.8g", cv_convert_float(var->conversion_factor,*(float *)buf_ptr)); - break; - - case TRICK_DOUBLE: - snprintf(temp_buf, value_size, "%.16g", cv_convert_double(var->conversion_factor,*(double *)buf_ptr)); - break; - - case TRICK_LONG_LONG: { - long long ll = *(long long *)buf_ptr; - if (var->conversion_factor != cv_get_trivial()) { - ll = (long long)cv_convert_double(var->conversion_factor, ll); - } - snprintf(temp_buf, value_size, "%lld", ll); - break; - } - - case TRICK_UNSIGNED_LONG_LONG: { - unsigned long long ull = *(unsigned long long *)buf_ptr; - if (var->conversion_factor != cv_get_trivial()) { - ull = (unsigned long long)cv_convert_double(var->conversion_factor, ull); - } - snprintf(temp_buf, value_size, "%llu", ull); - break; - } - - case TRICK_NUMBER_OF_TYPES: - snprintf(temp_buf, value_size, "BAD_REF" ); - break; - - default:{ - return (-1); - } - } // end switch - - if (size < var->size) { - // if returning an array, continue array as comma separated values - strcat(temp_buf, ",") ; - buf_ptr = (void*) ((long)buf_ptr + var->ref->attr->size) ; - } - strncat(value, temp_buf, value_size - strlen(value) - 1); - } //end while - - if (ref->units) { - std::stringstream unit_str; - if ( ref->attr->mods & TRICK_MODS_UNITSDASHDASH ) { - unit_str << " {--}"; - } else { - unit_str << " {" << ref->units << "}"; - - } - size_t max_copy_size = value_size - strlen(value) - 1; - strncat(value, unit_str.str().c_str(), max_copy_size); - } - - return (0); -} - - -/** - * Escape all of the non printable characters in a string. - */ -size_t escape_str(const char *in_s, char *out_s) -{ - size_t i, in_len, out_len; - if (in_s == NULL) { - out_s[0] = '\0'; - return 0; - } - in_len = strlen(in_s); - out_len = 0; - if (out_s != NULL) { - out_s[0] = '\0'; - } - for (i = 0; i < in_len; i++) { - int ch = in_s[i]; - char work_s[6]; - - if (isprint(ch)) { - work_s[0] = ch; - work_s[1] = '\0'; - } else { - if (ch == '\a') { - snprintf(work_s, sizeof(work_s), "\\a"); - } else if (ch == '\b') { - snprintf(work_s, sizeof(work_s), "\\b"); - } else if (ch == '\f') { - snprintf(work_s, sizeof(work_s), "\\f"); - } else if (ch == '\n') { - snprintf(work_s, sizeof(work_s), "\\n"); - } else if (ch == '\r') { - snprintf(work_s, sizeof(work_s), "\\n"); - } else if (ch == '\t') { - snprintf(work_s, sizeof(work_s), "\\t"); - } else if (ch == '\v') { - snprintf(work_s, sizeof(work_s), "\\v"); - } else { - snprintf(work_s, sizeof(work_s), "\\x%02x", ch); - } - } - out_len += strlen(work_s); - if (out_s != NULL) { - if (out_len < MAX_VAL_STRLEN) { - strcat(out_s, work_s); - } else { - // indicate string is truncated because it's too big - return -1; - } - } - } - - return (out_len); -} diff --git a/trick_source/sim_services/mains/master.cpp b/trick_source/sim_services/mains/master.cpp index 72c8a46e..444cae29 100644 --- a/trick_source/sim_services/mains/master.cpp +++ b/trick_source/sim_services/mains/master.cpp @@ -44,7 +44,6 @@ int master( int nargs, char **args) { if ( ret == 0 ) { exec->loop() ; } - ret = exec->shutdown() ; //TODO: add call to free all memory from memory manager diff --git a/trick_source/trick_swig/sim_services.i b/trick_source/trick_swig/sim_services.i index 452f4874..3a839639 100644 --- a/trick_source/trick_swig/sim_services.i +++ b/trick_source/trick_swig/sim_services.i @@ -125,6 +125,9 @@ #include "trick/MessageCout.hh" #include "trick/MessageThreadedCout.hh" #include "trick/MessageFile.hh" +#include "trick/MessageHSFile.hh" +#include "trick/MessageCustomFile.hh" +#include "trick/MessageCustomManager.hh" #include "trick/MessageLCout.hh" #include "trick/MessagePublisher.hh" #include "trick/MessageSubscriber.hh" diff --git a/trick_source/trick_utils/CMakeLists.txt b/trick_source/trick_utils/CMakeLists.txt index aca9fec6..d7941c5e 100644 --- a/trick_source/trick_utils/CMakeLists.txt +++ b/trick_source/trick_utils/CMakeLists.txt @@ -23,6 +23,9 @@ add_library( trick_utils_objs OBJECT ${TRICK_UTILS_SRC} ) ############################################################### add_subdirectory(comm) +add_subdirectory(connection_handlers) add_subdirectory(math) add_subdirectory(units) +add_subdirectory(var_binary_parser) + diff --git a/trick_source/trick_utils/comm/CMakeLists.txt b/trick_source/trick_utils/comm/CMakeLists.txt index 7788e067..402f5b4b 100644 --- a/trick_source/trick_utils/comm/CMakeLists.txt +++ b/trick_source/trick_utils/comm/CMakeLists.txt @@ -21,7 +21,6 @@ set( TRICKCOMM_SRC src/tc_read src/tc_read_byteswap src/tc_set_blockio - src/tc_set_socket_priority src/tc_write src/tc_write_byteswap src/trick_bswap_buffer diff --git a/trick_source/trick_utils/comm/src/tc_accept.c b/trick_source/trick_utils/comm/src/tc_accept.c index 3fe5bc5e..2d076f27 100644 --- a/trick_source/trick_utils/comm/src/tc_accept.c +++ b/trick_source/trick_utils/comm/src/tc_accept.c @@ -42,8 +42,11 @@ int tc_accept_(TCDevice * listen_device, TCDevice * device, const char *file, in } /* Turn off data buffering. This causes data to be sent immediately rather than queing it up until the transmit - buffer is filled, and check for error conditon on set socket option */ - if (setsockopt(the_socket, IPPROTO_TCP, TCP_NODELAY, (const void *) &on, (socklen_t) sizeof(on)) < 0) { + buffer is filled. */ + setsockopt(the_socket, IPPROTO_TCP, TCP_NODELAY, (const void *) &on, (socklen_t) sizeof(on)); + + /* Check for error conditon on set socket option */ + if (the_socket < 0) { if (tc_errno == TRICKCOMM_EWOULDBLOCK) { trick_error_report(listen_device->error_handler, TRICK_ERROR_ALERT, file, diff --git a/trick_source/trick_utils/comm/src/tc_set_socket_priority.c b/trick_source/trick_utils/comm/src/tc_set_socket_priority.c deleted file mode 100644 index 5fe4cce8..00000000 --- a/trick_source/trick_utils/comm/src/tc_set_socket_priority.c +++ /dev/null @@ -1,35 +0,0 @@ - -/* - * Set the priority of a socket. - */ - -#ifndef __APPLE__ - -#ifndef __WIN32__ -#include -#include -#include -#endif - -#include "trick/tc.h" -#include "trick/tc_proto.h" -#include "trick/trick_byteswap.h" - -int tc_set_socket_priority_(TCDevice * listen_device, TCDevice * device, int priority, const char *file, int line) -{ - char client_str[TC_TAG_LENGTH + 256]; - - snprintf(client_str, sizeof(client_str), "(ID = %d tag = %s)", listen_device->client_id, listen_device->client_tag); - - if (setsockopt(device->socket, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) < 0) { - trick_error_report(listen_device->error_handler, - TRICK_ERROR_ALERT, file, - line, "tc_set_socket_priority: %s: error on set socket option: " "TC_COULD_NOT_SET_SOCKET_PRIORITY", client_str); - return (TC_COULD_NOT_SET_SOCKET_PRIORITY); - } - - return(TC_SUCCESS); - -} - -#endif diff --git a/trick_source/trick_utils/connection_handlers/ClientConnection.cpp b/trick_source/trick_utils/connection_handlers/ClientConnection.cpp new file mode 100644 index 00000000..3df752b4 --- /dev/null +++ b/trick_source/trick_utils/connection_handlers/ClientConnection.cpp @@ -0,0 +1 @@ +#include "trick/ClientConnection.hh" \ No newline at end of file diff --git a/trick_source/trick_utils/connection_handlers/Makefile b/trick_source/trick_utils/connection_handlers/Makefile new file mode 100644 index 00000000..3e66afd6 --- /dev/null +++ b/trick_source/trick_utils/connection_handlers/Makefile @@ -0,0 +1,15 @@ +TRICK_HOME := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))../../..) + +# set CONFIG_MK to allow compilation without running configure +CONFIG_MK = 1 + +include ${TRICK_HOME}/share/trick/makefiles/Makefile.common +# set the TRICK_LIB variable to create a separate library for comm +TRICK_LIB := $(TRICK_LIB_DIR)/libtrick_connection_handlers.a +include ${TRICK_HOME}/share/trick/makefiles/Makefile.tricklib +-include Makefile_deps + +TRICK_CXXFLAGS += -std=c++11 + +# make the comm library when called by the master makefile. +trick: ${TRICK_LIB} diff --git a/trick_source/trick_utils/connection_handlers/MulticastGroup.cpp b/trick_source/trick_utils/connection_handlers/MulticastGroup.cpp new file mode 100644 index 00000000..0352e374 --- /dev/null +++ b/trick_source/trick_utils/connection_handlers/MulticastGroup.cpp @@ -0,0 +1,228 @@ +#include "trick/MulticastGroup.hh" +#include +#include +#include + + +Trick::MulticastGroup::MulticastGroup() : MulticastGroup(new SystemInterface) {} + +Trick::MulticastGroup::MulticastGroup (SystemInterface * system_interface) : _initialized(false), _system_interface(system_interface) {} + +Trick::MulticastGroup::~MulticastGroup() {} + +int Trick::MulticastGroup::restart () { + // Keep address list the same, but we may need to get a new socket + _system_interface = new SystemInterface(); + // return initialize(); + return 0; +} + + +int Trick::MulticastGroup::broadcast (std::string message) { + if (!isInitialized()) { + return -1; + } + + const char * message_send = message.c_str(); + for (struct sockaddr_in& address : _addresses) { + int status = _system_interface->sendto(_socket , message_send , strlen(message_send) , 0 , (struct sockaddr *)&address , (socklen_t)sizeof(address)) ; + if (status == -1) { + char * readable_addr = inet_ntoa( address.sin_addr ); + std::string err_msg = "MulticastGroup: Failed to broadcast to address " + std::string(readable_addr); + perror(err_msg.c_str()); + } + } + return 0; +} + +int Trick::MulticastGroup::addAddress (std::string addr, int port) { + auto in_addr = _system_interface->inet_addr(addr.c_str()); + if (in_addr == -1) { + std::string error_msg = "MulticastGroup: Cannot add address " + addr + " to multicast group"; + perror(error_msg.c_str()); + return -1; + } + + struct sockaddr_in mcast_addr; + memset(&mcast_addr, 0, sizeof(mcast_addr)); + mcast_addr.sin_family = AF_INET; + mcast_addr.sin_addr.s_addr = in_addr; + mcast_addr.sin_port = htons((uint16_t) port);; + _addresses.emplace_back(mcast_addr); + return 0; +} + +bool Trick::MulticastGroup::isInitialized () { + return _initialized; +} + +int Trick::MulticastGroup::initialize() { + _socket = _system_interface->socket(AF_INET, SOCK_DGRAM, 0); + if (_socket < 0) { + perror("MulticastGroup: socket"); + return -1; + } + + int option_value = 1; + if (_system_interface->setsockopt(_socket, SOL_SOCKET, SO_REUSEADDR, (char *) &option_value, (socklen_t) sizeof(option_value)) < 0) { + perror("MulticastGroup: setsockopt: reuseaddr"); + return -1; + } +#ifdef SO_REUSEPORT + if (_system_interface->setsockopt(_socket, SOL_SOCKET, SO_REUSEPORT, (char *) &option_value, sizeof(option_value)) < 0) { + perror("MulticastGroup: setsockopt: reuseport"); + return -1; + } +#endif + _initialized = true; + return 0; +} + +int Trick::MulticastGroup::initialize_with_receiving(std::string addr, std::string mcast_addr, int port) { + UDPConnection::initialize(addr, port); + + struct ip_mreq mreq; + mreq.imr_multiaddr.s_addr = inet_addr(mcast_addr.c_str()); + mreq.imr_interface.s_addr = htonl(INADDR_ANY); + int ret = setsockopt(_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) ; + if ( ret < 0 ) { + perror("ip_add_membership") ; + return ret ; + } + + // Save our info so that we know to ignore messages from ourself + memset(&_remote_serv_addr, 0, sizeof(struct sockaddr_in)); + _remote_serv_addr.sin_family = AF_INET; + _remote_serv_addr.sin_addr.s_addr = inet_addr(mcast_addr.c_str()); + _remote_serv_addr.sin_port = htons(port); + + int s_in_size = sizeof(_self_info) ; + getsockname( _socket , (struct sockaddr *)&_self_info, (socklen_t *)&s_in_size) ; + if ( _self_info.sin_addr.s_addr == 0 ) { + char hname[80]; + struct hostent *ip_host = NULL; + gethostname(hname, (size_t) 80); + ip_host = gethostbyname(hname); + if (ip_host != NULL) { + memcpy(&(_self_info.sin_addr.s_addr), ip_host->h_addr, (size_t) ip_host->h_length); + } else { + _self_info.sin_addr.s_addr = inet_addr(addr.c_str()); + _self_info.sin_port = htons(port); + } + } + + setBlockMode(false); + + + return 0; +} + +int Trick::MulticastGroup::disconnect() { + if (_initialized) { + _system_interface->close(_socket); + } + return 0; +} + + +int Trick::MulticastGroup::write (char * message, int size) { + if (!_started) + return -1; + + socklen_t sock_size = sizeof(_remote_serv_addr); + + return _system_interface->sendto(_socket, message, size, 0, (struct sockaddr *) &_remote_serv_addr, sock_size ); +} + +int Trick::MulticastGroup::write (const std::string& message) { + if (!_started) + return -1; + + char send_buf[message.length()+1]; + strcpy (send_buf, message.c_str()); + socklen_t sock_size = sizeof(_remote_serv_addr); + + return _system_interface->sendto(_socket, send_buf, message.length(), 0, (struct sockaddr *) &_remote_serv_addr, sock_size ); +} + + +int Trick::MulticastGroup::read (std::string& message, int max_len) { + if (!_started) + return 0; + + struct sockaddr_in s_in ; + socklen_t sock_size = sizeof(s_in) ; + + char incoming_msg[max_len]; + int nbytes = _system_interface->recvfrom( _socket, incoming_msg, MAX_CMD_LEN, MSG_PEEK,(struct sockaddr *)&s_in, &sock_size ) ; + + if (nbytes == 0 ) { + return 0; + } + + if (nbytes == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + // There's nothing ready in the socket, just return an empty string + message = ""; + return 0; + } + + // Otherwise, some other system error has occurred. Return an error. + return -1; + } + + // Make sure that this message isn't from ourself + if ( s_in.sin_addr.s_addr == _self_info.sin_addr.s_addr && s_in.sin_port == _self_info.sin_port) { + // Clear out the socket + _system_interface->recvfrom( _socket, incoming_msg, MAX_CMD_LEN, 0 , 0, 0) ; + return 0; + } + + /* find the last newline that is present on the socket */ + incoming_msg[nbytes] = '\0' ; + char *last_newline = rindex( incoming_msg , '\n') ; + + /* if there is a newline then there is a complete command on the socket */ + if ( last_newline != NULL ) { + socklen_t sock_size = sizeof(_remote_serv_addr); + /* only remove up to (and including) the last newline on the socket */ + int size = last_newline - incoming_msg + 1; + nbytes = _system_interface->recvfrom( _socket, incoming_msg, size, 0 , 0, 0) ; + } else { + nbytes = 0 ; + } + + std::stringstream msg_stream; + + if ( nbytes > 0 ) { + // Strip out \r characers + + int msg_len = nbytes ; + incoming_msg[msg_len] = '\0' ; + + for( int ii = 0 , jj = 0 ; ii <= msg_len ; ii++ ) { + if ( incoming_msg[ii] != '\r' ) { + msg_stream << incoming_msg[ii] ; + } + } + } + + message = msg_stream.str(); + return message.size(); +} + +std::string Trick::MulticastGroup::getClientHostname() { + if (!_started) { + return ""; + } + + return inet_ntoa(_remote_serv_addr.sin_addr); +} + +int Trick::MulticastGroup::getClientPort() { + if (!_started) { + return 0; + } + + return ntohs(_remote_serv_addr.sin_port); +} \ No newline at end of file diff --git a/trick_source/trick_utils/connection_handlers/TCPClientListener.cpp b/trick_source/trick_utils/connection_handlers/TCPClientListener.cpp new file mode 100644 index 00000000..4b532a93 --- /dev/null +++ b/trick_source/trick_utils/connection_handlers/TCPClientListener.cpp @@ -0,0 +1,291 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "trick/TCPClientListener.hh" + +Trick::TCPClientListener::TCPClientListener () : TCPClientListener (new SystemInterface()) {} +Trick::TCPClientListener::TCPClientListener (SystemInterface * system_interface) : _listen_socket(-1), _hostname(""), _port(0), _client_tag(""), _initialized(false), _system_interface(system_interface) {} + +Trick::TCPClientListener::~TCPClientListener () { + // Clean up our socket if initialized + if (_initialized) { + close (_listen_socket); + } + + delete _system_interface; +} + +int Trick::TCPClientListener::initialize(std::string in_hostname, int in_port) { + + if ((_listen_socket = _system_interface->socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror ("Server: Unable to open socket"); + return LISTENER_ERROR; + } + + int option_val = 1; + + // Do not replicate the socket's handle if this process is forked + // Removing this will cause all kinds of problems when starting a variable server client via input file + _system_interface->fcntl(_listen_socket, F_SETFD, FD_CLOEXEC); + + // Allow socket's bound address to be used by others + if (_system_interface->setsockopt(_listen_socket, SOL_SOCKET, SO_REUSEADDR, (const char *) &option_val, (socklen_t) sizeof(option_val)) != 0) { + perror("Server: Could not set socket to reuse addr"); + _system_interface->close (_listen_socket); + return LISTENER_ERROR; + } + // Turn off data buffering on the send side + if (_system_interface->setsockopt(_listen_socket, IPPROTO_TCP, TCP_NODELAY, (const void *) &option_val, (socklen_t) sizeof(option_val)) != 0) { + perror("Server: Could not turn off data buffering"); + _system_interface->close (_listen_socket); + return LISTENER_ERROR; + } + + struct sockaddr_in s_in; + memset(&s_in, 0 , sizeof(struct sockaddr_in)) ; + + // Look up the hostname + char name[80]; + gethostname(name, (size_t) 80); + + struct hostent *ip_host ; + socklen_t s_in_size = sizeof(s_in) ; + + s_in.sin_family = AF_INET; + + if (in_hostname == "" || in_hostname == "localhost" || strcmp(in_hostname.c_str(),name) == 0) { + s_in.sin_addr.s_addr = INADDR_ANY; + _hostname = std::string(name); + } else if ( inet_pton(AF_INET, in_hostname.c_str(), (struct in_addr *)&s_in.sin_addr.s_addr) == 1 ) { + /* numeric character string address */ + _hostname = in_hostname; + } else if ( (ip_host = gethostbyname(in_hostname.c_str())) != NULL ) { + /* some name other than the default name was given */ + memcpy((void *) &(s_in.sin_addr.s_addr), (const void *) ip_host->h_addr, (size_t) ip_host->h_length); + _hostname = in_hostname; + } else { + perror("Server: Could not determine source address"); + return LISTENER_ERROR ; + } + + // Set port + s_in.sin_port = htons((short) in_port); + + // Bind to socket + if (_system_interface->bind(_listen_socket, (struct sockaddr *)&s_in, sizeof(s_in)) < 0) { + + perror("Server: Could not bind to socket"); + _system_interface->close (_listen_socket); + return LISTENER_ERROR; + } + + // Check that correct port was bound to + _system_interface->getsockname( _listen_socket , (struct sockaddr *)&s_in, &s_in_size) ; + int bound_port = ntohs(s_in.sin_port); + + if (in_port != 0 && bound_port != in_port) { + std::cerr << "Server: Could not bind to requested port " << in_port << std::endl; + _system_interface->close(_listen_socket); + return LISTENER_ERROR; + } + + // Save port number + _port = bound_port; + + + // Start listening + if (_system_interface->listen(_listen_socket, SOMAXCONN) < 0) { + std::string error_message = "Server: Could not listen on port " + std::to_string(_port); + perror (error_message.c_str()); + _system_interface->close(_listen_socket); + return LISTENER_ERROR; + } + + // Done! + _initialized = true; + return 0; +} + +int Trick::TCPClientListener::initialize() { + return initialize("", 0); +} + + +int Trick::TCPClientListener::setBlockMode(bool blocking) { + if (!_initialized) + return LISTENER_ERROR; + + int flag = _system_interface->fcntl(_listen_socket, F_GETFL, 0); + + if (flag == -1) { + std::string error_message = "Unable to get flags for fd " + std::to_string(_listen_socket) + " block mode to " + std::to_string(blocking); + perror (error_message.c_str()); + return LISTENER_ERROR; + } + + if (blocking) { + flag &= ~O_NONBLOCK; + } else { + flag |= O_NONBLOCK; + } + + if (_system_interface->fcntl(_listen_socket, F_SETFL, flag) == -1) { + std::string error_message = "Unable to set fd " + std::to_string(_listen_socket) + " block mode to " + std::to_string(blocking); + perror (error_message.c_str()); + return LISTENER_ERROR; + } + + return 0; +} + +int Trick::TCPClientListener::setSocketPriority(unsigned short priority) { + if (!_initialized) + return LISTENER_ERROR; + + #ifdef __APPLE__ + // MacOS does not provide an SO_PRIORITY option or anything analagous + std::cerr << "Socket Priority cannot be set on MacOS." << std::endl; + return LISTENER_ERROR; + + #else + if (_system_interface->setsockopt(_listen_socket, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) < 0) { + std::string error_message = "Unabled to set socket fd " + std::to_string(_listen_socket) + " to priority " + std::to_string(priority); + perror(error_message.c_str()); + return LISTENER_ERROR; + } + + return 0; + #endif +} + + +bool Trick::TCPClientListener::checkForNewConnections() { + if (!_initialized) + return false; + + fd_set rfds; + struct timeval timeout_time = { 2, 0 }; + FD_ZERO(&rfds); + FD_SET(_listen_socket, &rfds); + timeout_time.tv_sec = 2 ; + + // Listen with a timeout of 2 seconds + int result = _system_interface->select(_listen_socket + 1, &rfds, NULL, NULL, &timeout_time); + + // If there's some kind of error, just ignore it and return false + if (result < 0) { + return false; + } + + return FD_ISSET(_listen_socket, &rfds); +} + + + +std::string Trick::TCPClientListener::getHostname () { + if (!_initialized) + return ""; + + return _hostname; +} + + +int Trick::TCPClientListener::getPort() { + if (!_initialized) + return -1; + + return _port; +} + + +int Trick::TCPClientListener::disconnect() { + if (!_initialized) { + return -1; + } + + _system_interface->shutdown(_listen_socket, SHUT_RDWR); + _system_interface->close (_listen_socket); + _initialized = false; + + return 0; +} + +bool Trick::TCPClientListener::validateSourceAddress(std::string in_hostname) { + // struct addrinfo hints, *res; + // memset(&hints, 0, sizeof hints); + // hints.ai_family = AF_INET; + // hints.ai_socktype = SOCK_STREAM; + // hints.ai_protocol = 0; + + // int err; + // if ((err = _system_interface->getaddrinfo(requested_source_address.c_str(), 0, &hints, &res)) != 0) { + // std::cerr << "Unable to lookup address: " << gai_strerror(err) << std::endl; + // return false; + // } + + // Look up the hostname + char name[80]; + gethostname(name, (size_t) 80); + + struct hostent *ip_host ; + sockaddr_in s_in; + socklen_t s_in_size = sizeof(s_in) ; + + s_in.sin_family = AF_INET; + + if (in_hostname == "" || in_hostname == "localhost" || strcmp(in_hostname.c_str(),name) == 0) { + s_in.sin_addr.s_addr = INADDR_ANY; + _hostname = std::string(name); + } else if ( inet_pton(AF_INET, in_hostname.c_str(), (struct in_addr *)&s_in.sin_addr.s_addr) == 1 ) { + /* numeric character string address */ + _hostname = in_hostname; + } else if ( (ip_host = gethostbyname(in_hostname.c_str())) != NULL ) { + /* some name other than the default name was given */ + memcpy((void *) &(s_in.sin_addr.s_addr), (const void *) ip_host->h_addr, (size_t) ip_host->h_length); + _hostname = in_hostname; + } else { + perror("Server: Could not determine source address"); + return false; + } + + return true; +} + +int Trick::TCPClientListener::checkSocket() { + if (!_initialized) + return -1; + + struct sockaddr_in s_in; + socklen_t s_in_size = sizeof(s_in) ; + _system_interface->getsockname( _listen_socket , (struct sockaddr *)&s_in, &s_in_size) ; + _port = ntohs(s_in.sin_port); + + return 0; +} + +bool Trick::TCPClientListener::isInitialized() { + return _initialized; +} + +Trick::TCPConnection * Trick::TCPClientListener::setUpNewConnection () { + if (!_initialized) + return NULL; + + TCPConnection * connection = new TCPConnection(_listen_socket); + return connection; +} + +int Trick::TCPClientListener::restart () { + _system_interface = new SystemInterface(); + return 0; +} + + diff --git a/trick_source/trick_utils/connection_handlers/TCPConnection.cpp b/trick_source/trick_utils/connection_handlers/TCPConnection.cpp new file mode 100644 index 00000000..a72ef7f4 --- /dev/null +++ b/trick_source/trick_utils/connection_handlers/TCPConnection.cpp @@ -0,0 +1,201 @@ +#include "trick/TCPConnection.hh" + +#include +#include +#include +#include + +Trick::TCPConnection::TCPConnection () : TCPConnection(0, new SystemInterface()) {} + +Trick::TCPConnection::TCPConnection (SystemInterface * system_interface) : TCPConnection(0, system_interface) {} + +Trick::TCPConnection::TCPConnection (int listen_socket) : TCPConnection(listen_socket, new SystemInterface()) {} + +Trick::TCPConnection::TCPConnection (int listen_socket, SystemInterface * system_interface) : _system_interface(system_interface), _listen_socket(listen_socket), _socket(0), _connected(false) { + _connection_type = TCP; +} + +int Trick::TCPConnection::start() { + if (_listen_socket <= 0) { + return -1; + } + + // Accept a waiting connection + struct sockaddr_storage their_addr; + socklen_t addr_size = sizeof their_addr; + + if ((_socket = _system_interface->accept(_listen_socket, (struct sockaddr *)&their_addr, &addr_size)) < 0) { + perror ("Unable to accept incoming connection"); + return -1; + } + + // Set to non blocking + setBlockMode(false); + + + _connected = true; + + return 0; +} + +int Trick::TCPConnection::write (char * message, int size) { + if (!_connected) + return -1; + + return _system_interface->send(_socket, message, size, 0); +} + +int Trick::TCPConnection::write (const std::string& message) { + if (!_connected) + return -1; + + char send_buf[message.length()+1]; + strcpy (send_buf, message.c_str()); + + return _system_interface->send(_socket, send_buf, message.length(), 0); +} + +int Trick::TCPConnection::read (std::string& message, int max_len) { + if (!_connected) { + std::cerr << "Trying to read from a socket that is not connected" << std::endl; + message = ""; + return 0; + } + + char incoming_msg[max_len]; + + int max_receive_length = max_len < MAX_CMD_LEN ? max_len : MAX_CMD_LEN; + int nbytes = _system_interface->recv(_socket, incoming_msg, max_receive_length, MSG_PEEK); + + if (nbytes == 0 ) { + message = ""; + return 0; + } + + if (nbytes == -1) { + if (errno == EAGAIN) { + message = ""; + return 0; + } else { + std::string error_msg = "Error while reading from socket " + std::to_string(_socket); + perror(error_msg.c_str()); + message = ""; + return -1; + } + } + + /* find the last newline that is present on the socket */ + incoming_msg[nbytes] = '\0' ; + char *last_newline = rindex( incoming_msg , '\n') ; + + /* if there is a newline then there is a complete command on the socket */ + if ( last_newline != NULL ) { + /* only remove up to (and including) the last newline on the socket */ + int size = last_newline - incoming_msg + 1; + nbytes = _system_interface->recv( _socket, incoming_msg, size, 0) ; + } else { + nbytes = 0 ; + } + + std::stringstream msg_stream; + + if ( nbytes > 0 ) { + int msg_len = nbytes ; + incoming_msg[msg_len] = '\0' ; + + // Strip off any \r characters + for( int ii = 0 , jj = 0 ; ii <= msg_len ; ii++ ) { + if ( incoming_msg[ii] != '\r' ) { + msg_stream << incoming_msg[ii] ; + } + } + } + + message = msg_stream.str(); + return message.size(); +} + + +int Trick::TCPConnection::disconnect () { + if (!_connected) { + return -1; + } + + _system_interface->shutdown(_socket, SHUT_RDWR); + _system_interface->close (_socket); + _connected = false; + + return 0; +} + +int Trick::TCPConnection::setBlockMode(bool blocking) { + + int flag = _system_interface->fcntl(_socket, F_GETFL, 0); + + if (flag == -1) { + std::string error_message = "Unable to get flags for fd " + std::to_string(_socket); + perror (error_message.c_str()); + return -1; + } + + if (blocking) { + flag &= ~O_NONBLOCK; + } else { + flag |= O_NONBLOCK; + } + + if (_system_interface->fcntl(_socket, F_SETFL, flag) == -1) { + std::string error_message = "Unable to set fd " + std::to_string(_socket) + " block mode to " + std::to_string(blocking); + perror (error_message.c_str()); + return -1; + } + + return 0; +} + +bool Trick::TCPConnection::isInitialized() { + return _connected; +} + + +int Trick::TCPConnection::restart() { + _system_interface = new SystemInterface(); + return 0; +} + +std::string Trick::TCPConnection::getClientTag () { + return _client_tag; +} + +int Trick::TCPConnection::setClientTag (std::string tag) { + _client_tag = tag; + return 0; +} + +std::string Trick::TCPConnection::getClientHostname() { + if (!_connected) { + return ""; + } + + struct sockaddr_in otherside; + socklen_t len = (socklen_t)sizeof(otherside); + + if (getpeername(_socket, (struct sockaddr*)&otherside, &len) != 0) + return ""; + + return inet_ntoa(otherside.sin_addr); +} + +int Trick::TCPConnection::getClientPort() { + if (!_connected) { + return 0; + } + + struct sockaddr_in otherside; + socklen_t len = (socklen_t)sizeof(otherside); + + if (getpeername(_socket, (struct sockaddr*)&otherside, &len) != 0) + return 0; + + return ntohs(otherside.sin_port); +} \ No newline at end of file diff --git a/trick_source/trick_utils/connection_handlers/UDPConnection.cpp b/trick_source/trick_utils/connection_handlers/UDPConnection.cpp new file mode 100644 index 00000000..e9076977 --- /dev/null +++ b/trick_source/trick_utils/connection_handlers/UDPConnection.cpp @@ -0,0 +1,252 @@ +#include "trick/UDPConnection.hh" + +#include +#include +#include +#include +#include + +Trick::UDPConnection::UDPConnection () : UDPConnection(new SystemInterface()) {} +Trick::UDPConnection::UDPConnection (SystemInterface * system_interface) : _started(false), _initialized(false), _port(0), _hostname(""), _system_interface(system_interface), _socket(0) {} + +int Trick::UDPConnection::initialize(const std::string& in_hostname, int in_port) { + + + if ((_socket = _system_interface->socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + perror ("Server: Unable to open socket"); + return -1; + } + + int option_val = 1; + + // Do not replicate the socket's handle if this process is forked + // Removing this will cause all kinds of problems when starting a variable server client via input file + _system_interface->fcntl(_socket, F_SETFD, FD_CLOEXEC); + + // Allow socket's bound address to be used by others + if (_system_interface->setsockopt(_socket, SOL_SOCKET, SO_REUSEADDR, (const char *) &option_val, (socklen_t) sizeof(option_val)) != 0) { + perror("Server: Could not set socket to reuse addr"); + _system_interface->close (_socket); + return -1; + } + + struct sockaddr_in s_in; + memset(&s_in, 0 , sizeof(struct sockaddr_in)) ; + + // Look up the hostname + char name[80]; + gethostname(name, (size_t) 80); + + struct hostent *ip_host ; + socklen_t s_in_size = sizeof(s_in) ; + + s_in.sin_family = AF_INET; + + if (in_hostname == "" || in_hostname == "localhost" || in_hostname == "127.0.0.1" || strcmp(in_hostname.c_str(),name) == 0) { + s_in.sin_addr.s_addr = INADDR_ANY; + _hostname = std::string(name); + } else if ( inet_pton(AF_INET, in_hostname.c_str(), (struct in_addr *)&s_in.sin_addr.s_addr) == 1 ) { + /* numeric character string address */ + _hostname = in_hostname; + } else if ( (ip_host = gethostbyname(in_hostname.c_str())) != NULL ) { + /* some name other than the default name was given */ + memcpy((void *) &(s_in.sin_addr.s_addr), (const void *) ip_host->h_addr, (size_t) ip_host->h_length); + _hostname = in_hostname; + } else { + perror("UDP socket: Could not determine source address"); + return -1 ; + } + + // Set port + s_in.sin_port = htons((short) in_port); + + // Bind to socket + if (_system_interface->bind(_socket, (struct sockaddr *)&s_in, sizeof(s_in)) < 0) { + + perror("UDP socket: Could not bind to socket"); + _system_interface->close (_socket); + return -1; + } + + // Check that correct port was bound to + _system_interface->getsockname( _socket , (struct sockaddr *)&s_in, &s_in_size) ; + int bound_port = ntohs(s_in.sin_port); + + if (in_port != 0 && bound_port != in_port) { + std::cerr << "UDP socket: Could not bind to requested port " << in_port << std::endl; + _system_interface->close(_socket); + return -1; + } + + // Save port number + _port = bound_port; + + setBlockMode(false); + + // Done! + _initialized = true; + return 0; +} + +int Trick::UDPConnection::start() { + // We've already set everything up, just make sure it was successful + if (_initialized) { + _started = true; + return 0; + } + return -1; +} + +int Trick::UDPConnection::write (char * message, int size) { + if (!_started) + return -1; + + socklen_t sock_size = sizeof(_remote_serv_addr); + + return _system_interface->sendto(_socket, message, size, 0, (struct sockaddr *) &_remote_serv_addr, sock_size ); +} + +int Trick::UDPConnection::write (const std::string& message) { + if (!_started) + return -1; + + char send_buf[message.length()+1]; + strcpy (send_buf, message.c_str()); + socklen_t sock_size = sizeof(_remote_serv_addr); + + return _system_interface->sendto(_socket, send_buf, message.length(), 0, (struct sockaddr *) &_remote_serv_addr, sock_size ); +} + +int Trick::UDPConnection::read (std::string& message, int max_len) { + if (!_started) + return 0; + + char incoming_msg[max_len]; + int nbytes = _system_interface->recvfrom( _socket, incoming_msg, MAX_CMD_LEN, MSG_PEEK, NULL, NULL ) ; + if (nbytes == 0 ) { + return 0; + } + + if (nbytes == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + // There's nothing ready in the socket, just return an empty string + message = ""; + return 0; + } + + // Otherwise, some other system error has occurred. Return an error. + return -1; + } + + /* find the last newline that is present on the socket */ + incoming_msg[nbytes] = '\0' ; + char *last_newline = rindex( incoming_msg , '\n') ; + + /* if there is a newline then there is a complete command on the socket */ + if ( last_newline != NULL ) { + socklen_t sock_size = sizeof(_remote_serv_addr); + /* Save the remote host information so we know where to send replies */ + /* only remove up to (and including) the last newline on the socket */ + int size = last_newline - incoming_msg + 1; + nbytes = _system_interface->recvfrom( _socket, incoming_msg, size, 0 , (struct sockaddr *) &_remote_serv_addr, &sock_size ) ; + } else { + nbytes = 0 ; + } + + std::stringstream msg_stream; + + if ( nbytes > 0 ) { + // Strip out \r characers + + int msg_len = nbytes ; + incoming_msg[msg_len] = '\0' ; + + for( int ii = 0 , jj = 0 ; ii <= msg_len ; ii++ ) { + if ( incoming_msg[ii] != '\r' ) { + msg_stream << incoming_msg[ii] ; + } + } + } + + message = msg_stream.str(); + return message.size(); +} + +int Trick::UDPConnection::disconnect () { + if (!_initialized) { + return -1; + } + + _system_interface->shutdown(_socket, SHUT_RDWR); + _system_interface->close (_socket); + _initialized = false; + _started = false; + + return 0; +} + +int Trick::UDPConnection::setBlockMode(bool blocking) { + int flag = _system_interface->fcntl(_socket, F_GETFL, 0); + + if (flag == -1) { + std::string error_message = "Unable to get flags for fd " + std::to_string(_socket); + perror (error_message.c_str()); + return -1; + } + + if (blocking) { + flag &= ~O_NONBLOCK; + } else { + flag |= O_NONBLOCK; + } + + if (_system_interface->fcntl(_socket, F_SETFL, flag) == -1) { + std::string error_message = "Unable to set fd " + std::to_string(_socket) + " block mode to " + std::to_string(blocking); + perror (error_message.c_str()); + return -1; + } + + return 0; +} + +int Trick::UDPConnection::restart() { + _system_interface = new SystemInterface(); + return 0; +} + +bool Trick::UDPConnection::isInitialized() { + return _started; +} + +int Trick::UDPConnection::getPort() { + return _port; +} + +std::string Trick::UDPConnection::getHostname() { + return _hostname; +} + +std::string Trick::UDPConnection::getClientTag () { + return _client_tag; +} + +int Trick::UDPConnection::setClientTag (std::string tag) { + _client_tag = tag; + return 0; +} + +std::string Trick::UDPConnection::getClientHostname() { + if (!_initialized) { + return ""; + } + + return inet_ntoa(_remote_serv_addr.sin_addr); +} + +int Trick::UDPConnection::getClientPort() { + if (!_initialized) { + return 0; + } + + return ntohs(_remote_serv_addr.sin_port); +} \ No newline at end of file diff --git a/trick_source/trick_utils/connection_handlers/test/.gitignore b/trick_source/trick_utils/connection_handlers/test/.gitignore new file mode 100644 index 00000000..05c5c19e --- /dev/null +++ b/trick_source/trick_utils/connection_handlers/test/.gitignore @@ -0,0 +1 @@ +comm_test \ No newline at end of file diff --git a/trick_source/trick_utils/connection_handlers/test/Makefile b/trick_source/trick_utils/connection_handlers/test/Makefile new file mode 100644 index 00000000..0047620a --- /dev/null +++ b/trick_source/trick_utils/connection_handlers/test/Makefile @@ -0,0 +1,38 @@ + +include ${TRICK_HOME}/share/trick/makefiles/Makefile.common +include ${TRICK_HOME}/share/trick/makefiles/Makefile.tricklib + +INCLUDE_DIRS = -I$(GTEST_HOME) -I${GTEST_HOME}/include -I$(TRICK_HOME)/include + +# Use the libtrick_connection_handlers library only. libtrick.a would also work. +TRICK_LIBS := ${TRICK_LIB_DIR}/libtrick_connection_handlers.a +TRICK_EXEC_LINK_LIBS += -L${GTEST_HOME}/lib64 -L${GTEST_HOME}/lib -lgtest -lgtest_main +TRICK_CXXFLAGS += ${INCLUDE_DIRS} -g -Wall -Wextra ${TRICK_SYSTEM_CXXFLAGS} ${TRICK_TEST_FLAGS} + +# Automatically determine all executable names produced by this Makefile. +TESTS = comm_test + +# Objects that we depend on +DEPENDS = ../object_*/*.o + +TRICK_CXXFLAGS += -Wno-unused-parameter + +default : all + +all : $(TESTS) + +# Set XML test results name +test : all + @ for i in $(TESTS) ; do \ + ./$$i --gtest_output=xml:${TRICK_HOME}/trick_test/ConnectionHandlers.xml; \ + done + +clean : clean_test + +clean_test : + $(RM) -rf $(TESTS) + + +$(TESTS) : $(CPP_OBJS) $(DEPENDS) + $(TRICK_CXX) $(TRICK_CXXFLAGS) $^ $(TRICK_EXEC_LINK_LIBS) -o $@ $(SYSCALL_MOCK_LINKFLAGS) + diff --git a/trick_source/trick_utils/connection_handlers/test/MulticastGroup_test.cpp b/trick_source/trick_utils/connection_handlers/test/MulticastGroup_test.cpp new file mode 100644 index 00000000..a664f9c9 --- /dev/null +++ b/trick_source/trick_utils/connection_handlers/test/MulticastGroup_test.cpp @@ -0,0 +1,136 @@ +#include + +#include "trick/MulticastGroup.hh" +#include "SystemInterfaceMock/SystemInterfaceMock.hh" + +class MulticastGroupTest : public testing::Test { + + protected: + MulticastGroupTest() : system_context(new SystemInferfaceMock()), mcast (system_context) {} + ~MulticastGroupTest(){ + mcast.disconnect(); + } + + SystemInferfaceMock * system_context; + Trick::MulticastGroup mcast; +}; + +TEST_F(MulticastGroupTest, not_initialized) { + // ARRANGE + // ACT + // ASSERT + EXPECT_EQ(mcast.isInitialized(), 0); +} + +TEST_F(MulticastGroupTest, initialize) { + // ARRANGE + // ACT + mcast.initialize(); + // ASSERT + EXPECT_EQ(mcast.isInitialized(), 1); +} + +TEST_F(MulticastGroupTest, initialize_socket_fails) { + // ARRANGE + system_context->register_socket_impl([](int a, int b, int c) { + errno = EPERM; + return -1; + }); + + // ACT + mcast.initialize(); + // ASSERT + EXPECT_EQ(mcast.isInitialized(), 0); +} + +TEST_F(MulticastGroupTest, initialize_sockopt_reuseaddr_fails) { + // ARRANGE + system_context->register_setsockopt_impl([](int sockfd, int level, int optname, const void *optval, socklen_t optlen) { + if (optname == SO_REUSEADDR) { + errno = EINVAL; + return -1; + } + return 0; + }); + + // ACT + mcast.initialize(); + // ASSERT + EXPECT_EQ(mcast.isInitialized(), 0); +} + +TEST_F(MulticastGroupTest, initialize_sockopt_reuseport_fails) { + // ARRANGE + system_context->register_setsockopt_impl([](int sockfd, int level, int optname, const void *optval, socklen_t optlen) { + if (optname == SO_REUSEPORT) { + errno = EINVAL; + return -1; + } + return 0; + }); + + // ACT + mcast.initialize(); + // ASSERT + EXPECT_EQ(mcast.isInitialized(), 0); +} + +TEST_F(MulticastGroupTest, add_address) { + // ARRANGE + // ACT + int result = mcast.addAddress("239.3.14.15", 9265); + + // ASSERT + EXPECT_EQ(result, 0); +} + +TEST_F(MulticastGroupTest, add_address_fails) { + // ARRANGE + system_context->register_inet_addr_impl([](const char * addr) { + return -1; + }); + + // ACT + int result = mcast.addAddress("239.3.14.15", 9265); + + // ASSERT + EXPECT_EQ(result, -1); +} + +TEST_F(MulticastGroupTest, broadcast_uninitialized) { + // ARRANGE + // ACT + int result = mcast.broadcast("Some message"); + + // ASSERT + EXPECT_EQ(result, -1); +} + +TEST_F(MulticastGroupTest, broadcast) { + // ARRANGE + mcast.initialize(); + mcast.addAddress("239.3.14.15", 9265); + + // ACT + int result = mcast.broadcast("Some message"); + + // ASSERT + EXPECT_EQ(result, 0); +} + + +TEST_F(MulticastGroupTest, broadcast_send_fails) { + // ARRANGE + system_context->register_sendto_impl([](int socket, const void * buffer, size_t length, int flags, const struct sockaddr * dest_addr, socklen_t dest_len) { + return -1; + }); + + mcast.initialize(); + mcast.addAddress("239.3.14.15", 9265); + + // ACT + int result = mcast.broadcast("Some message"); + + // ASSERT + EXPECT_EQ(result, 0); +} \ No newline at end of file diff --git a/trick_source/trick_utils/connection_handlers/test/SystemInterfaceMock/SystemInterfaceMock.hh b/trick_source/trick_utils/connection_handlers/test/SystemInterfaceMock/SystemInterfaceMock.hh new file mode 100644 index 00000000..5d3b8f6f --- /dev/null +++ b/trick_source/trick_utils/connection_handlers/test/SystemInterfaceMock/SystemInterfaceMock.hh @@ -0,0 +1,252 @@ + +#ifndef __SYSTEM_INTERFACE_MOCK__ +#define __SYSTEM_INTERFACE_MOCK__ + + +#include "trick/SystemInterface.hh" + + + +typedef std::function socket_func_type; + +typedef std::function setsockopt_func_type; + +typedef std::function bind_func_type; + +typedef std::function getsockname_func_type; + +typedef std::function listen_func_type; + +typedef std::function select_func_type; + +typedef std::function close_func_type; + +typedef std::function getaddrinfo_func_type; + +typedef std::function fcntl_func_type; + +typedef std::function shutdown_func_type; + +typedef std::function accept_func_type; + +typedef std::function send_func_type; + +typedef std::function sendto_func_type; + +typedef std::function recv_func_type; + +typedef std::function recvfrom_func_type; + +typedef std::function inet_addr_func_type; + + +class SystemInferfaceMock : public SystemInterface { + public: + SystemInferfaceMock () { + + real_socket_impl(); + real_setsockopt_impl(); + real_bind_impl(); + real_getsockname_impl(); + real_listen_impl(); + real_select_impl(); + real_close_impl(); + real_getaddrinfo_impl(); + real_fcntl_impl(); + real_shutdown_impl(); + real_accept_impl(); + real_send_impl(); + real_sendto_impl(); + real_recv_impl(); + real_recvfrom_impl(); + real_inet_addr_impl(); + } + + void set_all_real () { + + real_socket_impl(); + real_setsockopt_impl(); + real_bind_impl(); + real_getsockname_impl(); + real_listen_impl(); + real_select_impl(); + real_close_impl(); + real_getaddrinfo_impl(); + real_fcntl_impl(); + real_shutdown_impl(); + real_accept_impl(); + real_send_impl(); + real_sendto_impl(); + real_recv_impl(); + real_recvfrom_impl(); + real_inet_addr_impl(); + } + + void set_all_noop() { + + noop_socket_impl(); + noop_setsockopt_impl(); + noop_bind_impl(); + noop_getsockname_impl(); + noop_listen_impl(); + noop_select_impl(); + noop_close_impl(); + noop_getaddrinfo_impl(); + noop_fcntl_impl(); + noop_shutdown_impl(); + noop_accept_impl(); + noop_send_impl(); + noop_sendto_impl(); + noop_recv_impl(); + noop_recvfrom_impl(); + noop_inet_addr_impl(); + } + + + // socket implementation + public: + virtual int socket (int domain, int type, int protocol) override { return socket_impl( domain, type, protocol); } + void register_socket_impl (socket_func_type impl) { socket_impl = impl; } + void real_socket_impl () { socket_impl = [](int domain, int type, int protocol) -> int { return ::socket( domain, type, protocol); }; } + void noop_socket_impl () { socket_impl = [](int domain, int type, int protocol) -> int { return 0; }; } + private: + socket_func_type socket_impl; + + // setsockopt implementation + public: + virtual int setsockopt (int socket, int level, int option_name, const void * option_value, socklen_t option_len) override { return setsockopt_impl( socket, level, option_name, option_value, option_len); } + void register_setsockopt_impl (setsockopt_func_type impl) { setsockopt_impl = impl; } + void real_setsockopt_impl () { setsockopt_impl = [](int socket, int level, int option_name, const void * option_value, socklen_t option_len) -> int { return ::setsockopt( socket, level, option_name, option_value, option_len); }; } + void noop_setsockopt_impl () { setsockopt_impl = [](int socket, int level, int option_name, const void * option_value, socklen_t option_len) -> int { return 0; }; } + private: + setsockopt_func_type setsockopt_impl; + + // bind implementation + public: + virtual int bind (int socket, const struct sockaddr * address, socklen_t address_len) override { return bind_impl( socket, address, address_len); } + void register_bind_impl (bind_func_type impl) { bind_impl = impl; } + void real_bind_impl () { bind_impl = [](int socket, const struct sockaddr * address, socklen_t address_len) -> int { return ::bind( socket, address, address_len); }; } + void noop_bind_impl () { bind_impl = [](int socket, const struct sockaddr * address, socklen_t address_len) -> int { return 0; }; } + private: + bind_func_type bind_impl; + + // getsockname implementation + public: + virtual int getsockname (int socket, struct sockaddr * address, socklen_t * address_len) override { return getsockname_impl( socket, address, address_len); } + void register_getsockname_impl (getsockname_func_type impl) { getsockname_impl = impl; } + void real_getsockname_impl () { getsockname_impl = [](int socket, struct sockaddr * address, socklen_t * address_len) -> int { return ::getsockname( socket, address, address_len); }; } + void noop_getsockname_impl () { getsockname_impl = [](int socket, struct sockaddr * address, socklen_t * address_len) -> int { return 0; }; } + private: + getsockname_func_type getsockname_impl; + + // listen implementation + public: + virtual int listen (int socket, int backlog) override { return listen_impl( socket, backlog); } + void register_listen_impl (listen_func_type impl) { listen_impl = impl; } + void real_listen_impl () { listen_impl = [](int socket, int backlog) -> int { return ::listen( socket, backlog); }; } + void noop_listen_impl () { listen_impl = [](int socket, int backlog) -> int { return 0; }; } + private: + listen_func_type listen_impl; + + // select implementation + public: + virtual int select (int nfds, fd_set * readfds, fd_set * writefds, fd_set * errorfds, struct timeval * timeout) override { return select_impl( nfds, readfds, writefds, errorfds, timeout); } + void register_select_impl (select_func_type impl) { select_impl = impl; } + void real_select_impl () { select_impl = [](int nfds, fd_set * readfds, fd_set * writefds, fd_set * errorfds, struct timeval * timeout) -> int { return ::select( nfds, readfds, writefds, errorfds, timeout); }; } + void noop_select_impl () { select_impl = [](int nfds, fd_set * readfds, fd_set * writefds, fd_set * errorfds, struct timeval * timeout) -> int { return 0; }; } + private: + select_func_type select_impl; + + // close implementation + public: + virtual int close (int fildes) override { return close_impl( fildes); } + void register_close_impl (close_func_type impl) { close_impl = impl; } + void real_close_impl () { close_impl = [](int fildes) -> int { return ::close( fildes); }; } + void noop_close_impl () { close_impl = [](int fildes) -> int { return 0; }; } + private: + close_func_type close_impl; + + // getaddrinfo implementation + public: + virtual int getaddrinfo (const char * hostname, const char * servname, const struct addrinfo * hints, struct addrinfo ** res) override { return getaddrinfo_impl( hostname, servname, hints, res); } + void register_getaddrinfo_impl (getaddrinfo_func_type impl) { getaddrinfo_impl = impl; } + void real_getaddrinfo_impl () { getaddrinfo_impl = [](const char * hostname, const char * servname, const struct addrinfo * hints, struct addrinfo ** res) -> int { return ::getaddrinfo( hostname, servname, hints, res); }; } + void noop_getaddrinfo_impl () { getaddrinfo_impl = [](const char * hostname, const char * servname, const struct addrinfo * hints, struct addrinfo ** res) -> int { return 0; }; } + private: + getaddrinfo_func_type getaddrinfo_impl; + + // fcntl implementation + public: + virtual int fcntl (int fildes, int cmd, int arg) override { return fcntl_impl( fildes, cmd, arg); } + void register_fcntl_impl (fcntl_func_type impl) { fcntl_impl = impl; } + void real_fcntl_impl () { fcntl_impl = [](int fildes, int cmd, int arg) -> int { return ::fcntl( fildes, cmd, arg); }; } + void noop_fcntl_impl () { fcntl_impl = [](int fildes, int cmd, int arg) -> int { return 0; }; } + private: + fcntl_func_type fcntl_impl; + + // shutdown implementation + public: + virtual int shutdown (int socket, int how) override { return shutdown_impl( socket, how); } + void register_shutdown_impl (shutdown_func_type impl) { shutdown_impl = impl; } + void real_shutdown_impl () { shutdown_impl = [](int socket, int how) -> int { return ::shutdown( socket, how); }; } + void noop_shutdown_impl () { shutdown_impl = [](int socket, int how) -> int { return 0; }; } + private: + shutdown_func_type shutdown_impl; + + // accept implementation + public: + virtual int accept (int socket, struct sockaddr * address, socklen_t * address_len) override { return accept_impl( socket, address, address_len); } + void register_accept_impl (accept_func_type impl) { accept_impl = impl; } + void real_accept_impl () { accept_impl = [](int socket, struct sockaddr * address, socklen_t * address_len) -> int { return ::accept( socket, address, address_len); }; } + void noop_accept_impl () { accept_impl = [](int socket, struct sockaddr * address, socklen_t * address_len) -> int { return 0; }; } + private: + accept_func_type accept_impl; + + // send implementation + public: + virtual ssize_t send (int socket, const void * buffer, size_t length, int flags) override { return send_impl( socket, buffer, length, flags); } + void register_send_impl (send_func_type impl) { send_impl = impl; } + void real_send_impl () { send_impl = [](int socket, const void * buffer, size_t length, int flags) -> ssize_t { return ::send( socket, buffer, length, flags); }; } + void noop_send_impl () { send_impl = [](int socket, const void * buffer, size_t length, int flags) -> ssize_t { return 0; }; } + private: + send_func_type send_impl; + + // sendto implementation + public: + virtual ssize_t sendto (int socket, const void * buffer, size_t length, int flags, const struct sockaddr * dest_addr, socklen_t dest_len) override { return sendto_impl( socket, buffer, length, flags, dest_addr, dest_len); } + void register_sendto_impl (sendto_func_type impl) { sendto_impl = impl; } + void real_sendto_impl () { sendto_impl = [](int socket, const void * buffer, size_t length, int flags, const struct sockaddr * dest_addr, socklen_t dest_len) -> ssize_t { return ::sendto( socket, buffer, length, flags, dest_addr, dest_len); }; } + void noop_sendto_impl () { sendto_impl = [](int socket, const void * buffer, size_t length, int flags, const struct sockaddr * dest_addr, socklen_t dest_len) -> ssize_t { return 0; }; } + private: + sendto_func_type sendto_impl; + + // recv implementation + public: + virtual ssize_t recv (int socket, void * buffer, size_t length, int flags) override { return recv_impl( socket, buffer, length, flags); } + void register_recv_impl (recv_func_type impl) { recv_impl = impl; } + void real_recv_impl () { recv_impl = [](int socket, void * buffer, size_t length, int flags) -> ssize_t { return ::recv( socket, buffer, length, flags); }; } + void noop_recv_impl () { recv_impl = [](int socket, void * buffer, size_t length, int flags) -> ssize_t { return 0; }; } + private: + recv_func_type recv_impl; + + // recvfrom implementation + public: + virtual ssize_t recvfrom (int socket, void * buffer, size_t length, int flags, struct sockaddr * address, socklen_t * address_len) override { return recvfrom_impl( socket, buffer, length, flags, address, address_len); } + void register_recvfrom_impl (recvfrom_func_type impl) { recvfrom_impl = impl; } + void real_recvfrom_impl () { recvfrom_impl = [](int socket, void * buffer, size_t length, int flags, struct sockaddr * address, socklen_t * address_len) -> ssize_t { return ::recvfrom( socket, buffer, length, flags, address, address_len); }; } + void noop_recvfrom_impl () { recvfrom_impl = [](int socket, void * buffer, size_t length, int flags, struct sockaddr * address, socklen_t * address_len) -> ssize_t { return 0; }; } + private: + recvfrom_func_type recvfrom_impl; + + // inet_addr implementation + public: + virtual in_addr_t inet_addr (const char * cp) override { return inet_addr_impl( cp); } + void register_inet_addr_impl (inet_addr_func_type impl) { inet_addr_impl = impl; } + void real_inet_addr_impl () { inet_addr_impl = [](const char * cp) -> in_addr_t { return ::inet_addr( cp); }; } + void noop_inet_addr_impl () { inet_addr_impl = [](const char * cp) -> in_addr_t { return 0; }; } + private: + inet_addr_func_type inet_addr_impl; + +}; + +#endif diff --git a/trick_source/trick_utils/connection_handlers/test/TCPClientListener_test.cpp b/trick_source/trick_utils/connection_handlers/test/TCPClientListener_test.cpp new file mode 100644 index 00000000..7a097a48 --- /dev/null +++ b/trick_source/trick_utils/connection_handlers/test/TCPClientListener_test.cpp @@ -0,0 +1,448 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "trick/TCPClientListener.hh" +#include "SystemInterfaceMock/SystemInterfaceMock.hh" + + +class TCPClientListenerTest : public testing::Test { + + protected: + TCPClientListenerTest() : system_context(new SystemInferfaceMock()), listener (system_context) {} + ~TCPClientListenerTest(){} + + SystemInferfaceMock * system_context; + Trick::TCPClientListener listener; +}; + + +TEST_F( TCPClientListenerTest, initialized ) { + EXPECT_EQ(listener.isInitialized(), false); +} + +TEST_F( TCPClientListenerTest, initialize_localhost_0 ) { + // ARRANGE + // Look up the hostname + char name[80]; + gethostname(name, (size_t) 80); + + // ACT + listener.initialize("localhost", 0); + + // ASSERT + EXPECT_EQ(listener.isInitialized(), true); + EXPECT_EQ(listener.getHostname(), std::string(name)); +} + +TEST_F( TCPClientListenerTest, initialize_localhost_54321 ) { + // ARRANGE + // ACT + listener.initialize("localhost", 54321); + + // ASSERT + EXPECT_EQ(listener.isInitialized(), true); + EXPECT_EQ(listener.getPort(), 54321); +} + +TEST_F( TCPClientListenerTest, initialize_no_args ) { + // ARRANGE + // ACT + listener.initialize(); + + // ASSERT + EXPECT_EQ(listener.isInitialized(), true); + EXPECT_GT(listener.getPort(), 1000); +} + +TEST_F( TCPClientListenerTest, initialize_localhost_numerical_54321 ) { + // ARRANGE + // ACT + listener.initialize("127.0.0.1", 54321); + + // ASSERT + EXPECT_EQ(listener.isInitialized(), true); + EXPECT_EQ(listener.getPort(), 54321); +} + +TEST_F( TCPClientListenerTest, initialize_invalid_hostname ) { + // ARRANGE + // ACT + listener.initialize("some_invalid_hostname", 0); + + // ASSERT + EXPECT_EQ(listener.isInitialized(), false); +} + +TEST_F( TCPClientListenerTest, failed_socket ) { + // ARRANGE + system_context->register_socket_impl([](int a, int b, int c) { + errno = EPERM; + return -1; + }); + + // ACT + listener.initialize("localhost", 54321); + + // ASSERT + EXPECT_EQ(listener.isInitialized(), false); +} + +TEST_F( TCPClientListenerTest, failed_setsockopt_reuseaddr ) { + // ARRANGE + system_context->register_setsockopt_impl([](int sockfd, int level, int optname, const void *optval, socklen_t optlen) { + errno = EINVAL; + return -1; + }); + + // ACT + listener.initialize("localhost", 54321); + + // ASSERT + EXPECT_EQ(listener.isInitialized(), false); +} + +TEST_F( TCPClientListenerTest, failed_setsockopt_buffering ) { + // ARRANGE + system_context->register_setsockopt_impl([](int sockfd, int level, int optname, const void *optval, socklen_t optlen) { + if (level == IPPROTO_TCP && optname == TCP_NODELAY) { + errno = ENOTSOCK; + return -1; + } + + return 0; + }); + + // ACT + listener.initialize("localhost", 54321); + + // ASSERT + EXPECT_EQ(listener.isInitialized(), false); +} + +TEST_F( TCPClientListenerTest, failed_bind ) { + // ARRANGE + system_context->register_bind_impl([](int sockfd, const struct sockaddr *addr,socklen_t addrlen) { + errno = EADDRINUSE; + return -1; + }); + + // ACT + listener.initialize("localhost", 54321); + + // ASSERT + EXPECT_EQ(listener.isInitialized(), false); +} + +TEST_F( TCPClientListenerTest, failed_sockname ) { + // ARRANGE + system_context->register_getsockname_impl([](int sockfd, struct sockaddr *addr, socklen_t *addrlen) { + ((struct sockaddr_in *) addr)->sin_port = htons(1234); + return 0; + }); + + // ACT + listener.initialize("localhost", 54321); + + // ASSERT + EXPECT_EQ(listener.isInitialized(), false); +} + +TEST_F( TCPClientListenerTest, failed_listen ) { + // ARRANGE + system_context->register_listen_impl([](int sockfd, int backlog) { + errno = EADDRINUSE; + return -1; + }); + + // ACT + listener.initialize("localhost", 54321); + + // ASSERT + EXPECT_EQ(listener.isInitialized(), false); +} + +TEST_F( TCPClientListenerTest, checkForNewConnections_uninitialized ) { + // ARRANGE + // ACT + bool result = listener.checkForNewConnections(); + + // ASSERT + EXPECT_EQ(result, false); +} + +TEST_F( TCPClientListenerTest, checkForNewConnections ) { + // ARRANGE + int socket_fd; + system_context->register_socket_impl([&socket_fd](int a, int b, int c) { + socket_fd = ::socket(a, b, c); + return socket_fd; + }); + system_context->register_select_impl([&socket_fd](int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout) { + FD_SET(socket_fd, readfds); + return 1; + }); + listener.initialize(); + + // ACT + bool result = listener.checkForNewConnections(); + + // ASSERT + EXPECT_EQ(result, true); +} + +TEST_F( TCPClientListenerTest, checkForNewConnections_select_error ) { + // ARRANGE + system_context->register_select_impl([](int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout) { + return -1; + }); + listener.initialize(); + + // ACT + bool result = listener.checkForNewConnections(); + + // ASSERT + EXPECT_EQ(result, false); +} + +TEST_F( TCPClientListenerTest, setBlockMode_false ) { + // ARRANGE + int socket_fd; + system_context->register_socket_impl([&socket_fd](int a, int b, int c) { + socket_fd = ::socket(a, b, c); + return socket_fd; + }); + listener.initialize(); + + // ACT + int status = listener.setBlockMode(false); + + // ASSERT + EXPECT_EQ(status, 0); + + int flag = fcntl(socket_fd, F_GETFL, 0); + EXPECT_TRUE(flag & O_NONBLOCK); +} + +TEST_F( TCPClientListenerTest, setBlockMode_nonblocking) { + // ARRANGE + int socket_fd; + system_context->register_socket_impl([&socket_fd](int a, int b, int c) { + socket_fd = ::socket(a, b, c); + return socket_fd; + }); + listener.initialize(); + + // ACT + int status = listener.setBlockMode(false); + + // ASSERT + EXPECT_EQ(status, 0); + + int flag = fcntl(socket_fd, F_GETFL, 0); + EXPECT_TRUE(flag & O_NONBLOCK); +} + + +TEST_F( TCPClientListenerTest, setBlockMode_blocking) { + // ARRANGE + int socket_fd; + system_context->register_socket_impl([&socket_fd](int a, int b, int c) { + socket_fd = ::socket(a, b, c); + return socket_fd; + }); + listener.initialize(); + + // ACT + int status = listener.setBlockMode(true); + + // ASSERT + EXPECT_EQ(status, 0); + + int flag = fcntl(socket_fd, F_GETFL, 0); + EXPECT_FALSE(flag & O_NONBLOCK); +} + +TEST_F( TCPClientListenerTest, setBlockMode_fcntl_getfl_fail) { + // ARRANGE + int socket_fd; + system_context->register_socket_impl([&socket_fd](int a, int b, int c) { + socket_fd = ::socket(a, b, c); + return socket_fd; + }); + system_context->register_fcntl_impl([](int a, int b, int c) { + errno = EACCES; + return -1; + }); + listener.initialize(); + + // ACT + int status = listener.setBlockMode(true); + + // ASSERT + EXPECT_EQ(status, -1); +} + +TEST_F( TCPClientListenerTest, setBlockMode_fcntl_setfl_fail) { + // ARRANGE + int socket_fd; + system_context->register_socket_impl([&socket_fd](int a, int b, int c) { + socket_fd = ::socket(a, b, c); + return socket_fd; + }); + system_context->register_fcntl_impl([](int a, int cmd, int c) { + if (cmd == F_SETFL) { + errno = EBADF; + return -1; + } + return 0; + }); + + listener.initialize(); + + // ACT + int status = listener.setBlockMode(true); + + // ASSERT + EXPECT_EQ(status, -1); +} + + +TEST_F( TCPClientListenerTest, validateSourceAddress_localhost) { + // ARRANGE + // ACT + bool status = listener.validateSourceAddress("localhost"); + + // ASSERT + EXPECT_EQ(status, true); +} + +TEST_F( TCPClientListenerTest, validateSourceAddress_junk) { + // ARRANGE + // ACT + bool status = listener.validateSourceAddress("alsdkfjgalkdj"); + + // ASSERT + EXPECT_EQ(status, false); +} + +TEST_F( TCPClientListenerTest, checkSocket) { + // ARRANGE + listener.initialize(); + int port = listener.getPort(); + + // ACT + int status = listener.checkSocket(); + + // ASSERT + EXPECT_EQ(status, 0); + EXPECT_EQ(listener.getPort(), port); +} + +TEST_F( TCPClientListenerTest, checkSocket_uninitialized) { + // ARRANGE + // ACT + int status = listener.checkSocket(); + + // ASSERT + EXPECT_EQ(status, -1); +} + +TEST_F( TCPClientListenerTest, setPriority ) { + // ARRANGE + system_context->register_setsockopt_impl([](int sockfd, int level, int optname, const void *optval, socklen_t optlen) { + return 0; + }); + listener.initialize(); + + // ACT + int result = listener.setSocketPriority(5); + + // ASSERT + #ifdef __APPLE__ + // Always fails on MacOS + EXPECT_EQ(-1, result); + #else + EXPECT_EQ(0, result); + #endif +} + +TEST_F( TCPClientListenerTest, setPriorityFails ) { + // ARRANGE + system_context->register_setsockopt_impl([](int sockfd, int level, int optname, const void *optval, socklen_t optlen) { + errno = EINVAL; + return -1; + }); + listener.initialize(); + + // ACT + int result = listener.setSocketPriority(5); + + // ASSERT + EXPECT_EQ(-1, result); +} + +TEST_F( TCPClientListenerTest, uninitialized ) { + // ARRANGE + system_context->register_setsockopt_impl([](int sockfd, int level, int optname, const void *optval, socklen_t optlen) { + errno = EINVAL; + return -1; + }); + + // ACT + int result = listener.setSocketPriority(5); + + // ASSERT + EXPECT_EQ(-1, result); +} + +TEST_F( TCPClientListenerTest, disconnect) { + // ARRANGE + listener.initialize(); + + // ACT + int status = listener.disconnect(); + + // ASSERT + EXPECT_EQ(status, 0); +} + +TEST_F( TCPClientListenerTest, disconnect_uninitialized) { + // ARRANGE + // ACT + int status = listener.disconnect(); + + // ASSERT + EXPECT_EQ(status, -1); +} + +TEST_F( TCPClientListenerTest, setupNewConnection) { + // ARRANGE + listener.initialize(); + + // ACT + Trick::TCPConnection * connection = listener.setUpNewConnection(); + + // ASSERT + EXPECT_TRUE(connection != NULL); +} + +TEST_F( TCPClientListenerTest, setupNewConnection_uninitialized) { + // ARRANGE + + // ACT + Trick::TCPConnection * connection = listener.setUpNewConnection(); + + // ASSERT + EXPECT_TRUE(connection == NULL); +} \ No newline at end of file diff --git a/trick_source/trick_utils/connection_handlers/test/TCPConnection_test.cpp b/trick_source/trick_utils/connection_handlers/test/TCPConnection_test.cpp new file mode 100644 index 00000000..2494a55d --- /dev/null +++ b/trick_source/trick_utils/connection_handlers/test/TCPConnection_test.cpp @@ -0,0 +1,379 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "trick/TCPConnection.hh" +#include "SystemInterfaceMock/SystemInterfaceMock.hh" + + +class TCPConnectionTest : public testing::Test { + + protected: + TCPConnectionTest() : system_context(new SystemInferfaceMock()), connection (fake_listen_socket_fd, system_context) {} + ~TCPConnectionTest(){} + + SystemInferfaceMock * system_context; + Trick::TCPConnection connection; + const static int fake_listen_socket_fd; +}; + +const int TCPConnectionTest::fake_listen_socket_fd = 5; + +TEST_F( TCPConnectionTest, initialized ) { + // ARRANGE + system_context->register_accept_impl([&](int socket, struct sockaddr * address, socklen_t * address_len) -> int { + return 6; + }); + system_context->noop_fcntl_impl(); + + // ACT + int result = connection.start(); + + // ASSERT + ASSERT_EQ(result, 0); +} + +TEST_F( TCPConnectionTest, initialized_accept_failure ) { + // ARRANGE + system_context->register_accept_impl([&](int socket, struct sockaddr * address, socklen_t * address_len) -> int { + errno = EWOULDBLOCK; + return -1; + }); + + // ACT + int result = connection.start(); + + // ASSERT + ASSERT_EQ(result, -1); +} + + +TEST_F( TCPConnectionTest, setBlockMode_nonblocking) { + // ARRANGE + int socket_fd; + system_context->register_accept_impl([&](int socket, struct sockaddr * address, socklen_t * address_len) -> int { + socket_fd = ::socket(AF_INET, SOCK_STREAM, 0); + return socket_fd; + }); + connection.start(); + + // ACT + int status = connection.setBlockMode(false); + + // ASSERT + EXPECT_EQ(status, 0); + + int flag = fcntl(socket_fd, F_GETFL, 0); + EXPECT_TRUE(flag & O_NONBLOCK); + + close(socket_fd); +} + + +TEST_F( TCPConnectionTest, setBlockMode_blocking) { +// ARRANGE + int socket_fd; + system_context->register_accept_impl([&](int socket, struct sockaddr * address, socklen_t * address_len) -> int { + socket_fd = ::socket(AF_INET, SOCK_STREAM, 0); + return socket_fd; + }); + connection.start(); + + // ACT + int status = connection.setBlockMode(true); + + // ASSERT + EXPECT_EQ(status, 0); + + int flag = fcntl(socket_fd, F_GETFL, 0); + EXPECT_FALSE(flag & O_NONBLOCK); + + close(socket_fd); +} + +TEST_F( TCPConnectionTest, setBlockMode_fcntl_getfl_fail) { + // ARRANGE + int socket_fd; + system_context->register_accept_impl([&](int socket, struct sockaddr * address, socklen_t * address_len) -> int { + socket_fd = ::socket(AF_INET, SOCK_STREAM, 0); + return socket_fd; + }); + connection.start(); + system_context->register_fcntl_impl([](int a, int b, int c) { + errno = EACCES; + return -1; + }); + // ACT + + int status = connection.setBlockMode(true); + + // ASSERT + EXPECT_EQ(status, -1); + + close(socket_fd); +} + +TEST_F( TCPConnectionTest, setBlockMode_fcntl_setfl_fail) { + // ARRANGE + int socket_fd; + system_context->register_accept_impl([&](int socket, struct sockaddr * address, socklen_t * address_len) -> int { + socket_fd = ::socket(AF_INET, SOCK_STREAM, 0); + return socket_fd; + }); + + connection.start(); + + system_context->register_fcntl_impl([](int a, int cmd, int c) { + if (cmd == F_SETFL) { + errno = EBADF; + return -1; + } + return 0; + }); + + // ACT + int status = connection.setBlockMode(true); + + // ASSERT + EXPECT_EQ(status, -1); + + close(socket_fd); +} + +TEST_F( TCPConnectionTest, write_string ) { + // ARRANGE + char sent_data[100]; + memset(sent_data, 0, 100); + system_context->register_accept_impl([&](int socket, struct sockaddr * address, socklen_t * address_len) -> int { + return 6; + }); + system_context->noop_fcntl_impl(); + system_context->register_send_impl([&](int socket, const void * buffer, size_t length, int flags) -> ssize_t { + memcpy (sent_data, buffer, length); + return length; + }); + connection.start(); + + // ACT + std::string str = "This is a message to write"; + int result = connection.write(str); + + // ASSERT + ASSERT_EQ(result, str.length()); + ASSERT_STREQ(str.c_str(), sent_data); +} + +TEST_F( TCPConnectionTest, write_binary_buf ) { + // ARRANGE + char to_send[8] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}; + char sent_data[100]; + + memset(sent_data, 0, 100); + system_context->register_accept_impl([&](int socket, struct sockaddr * address, socklen_t * address_len) -> int { + return 6; + }); + system_context->noop_fcntl_impl(); + system_context->register_send_impl([&](int socket, const void * buffer, size_t length, int flags) -> ssize_t { + memcpy (sent_data, buffer, length); + return length; + }); + connection.start(); + + // ACT + int result = connection.write(to_send, sizeof(to_send)); + + // ASSERT + ASSERT_EQ(result, sizeof(to_send)); + for (int i = 0; i < 8; i++) { + ASSERT_EQ(to_send[i], sent_data[i]); + } +} + + +TEST_F( TCPConnectionTest, write_string_uninitialized ) { + // ARRANGE + std::string str = "This is a message to write"; + + // ACT + int result = connection.write(str); + + // ASSERT + ASSERT_EQ(result, -1); +} + +TEST_F( TCPConnectionTest, write_binary_buf_uninitialized ) { + // ARRANGE + char to_send[8] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}; + + // ACT + int result = connection.write(to_send, sizeof(to_send)); + + // ASSERT + ASSERT_EQ(result, -1); +} + + +TEST_F( TCPConnectionTest, read_nonewline ) { + // ARRANGE + std::string data_to_read = "Here is an incomplete message from a socket"; + + system_context->register_accept_impl([&](int socket, struct sockaddr * address, socklen_t * address_len) -> int { + return 6; + }); + system_context->noop_fcntl_impl(); + system_context->register_recv_impl([&](int socket, void * buffer, size_t length, int flags) -> ssize_t { + int data_to_read_size = data_to_read.size() < length ? data_to_read.size() : length; + + memcpy (buffer, data_to_read.c_str(), data_to_read_size); + return data_to_read_size; + }); + connection.start(); + + // ACT + std::string result; + int nbytes = connection.read(result); + + // ASSERT + ASSERT_EQ(result, std::string("")); + ASSERT_EQ(nbytes, 0); +} + + +TEST_F( TCPConnectionTest, read ) { + // ARRANGE + std::string data_to_read = "Here is a complete message from a socket\n This part is incomplete"; + + system_context->register_accept_impl([&](int socket, struct sockaddr * address, socklen_t * address_len) -> int { + return 6; + }); + system_context->noop_fcntl_impl(); + system_context->register_recv_impl([&](int socket, void * buffer, size_t length, int flags) -> ssize_t { + int data_to_read_size = data_to_read.size() < length ? data_to_read.size() : length; + memcpy (buffer, data_to_read.c_str(), data_to_read_size); + return data_to_read_size; + }); + connection.start(); + + // ACT + std::string result; + int nbytes = connection.read(result); + + // ASSERT + std::string expected = "Here is a complete message from a socket\n"; + expected += '\0'; + ASSERT_EQ(result, expected); + ASSERT_EQ(nbytes, expected.size()); +} + + +TEST_F( TCPConnectionTest, read_nodata ) { + // ARRANGE + system_context->register_accept_impl([&](int socket, struct sockaddr * address, socklen_t * address_len) -> int { + return 6; + }); + system_context->noop_fcntl_impl(); + system_context->register_recv_impl([&](int socket, void * buffer, size_t length, int flags) -> ssize_t { + errno = EAGAIN; + return -1; + }); + connection.start(); + + // ACT + std::string result; + int nbytes = connection.read(result); + + + // ASSERT + std::string expected = ""; + ASSERT_EQ(result, expected); + EXPECT_EQ(nbytes, 0); +} + +TEST_F( TCPConnectionTest, read_other_error ) { + // ARRANGE + system_context->register_accept_impl([&](int socket, struct sockaddr * address, socklen_t * address_len) -> int { + return 6; + }); + system_context->noop_fcntl_impl(); + system_context->register_recv_impl([&](int socket, void * buffer, size_t length, int flags) -> ssize_t { + errno = EBADF; + return -1; + }); + + connection.start(); + + // ACT + std::string result; + int nbytes = connection.read(result); + + // ASSERT + std::string expected = ""; + ASSERT_EQ(result, expected); + EXPECT_EQ(nbytes, -1); + +} + + + +TEST_F( TCPConnectionTest, read_uninitialized ) { + // ARRANGE + // ACT + std::string result; + int nbytes = connection.read(result); + + // ASSERT + std::string expected = ""; + ASSERT_EQ(result, expected); + ASSERT_EQ(nbytes, 0); + +} + + +TEST_F( TCPConnectionTest, disconnect ) { + // ARRANGE + int socket_fd = 6; + int close_called = 0; + int shutdown_called = 0; + system_context->register_accept_impl([&](int socket, struct sockaddr * address, socklen_t * address_len) -> int { + return socket_fd; + }); + system_context->register_close_impl([&](int socket) { + if (socket == socket_fd) + close_called++; + return 0; + }); + system_context->register_shutdown_impl([&](int socket, int how) { + if (socket == socket_fd) + shutdown_called++; + return 0; + }); + system_context->noop_fcntl_impl(); + connection.start(); + + // ACT + int result = connection.disconnect(); + + // ASSERT + ASSERT_EQ(result, 0); + ASSERT_EQ(close_called, 1); + ASSERT_EQ(shutdown_called, 1); + + // Don't shut down if already shut down + // ACT + result = connection.disconnect(); + + // ASSERT + ASSERT_EQ(result, -1); + ASSERT_EQ(close_called, 1); + ASSERT_EQ(shutdown_called, 1); + +} \ No newline at end of file diff --git a/trick_source/trick_utils/connection_handlers/test/UDPConnection_test.cpp b/trick_source/trick_utils/connection_handlers/test/UDPConnection_test.cpp new file mode 100644 index 00000000..a7c7623f --- /dev/null +++ b/trick_source/trick_utils/connection_handlers/test/UDPConnection_test.cpp @@ -0,0 +1,406 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "trick/UDPConnection.hh" +#include "SystemInterfaceMock/SystemInterfaceMock.hh" + + +class UDPConnectionTest : public testing::Test { + + protected: + UDPConnectionTest() : system_context(new SystemInferfaceMock()), connection (system_context) {} + ~UDPConnectionTest() {} + + SystemInferfaceMock * system_context; + Trick::UDPConnection connection; +}; + + + +TEST_F( UDPConnectionTest, initialize_localhost_0 ) { + // ARRANGE + char name[80]; + gethostname(name, (size_t) 80); + + // ACT + connection.initialize("localhost", 0); + connection.start(); + + // ASSERT + EXPECT_EQ(connection.isInitialized(), true); + EXPECT_EQ(connection.getHostname(), std::string(name)); +} + +TEST_F( UDPConnectionTest, initialize_localhost_54321 ) { + // ARRANGE + // ACT + connection.initialize("localhost", 54321); + connection.start(); + + // ASSERT + EXPECT_EQ(connection.isInitialized(), true); + EXPECT_EQ(connection.getPort(), 54321); +} + +TEST_F( UDPConnectionTest, initialize_localhost_numerical_54321 ) { + // ARRANGE + // ACT + connection.initialize("127.0.0.1", 12345); + connection.start(); + + // ASSERT + EXPECT_EQ(connection.getPort(), 12345); +} + +TEST_F( UDPConnectionTest, initialize_invalid_hostname ) { + // ARRANGE + // ACT + connection.initialize("some_invalid_hostname", 0); + connection.start(); + + // ASSERT + EXPECT_EQ(connection.isInitialized(), false); +} + +TEST_F( UDPConnectionTest, failed_socket ) { + // ARRANGE + system_context->register_socket_impl([](int a, int b, int c) { + errno = EPERM; + return -1; + }); + + // ACT + connection.initialize("localhost", 54321); + + // ASSERT + EXPECT_EQ(connection.isInitialized(), false); +} + +TEST_F( UDPConnectionTest, failed_setsockopt_reuseaddr ) { + // ARRANGE + system_context->register_setsockopt_impl([](int sockfd, int level, int optname, const void *optval, socklen_t optlen) { + errno = EINVAL; + return -1; + }); + + // ACT + connection.initialize("localhost", 54321); + + // ASSERT + EXPECT_EQ(connection.isInitialized(), false); +} + +TEST_F( UDPConnectionTest, failed_bind ) { + // ARRANGE + system_context->register_bind_impl([](int sockfd, const struct sockaddr *addr,socklen_t addrlen) { + errno = EADDRINUSE; + return -1; + }); + + // ACT + connection.initialize("localhost", 54321); + + // ASSERT + EXPECT_EQ(connection.isInitialized(), false); +} + +TEST_F( UDPConnectionTest, failed_sockname ) { + // ARRANGE + system_context->register_getsockname_impl([](int sockfd, struct sockaddr *addr, socklen_t *addrlen) { + ((struct sockaddr_in *) addr)->sin_port = htons(1234); + return 0; + }); + + // ACT + connection.initialize("localhost", 3412); + + // ASSERT + EXPECT_EQ(connection.isInitialized(), false); +} + + +TEST_F( UDPConnectionTest, setBlockMode_nonblocking) { + // ARRANGE + int socket_fd; + system_context->register_socket_impl([&socket_fd](int a, int b, int c) { + socket_fd = ::socket(a, b, c); + return socket_fd; + }); + + connection.initialize("localhost", 0); + connection.start(); + + // ACT + int status = connection.setBlockMode(false); + + // ASSERT + EXPECT_EQ(status, 0); + + int flag = fcntl(socket_fd, F_GETFL, 0); + EXPECT_TRUE(flag & O_NONBLOCK); + + close(socket_fd); +} + + +TEST_F( UDPConnectionTest, setBlockMode_blocking) { + // ARRANGE + int socket_fd; + system_context->register_socket_impl([&socket_fd](int a, int b, int c) { + socket_fd = ::socket(a, b, c); + return socket_fd; + }); + + connection.initialize("localhost", 0); + connection.start(); + + // ACT + int status = connection.setBlockMode(true); + + // ASSERT + EXPECT_EQ(status, 0); + + int flag = fcntl(socket_fd, F_GETFL, 0); + EXPECT_FALSE(flag & O_NONBLOCK); + + close(socket_fd); +} + +TEST_F( UDPConnectionTest, setBlockMode_fcntl_getfl_fail) { + // ARRANGE + int socket_fd; + system_context->register_socket_impl([&socket_fd](int a, int b, int c) { + socket_fd = ::socket(a, b, c); + return socket_fd; + }); + + connection.initialize("localhost", 0); + connection.start(); + + system_context->register_fcntl_impl([](int a, int b, int c) { + errno = EACCES; + return -1; + }); + + // ACT + + int status = connection.setBlockMode(true); + + // ASSERT + EXPECT_EQ(status, -1); + + close(socket_fd); +} + +TEST_F( UDPConnectionTest, setBlockMode_fcntl_setfl_fail) { + // ARRANGE + int socket_fd; + system_context->register_socket_impl([&socket_fd](int a, int b, int c) { + socket_fd = ::socket(a, b, c); + return socket_fd; + }); + + connection.initialize("localhost", 0); + connection.start(); + + system_context->register_fcntl_impl([](int a, int cmd, int c) { + if (cmd == F_SETFL) { + errno = EBADF; + return -1; + } + return 0; + }); + + // ACT + int status = connection.setBlockMode(true); + + // ASSERT + EXPECT_EQ(status, -1); + + close(socket_fd); +} + + +TEST_F( UDPConnectionTest, write_string ) { + // ARRANGE + char sent_data[100]; + memset(sent_data, 0, 100); + system_context->noop_fcntl_impl(); + system_context->register_sendto_impl([&](int socket, const void * buffer, size_t length, int flags, const struct sockaddr * dest_addr, socklen_t dest_len) -> ssize_t { + memcpy (sent_data, buffer, length); + return length; + }); + connection.initialize("localhost", 0); + connection.start(); + + // ACT + std::string str = "This is a message to write"; + int result = connection.write(str); + + // ASSERT + ASSERT_EQ(result, str.length()); + ASSERT_STREQ(str.c_str(), sent_data); +} + +TEST_F( UDPConnectionTest, write_binary_buf ) { + // ARRANGE + char to_send[8] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}; + char sent_data[100]; + + memset(sent_data, 0, 100); + system_context->noop_fcntl_impl(); + system_context->register_sendto_impl([&](int socket, const void * buffer, size_t length, int flags, const struct sockaddr * dest_addr, socklen_t dest_len) -> ssize_t { + memcpy (sent_data, buffer, length); + return length; + }); + connection.initialize("localhost", 0); + connection.start(); + + // ACT + int result = connection.write(to_send, sizeof(to_send)); + + // ASSERT + ASSERT_EQ(result, sizeof(to_send)); + for (int i = 0; i < 8; i++) { + ASSERT_EQ(to_send[i], sent_data[i]); + } +} + + +TEST_F( UDPConnectionTest, write_string_uninitialized ) { + // ARRANGE + std::string str = "This is a message to write"; + + // ACT + int result = connection.write(str); + + // ASSERT + ASSERT_EQ(result, -1); +} + +TEST_F( UDPConnectionTest, write_binary_buf_uninitialized ) { + // ARRANGE + char to_send[8] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}; + + // ACT + int result = connection.write(to_send, sizeof(to_send)); + + // ASSERT + ASSERT_EQ(result, -1); +} + + +TEST_F( UDPConnectionTest, read_nonewline ) { + // ARRANGE + std::string data_to_read = "Here is an incomplete message from a socket"; + + system_context->noop_fcntl_impl(); + system_context->register_recvfrom_impl([&](int socket, void * buffer, size_t length, int flags, struct sockaddr * address, socklen_t * address_len) -> ssize_t { + int data_to_read_size = data_to_read.size() < length ? data_to_read.size() : length; + + memcpy (buffer, data_to_read.c_str(), data_to_read_size); + return data_to_read_size; + }); + connection.initialize("localhost", 0); + connection.start(); + + + // ACT + std::string result; + int nbytes = connection.read(result); + + // ASSERT + ASSERT_EQ(result, std::string("")); + ASSERT_EQ(nbytes, 0); +} + + +TEST_F( UDPConnectionTest, read ) { + // ARRANGE + std::string data_to_read = "Here is a complete message from a socket\n This part is incomplete"; + + system_context->noop_fcntl_impl(); + system_context->register_recvfrom_impl([&](int socket, void * buffer, size_t length, int flags, struct sockaddr * address, socklen_t * address_len) -> ssize_t { + int data_to_read_size = data_to_read.size() < length ? data_to_read.size() : length; + memcpy (buffer, data_to_read.c_str(), data_to_read_size); + return data_to_read_size; + }); + connection.initialize("localhost", 0); + connection.start(); + + // ACT + std::string result; + int nbytes = connection.read(result); + + // ASSERT + std::string expected = "Here is a complete message from a socket\n"; + expected += '\0'; + ASSERT_EQ(result, expected); + ASSERT_EQ(nbytes, expected.size()); + +} + + +TEST_F( UDPConnectionTest, read_nodata ) { + // ARRANGE + system_context->noop_fcntl_impl(); + system_context->register_recvfrom_impl([&](int socket, void * buffer, size_t length, int flags, struct sockaddr * address, socklen_t * address_len) -> ssize_t { + errno = EAGAIN; + return -1; + }); + connection.initialize("localhost", 0); + connection.start(); + + // ACT + std::string result; + int nbytes = connection.read(result); + + // ASSERT + std::string expected = ""; + ASSERT_EQ(result, expected); + ASSERT_EQ(nbytes, 0); +} + +TEST_F( UDPConnectionTest, read_other_error ) { + // ARRANGE + system_context->noop_fcntl_impl(); + system_context->register_recvfrom_impl([&](int socket, void * buffer, size_t length, int flags, struct sockaddr * address, socklen_t * address_len) -> ssize_t { + errno = EBADF; + return -1; + }); + connection.initialize("localhost", 0); + connection.start(); + + // ACT + std::string result; + int nbytes = connection.read(result); + + // ASSERT + std::string expected = ""; + ASSERT_EQ(result, expected); + ASSERT_EQ(nbytes, -1); + +} + +TEST_F( UDPConnectionTest, read_uninitialized ) { + // ARRANGE + // ACT + std::string result; + int nbytes = connection.read(result); + + // ASSERT + std::string expected = ""; + ASSERT_EQ(result, expected); + ASSERT_EQ(nbytes, 0); +} diff --git a/trick_source/trick_utils/var_binary_parser/.gitignore b/trick_source/trick_utils/var_binary_parser/.gitignore index 2b9516df..9e345c98 100644 --- a/trick_source/trick_utils/var_binary_parser/.gitignore +++ b/trick_source/trick_utils/var_binary_parser/.gitignore @@ -1,2 +1,2 @@ *.o -TEST_var_binary_parser \ No newline at end of file +TEST_var_binary_parser diff --git a/trick_source/trick_utils/var_binary_parser/src/var_binary_parser.cc b/trick_source/trick_utils/var_binary_parser/src/var_binary_parser.cc index 4255efa8..d8503316 100644 --- a/trick_source/trick_utils/var_binary_parser/src/var_binary_parser.cc +++ b/trick_source/trick_utils/var_binary_parser/src/var_binary_parser.cc @@ -272,24 +272,6 @@ const size_t ParsedBinaryMessage::variable_name_length_size = 4; const size_t ParsedBinaryMessage::variable_type_size = 4; const size_t ParsedBinaryMessage::variable_size_size = 4; -// Sometimes, we need to reconstruct the size from the message. -// Risk of segfaulting here if the bytes are not correctly formed -int ParsedBinaryMessage::parse (char * raw_bytes) { - std::vector bytes; - unsigned int i = 0; - for ( ; i < message_indicator_size + message_size_size; i++) { - bytes.push_back(raw_bytes[i]); - } - - unsigned int message_size = bytesToInt(std::vector (bytes.begin() + message_indicator_size, bytes.begin() + message_indicator_size + message_size_size), _byteswap); - for ( ; i < message_size + message_indicator_size; i++) { - bytes.push_back(raw_bytes[i]); - } - - return parse(bytes); -} - - int ParsedBinaryMessage::parse (const std::vector& bytes) { if (bytes.size() < header_size) { throw MalformedMessageException(std::string("Not enough bytes in message to contain header: expected at least 12, got " + std::to_string(bytes.size()))); diff --git a/trick_source/trick_utils/var_binary_parser/test/Makefile b/trick_source/trick_utils/var_binary_parser/test/Makefile index 59090679..d82b950c 100644 --- a/trick_source/trick_utils/var_binary_parser/test/Makefile +++ b/trick_source/trick_utils/var_binary_parser/test/Makefile @@ -34,4 +34,4 @@ TEST_var_binary_parser.o : TEST_var_binary_parser.cc $(OTHER_OBJECTS) TEST_var_binary_parser : TEST_var_binary_parser.o $(OTHER_OBJECTS) @echo 'Building TEST_var_binary_parser' - $(CXX) $(TRICK_SYSTEM_LDFLAGS) $(CPPFLAGS) -o $@ $^ -L${TRICK_HOME}/lib_${TRICK_HOST_CPU} $(LIBS) $(EXEC_LINK_LIBS) \ No newline at end of file + $(CXX) $(TRICK_SYSTEM_LDFLAGS) $(CPPFLAGS) -o $@ $^ -L${TRICK_HOME}/lib_${TRICK_HOST_CPU} $(LIBS) $(EXEC_LINK_LIBS) diff --git a/trick_source/trick_utils/var_binary_parser/test/TEST_var_binary_parser.cc b/trick_source/trick_utils/var_binary_parser/test/TEST_var_binary_parser.cc index 1ea19406..0572371e 100644 --- a/trick_source/trick_utils/var_binary_parser/test/TEST_var_binary_parser.cc +++ b/trick_source/trick_utils/var_binary_parser/test/TEST_var_binary_parser.cc @@ -3,7 +3,6 @@ #include #include #include -// #include #include "trick/var_binary_parser.hh" // int hi = 161 @@ -837,7 +836,7 @@ TEST (BinaryParserTest, ParseLong) { // apparently this can be different by platform so we need to be careful here size_t long_size = sizeof(long); std::vector bytes; - for (int i = 0; i < long_size-1; i++) { + for (unsigned int i = 0; i < long_size-1; i++) { bytes.push_back(0x00); } bytes.push_back(0x80); @@ -871,7 +870,7 @@ TEST (BinaryParserTest, ParseUnsignedLong) { // apparently this can be different by platform so we need to be careful here size_t long_size = sizeof(unsigned long); std::vector bytes; - for (int i = 0; i < long_size-1; i++) { + for (unsigned int i = 0; i < long_size-1; i++) { bytes.push_back(0xFF); } bytes.push_back(0x7F); @@ -904,7 +903,7 @@ TEST (BinaryParserTest, ParseLongLong) { // apparently this can be different by platform so we need to be careful here size_t long_long_size = sizeof(long long); std::vector bytes; - for (int i = 0; i < long_long_size-1; i++) { + for (unsigned int i = 0; i < long_long_size-1; i++) { bytes.push_back(0x00); } bytes.push_back(0x80); @@ -937,7 +936,7 @@ TEST (BinaryParserTest, ParseUnsignedLongLong) { // apparently this can be different by platform so we need to be careful here size_t long_long_size = sizeof(unsigned long long); std::vector bytes; - for (int i = 0; i < long_long_size-1; i++) { + for (unsigned int i = 0; i < long_long_size-1; i++) { bytes.push_back(0xFF); } bytes.push_back(0x7F); @@ -1055,7 +1054,7 @@ TEST (BinaryParserTest, ParseWChar) { wchar_t test_wchar = L'J'; std::vector bytes; - for (int i = 0; i < sizeof(wchar_t); i++) { + for (unsigned int i = 0; i < sizeof(wchar_t); i++) { bytes.push_back((unsigned char)((test_wchar >> (i*8)) & 0xFF)); } @@ -1066,11 +1065,17 @@ TEST (BinaryParserTest, ParseWChar) { TEST (BinaryParserTest, ParseWCharWrongType) { Var variable; - std::vector bytes = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0xc0}; + wchar_t test_wchar = L'J'; + std::vector bytes; + + for (unsigned int i = 0; i < sizeof(wchar_t); i++) { + bytes.push_back((unsigned char)((test_wchar >> (i*8)) & 0xFF)); + } + variable.setValue(bytes, 8, TRICK_INTEGER, false); try { - variable.getValue(); + variable.getValue(); FAIL() << "Expected exception thrown"; } catch(ParseTypeException& ex) { diff --git a/trick_source/web/CivetServer/include/VariableServerSessionWeb.hh b/trick_source/web/CivetServer/include/VariableServerSessionWeb.hh new file mode 100644 index 00000000..a138fc19 --- /dev/null +++ b/trick_source/web/CivetServer/include/VariableServerSessionWeb.hh @@ -0,0 +1,51 @@ +/************************************************************************* +PURPOSE: (Represent the state of a variable server websocket connection.) +**************************************************************************/ + +#ifndef WSSESSION_HH +#define WSSESSION_HH + +#include +#include + +#ifndef SWIG +#include "CivetServer.h" +#endif + +#include "trick/WebSocketSession.hh" +#include "VariableServerVariable.hh" + +class VariableServerSession : public WebSocketSession { + public: + VariableServerSession(struct mg_connection *nc); + ~VariableServerSession(); + void marshallData(); /* -- base */ + void sendMessage(); /* -- base */ + int handleMessage(const std::string&); /* -- base */ + + void setTimeInterval(unsigned int milliseconds); + void addVariable(char* vname); + void stageValues(); + void pause(); + void unpause(); + void clear(); + void exit(); + + // static int bad_ref_int ; + + private: + int sendErrorMessage(const char* fmt, ... ); + int sendSieMessage(void); + int sendUnitsMessage(const char* vname); + // REF2* make_error_ref(const char* in_name); + double stageTime; + bool dataStaged; + + std::vector session_variables; + bool cyclicSendEnabled; + long long nextTime; + long long intervalTimeTics; +}; + +WebSocketSession* makeVariableServerSession( struct mg_connection *nc ); +#endif diff --git a/trickops.py b/trickops.py index 14027706..6031de82 100644 --- a/trickops.py +++ b/trickops.py @@ -21,38 +21,34 @@ class SimTestWorkflow(TrickWorkflow): trick_dir=trick_top_level, config_file=(trick_top_level + "/test_sims.yml"), cpus=self.cpus, quiet=quiet) def run( self ): build_jobs = self.get_jobs(kind='build') - # Two sims have runs that require ordering via phases: + + # This is awful but I can't think of another way around it + # SIM_test_varserver has 2 tests that should return the code for SIGUSR1, the number is different on Mac vs Linux + # so it can't be hardcoded in the input yml file. Maybe this is a case having a label on a run would be cleaner? + import signal + run_names = ["Run test/SIM_test_varserv RUN_test/err1_test.py", "Run test/SIM_test_varserv RUN_test/err2_test.py"] + for job in [job for job in self.get_jobs(kind='run') if job.name in run_names]: + job._expected_exit_status = signal.SIGUSR1.value + + # Several test sims have runs that require ordering via phases: # - SIM_stls dumps a checkpoint that is then read in and checked by a subsequent run # - SIM_checkpoint_data_recording dumps checkpoints that are read by subsequent runs - first_run_jobs = self.get_jobs(kind='run', phase=-1) # Get all jobs with early phase -1 - remaining_run_jobs = self.get_jobs(kind='run', phase=0) # Get all jobs with default phase 0 + # - SIM_test_varserver has 3 runs that cannot be concurrent + # - SIM_mc_generation generates runs and then runs them + phases = [-1, 0, 1, 2, 3] + analysis_jobs = self.get_jobs(kind='analyze') - - # Some tests fail intermittently for reasons not related to the tests themselves, mostly network weirdness. - # Allow retries so that we can still cover some network-adjacent code - retry_allowed_sims = self.get_sims(labels='retries_allowed') - retry_allowed_jobs = [run.get_run_job() for run in [item for sublist in [sim.get_runs() for sim in retry_allowed_sims] for item in sublist]] - for job in retry_allowed_jobs: - # Note there's an assumption/dependency here that 'retries_allowed' runs - # are only in the remaining_run_jobs list. - Jordan 2/2023 - remaining_run_jobs.remove(job) - builds_status = self.execute_jobs(build_jobs, max_concurrent=self.cpus, header='Executing all sim builds.') - first_phase_run_status = self.execute_jobs(first_run_jobs, max_concurrent=self.cpus, header="Executing first phase runs.") - runs_status = self.execute_jobs(remaining_run_jobs, max_concurrent=self.cpus, header='Executing remaining runs.') - # Run the retry_allowed jobs - self.execute_jobs(retry_allowed_jobs, max_concurrent=self.cpus, header='Executing retry-allowed runs.') - - # If anything failed, try it again up to max_retries times - all_retried_status = 0 - final_retry_jobs = [] - for sim in retry_allowed_sims: - failing_runs = [run for run in sim.get_runs() if run.get_run_job().get_status() == Job.Status.FAILED] - for run in failing_runs: - status, final_job = self.retry_job(sim, run, max_retries) - final_retry_jobs += [final_job] - all_retried_status = all_retried_status or status + jobs = build_jobs + + run_status = 0 + for phase in phases: + run_jobs = self.get_jobs(kind='run', phase=phase) + this_status = self.execute_jobs(run_jobs, max_concurrent=self.cpus, header="Executing phase " + str(phase) + " runs.", job_timeout=1000) + run_status = run_status or this_status + jobs += run_jobs + comparison_result = self.compare() analysis_status = self.execute_jobs(analysis_jobs, max_concurrent=self.cpus, header='Executing all analysis.') @@ -60,40 +56,30 @@ class SimTestWorkflow(TrickWorkflow): self.status_summary() # Print a Succinct summary # Dump failing logs - jobs = build_jobs + first_run_jobs + remaining_run_jobs + final_retry_jobs for job in jobs: - if job.get_status() == Job.Status.FAILED: - print("Failing job: ", job.name) + if job.get_status() == Job.Status.FAILED or job.get_status() == Job.Status.TIMEOUT: + print ("*"*120) + if job.get_status() == Job.Status.FAILED: + header = "Failing job: " + job.name + else: + header = "Timed out job: " + job.name + + numspaces = int((120 - 20 - len(header))/2 -2) + print("*"*10, " "*numspaces, header, " "*numspaces, "*"*10,) print ("*"*120) print(open(job.log_file, "r").read()) - print ("*"*120, "\n") + print ("*"*120, "\n\n\n") - return (builds_status or runs_status or first_phase_run_status or all_retried_status or len(self.config_errors) > 0 or comparison_result or analysis_status) - - # Retries a job up to max_retries times and adds runs to the sim - # Returns tuple of (job_status, final retry job) - def retry_job(self, sim, run, max_retries): - tries = 0 - job_failing = 1 - retry_run = None - retry_job = None - while tries < max_retries and job_failing: - tries += 1 - retry_run = TrickWorkflow.Run(sim_dir=run.sim_dir, input_file=run.input_file, binary=run.binary, returns=run.returns,log_dir=run.log_dir) - retry_job = retry_run.get_run_job() - retry_job.name = retry_job.name + "_retry_" + str(tries) - job_failing = self.execute_jobs([retry_job], max_concurrent=1, header="Retrying failed job") - sim.add_run(retry_run) - - return (job_failing, retry_job) + return (builds_status or run_status or len(self.config_errors) > 0 or comparison_result or analysis_status) if __name__ == "__main__": parser = argparse.ArgumentParser(description='Build, run, and compare all test sims for Trick', formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument( "--trick_top_level", type=str, help="Path to TRICK_HOME", default=thisdir) - parser.add_argument( "--quiet", action="store_true", help="Suppress progress bars.") + parser.add_argument( "--quiet", action="store_true", help="Suppress progress bars (automatically set to True if environment variable CI is present).") parser.add_argument( "--cpus", type=int, default=(os.cpu_count() if os.cpu_count() is not None else 8), help="Number of cpus to use for testing. For builds this number is used for MAKEFLAGS *and* number of " "concurrent builds (cpus^2). For sim runs this controls the maximum number of simultaneous runs.") myargs = parser.parse_args() - sys.exit(SimTestWorkflow(quiet=myargs.quiet, trick_top_level=myargs.trick_top_level, cpus=myargs.cpus).run()) + should_be_quiet = myargs.quiet or os.getenv('CI') is not None + sys.exit(SimTestWorkflow(quiet=should_be_quiet, trick_top_level=myargs.trick_top_level, cpus=myargs.cpus).run())