From 5ff52a01c5372ac965b2ca9f168a4690d3679989 Mon Sep 17 00:00:00 2001 From: Jacqueline Deans Date: Fri, 20 Jan 2023 15:46:10 -0600 Subject: [PATCH] Add option to terminate sim on python event error (#1434) --- .../running_a_simulation/Input-File.md | 3 +++ include/trick/IPPythonEvent.hh | 13 ++++++++++- share/trick/swig/shortcuts.py | 1 + test/SIM_events/RUN_test/unit_test_error1.py | 7 ++++++ test/SIM_events/RUN_test/unit_test_error2.py | 12 ++++++++++ test/SIM_events/RUN_test/unit_test_error3.py | 12 ++++++++++ test_sims.yml | 22 +++++++++++++------ .../sim_services/InputProcessor/IPPython.cpp | 4 ++-- .../InputProcessor/IPPythonEvent.cpp | 22 +++++++++++++++---- 9 files changed, 82 insertions(+), 14 deletions(-) create mode 100644 test/SIM_events/RUN_test/unit_test_error1.py create mode 100644 test/SIM_events/RUN_test/unit_test_error2.py create mode 100644 test/SIM_events/RUN_test/unit_test_error3.py diff --git a/docs/documentation/running_a_simulation/Input-File.md b/docs/documentation/running_a_simulation/Input-File.md index 02a9fd80..68cfd5b4 100644 --- a/docs/documentation/running_a_simulation/Input-File.md +++ b/docs/documentation/running_a_simulation/Input-File.md @@ -449,6 +449,9 @@ For information on how Trick processes events during runtime, see [Event Process # Add the event to the input processor's list of events (it will be processed at top of frame before scheduled jobs) trick.add_event() + +# Tell trick whether to terminate the sim if an error occurs while parsing Python code. Defaults to False +trick.terminate_on_event_parse_error() ``` #### Advanced Event (Malfunction) Usage diff --git a/include/trick/IPPythonEvent.hh b/include/trick/IPPythonEvent.hh index 7a512d4b..f62fe3b9 100644 --- a/include/trick/IPPythonEvent.hh +++ b/include/trick/IPPythonEvent.hh @@ -184,6 +184,15 @@ namespace Trick { */ static void set_event_info_msg_off() ; + /** + @brief @userdesc Command to set whether the sim should check error codes from + Python parsing and terminate if an error is detected. Set to false by default + @par Python Usage: + @code trick.terminate_on_event_parse_error(True|False) @endcode + @return always 0 + */ + static void terminate_on_event_parse_error(bool on_off); + /** @brief called by the event manager when the event is loaded from a checkpoint */ @@ -402,7 +411,7 @@ namespace Trick { any events instantiated yet */ static void set_python_processor(Trick::IPPython * in_ip) ; static void set_mtv(Trick::MTV * in_mtv) ; - + private: /* A static pointer to the python input processor set at the S_define level */ @@ -411,6 +420,8 @@ namespace Trick { /* A static pointer to the MTV set at the S_define level */ static Trick::MTV * mtv ; + /* Defaults to false */ + static bool terminate_sim_on_event_python_error; } ; } diff --git a/share/trick/swig/shortcuts.py b/share/trick/swig/shortcuts.py index eaa6c783..7bd36f0d 100644 --- a/share/trick/swig/shortcuts.py +++ b/share/trick/swig/shortcuts.py @@ -44,6 +44,7 @@ if hasattr(top.cvar, 'trick_ip'): set_event_info_msg_on = trick.IPPythonEvent.set_event_info_msg_on set_event_info_msg_off = trick.IPPythonEvent.set_event_info_msg_off +terminate_on_event_parse_error = trick.IPPythonEvent.terminate_on_event_parse_error # bind pyton input_processor event routines to shortcut names. new_event = trick.ippython_new_event diff --git a/test/SIM_events/RUN_test/unit_test_error1.py b/test/SIM_events/RUN_test/unit_test_error1.py new file mode 100644 index 00000000..c72fd8e8 --- /dev/null +++ b/test/SIM_events/RUN_test/unit_test_error1.py @@ -0,0 +1,7 @@ +trick.stop(1.0) + +# print(dir(trick)) +trick.terminate_on_event_parse_error(True) + +# Error in add read +trick.add_read(0.1, "a = b") \ No newline at end of file diff --git a/test/SIM_events/RUN_test/unit_test_error2.py b/test/SIM_events/RUN_test/unit_test_error2.py new file mode 100644 index 00000000..c7e7af3f --- /dev/null +++ b/test/SIM_events/RUN_test/unit_test_error2.py @@ -0,0 +1,12 @@ +trick.terminate_on_event_parse_error(True) + +# Error in condition +event1 = trick.new_event("event1") +event1.condition(0, "this is a syntax error") +event1.action(0, "print (\"event1\");") +event1.action(1, "event1.activate()") +event1.set_cycle(1.0) +event1.activate() +trick.add_event(event1) + +trick.stop(10) \ No newline at end of file diff --git a/test/SIM_events/RUN_test/unit_test_error3.py b/test/SIM_events/RUN_test/unit_test_error3.py new file mode 100644 index 00000000..65d3954c --- /dev/null +++ b/test/SIM_events/RUN_test/unit_test_error3.py @@ -0,0 +1,12 @@ +trick.terminate_on_event_parse_error(True) + +# Error in event action +event1 = trick.new_event("event1") +event1.condition(0, "True") +event1.action(0, "this is a syntax error") +event1.action(1, "event1.activate()") +event1.set_cycle(1.0) +event1.activate() +trick.add_event(event1) + +trick.stop(10) \ No newline at end of file diff --git a/test_sims.yml b/test_sims.yml index 56c67985..68df7f32 100644 --- a/test_sims.yml +++ b/test_sims.yml @@ -42,13 +42,6 @@ SIM_demo_sdefine: runs: RUN_test/unit_test.py: returns: 0 -SIM_events: - path: test/SIM_events - build_command: "trick-CP -t" - binary: "T_main_{cpu}_test.exe" - runs: - RUN_test/unit_test.py: - returns: 0 SIM_exec_set_time_tic_value: path: test/SIM_exec_set_time_tic_value build_command: "trick-CP -t" @@ -267,6 +260,21 @@ SIM_checkpoint_data_recording: # compare: # - test/SIM_checkpoint_data_recording/RUN_test6/ref_log_foo2.csv vs. test/SIM_checkpoint_data_recording/RUN_test6/log_foo2.csv +SIM_events: + path: test/SIM_events + build_command: "trick-CP -t" + binary: "T_main_{cpu}_test.exe" + runs: + RUN_test/unit_test.py: + returns: 0 + RUN_test/unit_test_error1.py: + returns: 255 + RUN_test/unit_test_error2.py: + returns: 255 + RUN_test/unit_test_error3.py: + returns: 255 + + # The variable server client and SIM_amoeba sometimes fail to connect and need to be retried SIM_test_varserv: path: test/SIM_test_varserv diff --git a/trick_source/sim_services/InputProcessor/IPPython.cpp b/trick_source/sim_services/InputProcessor/IPPython.cpp index a26b01ed..665b5772 100644 --- a/trick_source/sim_services/InputProcessor/IPPython.cpp +++ b/trick_source/sim_services/InputProcessor/IPPython.cpp @@ -179,11 +179,11 @@ int Trick::IPPython::parse_condition(std::string in_string, int & cond_return_va pthread_mutex_lock(&ip_mutex); in_string = std::string("trick_ip.ip.return_val = ") + in_string + "\n" ; // Running the simple string will set return_val. - PyRun_SimpleString(in_string.c_str()) ; + int py_ret = PyRun_SimpleString(in_string.c_str()) ; cond_return_val = return_val ; pthread_mutex_unlock(&ip_mutex); - return 0 ; + return py_ret ; } diff --git a/trick_source/sim_services/InputProcessor/IPPythonEvent.cpp b/trick_source/sim_services/InputProcessor/IPPythonEvent.cpp index 3db54c32..c822ddf9 100644 --- a/trick_source/sim_services/InputProcessor/IPPythonEvent.cpp +++ b/trick_source/sim_services/InputProcessor/IPPythonEvent.cpp @@ -29,6 +29,7 @@ extern Trick::MemoryManager * trick_MM ; Trick::IPPython * Trick::IPPythonEvent::ip ; Trick::MTV * Trick::IPPythonEvent::mtv ; bool Trick::IPPythonEvent::info_msg = false ; +bool Trick::IPPythonEvent::terminate_sim_on_event_python_error = false; Trick::condition_t::condition_t() { enabled = 0 ; @@ -169,6 +170,10 @@ void Trick::IPPythonEvent::set_event_info_msg_off() { info_msg = false; } +void Trick::IPPythonEvent::terminate_on_event_parse_error(bool on_off) { + terminate_sim_on_event_python_error = on_off; +} + void Trick::IPPythonEvent::restart() { int jj ; @@ -515,7 +520,10 @@ int Trick::IPPythonEvent::process( long long curr_time ) { } else { // it's a read event active = false ; - ip->parse(action_list[0]->str) ; + int ret = ip->parse(action_list[0]->str) ; + if (ret != 0 && terminate_sim_on_event_python_error) { + exec_terminate_with_return( ret , __FILE__ , __LINE__ , "Python error in event processing" ) ; + } // keep stats so mtv will show when it ran fired_count++ ; fired_time = curr_time ; @@ -572,7 +580,10 @@ bool Trick::IPPythonEvent::process_user_event( long long curr_time ) { } else { // otherwise use python to evaluate string std::string full_in_string ; - ip->parse_condition(condition_list[ii]->str, return_val) ; + int python_ret = ip->parse_condition(condition_list[ii]->str, return_val) ; + if (python_ret != 0 && terminate_sim_on_event_python_error) { + exec_terminate_with_return( python_ret , __FILE__ , __LINE__ , "Python error in event condition processing" ) ; + } } if (return_val) { //TODO: write to log/send_hs that trigger fired @@ -639,8 +650,11 @@ bool Trick::IPPythonEvent::process_user_event( long long curr_time ) { break; } } else { - // otherwise use python to evaluate string - ip->parse(action_list[ii]->str) ; + // otherwise use python to evaluate string + int ret = ip->parse(action_list[ii]->str) ; + if (ret != 0 && terminate_sim_on_event_python_error) { + exec_terminate_with_return( ret , __FILE__ , __LINE__ , "Python error in event action processing" ) ; + } } it_ran = true ; action_list[ii]->ran = true ;