diff --git a/background_installer.sh b/background_installer.sh index 1633dad..9db0d1a 100755 --- a/background_installer.sh +++ b/background_installer.sh @@ -335,6 +335,8 @@ elif [ "$1" == "win_msys2" ]; then exit 1 fi + cp -f ./utils/snap7_src/build/bin/win64/snap7.* ./webserver/core/ + install_st_optimizer install_glue_generator disable_opendnp3 diff --git a/documentation/S7-Protocol/Siemens Protocol driver for OpenPLC.pdf b/documentation/S7-Protocol/Siemens Protocol driver for OpenPLC.pdf index ecc4aab..f431dc2 100644 Binary files a/documentation/S7-Protocol/Siemens Protocol driver for OpenPLC.pdf and b/documentation/S7-Protocol/Siemens Protocol driver for OpenPLC.pdf differ diff --git a/utils/snap7_src/wrapper/oplc_snap7.cpp b/utils/snap7_src/wrapper/oplc_snap7.cpp index 9c8cb74..1fee14c 100644 --- a/utils/snap7_src/wrapper/oplc_snap7.cpp +++ b/utils/snap7_src/wrapper/oplc_snap7.cpp @@ -749,6 +749,7 @@ bool TS7Partner::Linked() //****************************************************************************** TS7Server *Server = NULL; +bool s7Inited = false; #define MAX_S7IO BUFFER_SIZE*8 #define MK_SIZE 16 @@ -1248,32 +1249,56 @@ int S7API RWAreaCallBack(void* usrPtr, int Sender, int Operation, PS7Tag PTag, v //------------------------------------------------------------------------------ void initializeSnap7() { - s7mapUnusedVars(); + if (!s7Inited) + { + s7mapUnusedVars(); - Server = new TS7Server; - // With the next function we can limit the events to start/stop/client added etc. - // For a deep debug comment the line - Server->SetEventsMask(0x3ff); - // Set the Server events callback - Server->SetEventsCallback(EventCallBack, NULL); + Server = new TS7Server; + // With the next function we can limit the events to start/stop/client added etc. + // For a deep debug comment the line + Server->SetEventsMask(0x3ff); + // Set the Server events callback + Server->SetEventsCallback(EventCallBack, NULL); - // Shared resources: - // We cannot directly share OpenPLC internal buffers, because: - // 1 - They are array of pointers to vars - // 2 - The access would be not synchronized (i.e. not consistent) - // So, we will use a callback and, inside it, we will perform a synchronized data transfer. - Server->SetRWAreaCallback(RWAreaCallBack, NULL); - // Listen on S7 Port (102) - Server->StartTo("0.0.0.0"); // Success or fail will be logged into EventCallBack + // Shared resources: + // We cannot directly share OpenPLC internal buffers, because: + // 1 - They are array of pointers to vars + // 2 - The access would be not synchronized (i.e. not consistent) + // So, we will use a callback and, inside it, we will perform a synchronized data transfer. + Server->SetRWAreaCallback(RWAreaCallBack, NULL); + s7Inited = true; + } } +//------------------------------------------------------------------------------ +// Snap7 Server start +//------------------------------------------------------------------------------ +void startSnap7() +{ + // Listen on S7 Port 102. + // If Server is already running the command will be ignored. + if (s7Inited) + Server->StartTo("0.0.0.0"); // Success or fail will be logged into EventCallBack +} + +//------------------------------------------------------------------------------ +// Snap7 Server stop +//------------------------------------------------------------------------------ +void stopSnap7() +{ + // If Server is already stopped the command will be ignored. + if (s7Inited) + Server->Stop(); +} + //------------------------------------------------------------------------------ // Snap7 Server destruction //------------------------------------------------------------------------------ void finalizeSnap7() { - if (Server != NULL) + if (s7Inited) { + s7Inited = false; Server->Stop(); delete Server; Server = NULL; diff --git a/utils/snap7_src/wrapper/oplc_snap7.h b/utils/snap7_src/wrapper/oplc_snap7.h index a89c1da..c10edca 100644 --- a/utils/snap7_src/wrapper/oplc_snap7.h +++ b/utils/snap7_src/wrapper/oplc_snap7.h @@ -943,8 +943,8 @@ typedef TS7Partner *PS7Partner; void initializeSnap7(); void finalizeSnap7(); - - +void startSnap7(); +void stopSnap7(); #endif // __cplusplus #endif // snap7_h diff --git a/webserver/check_openplc_db.py b/webserver/check_openplc_db.py index b895567..ebead5a 100644 --- a/webserver/check_openplc_db.py +++ b/webserver/check_openplc_db.py @@ -119,6 +119,7 @@ def checkTableSettings(conn): checkSettingExists(conn, 'Modbus_port', '502') checkSettingExists(conn, 'Dnp3_port', '20000') checkSettingExists(conn, 'Start_run_mode', 'false') + checkSettingExists(conn, 'snap7', 'false') checkSettingExists(conn, 'Slave_polling', '100') checkSettingExists(conn, 'Slave_timeout', '1000') checkSettingExists(conn, 'Enip_port', '44818') diff --git a/webserver/core/interactive_server.cpp b/webserver/core/interactive_server.cpp index ce79330..7b054d5 100644 --- a/webserver/core/interactive_server.cpp +++ b/webserver/core/interactive_server.cpp @@ -36,6 +36,8 @@ #include #include "ladder.h" +#include "oplc_snap7.h" + #define BUFFER_SIZE 1024 //Global Variables @@ -43,6 +45,7 @@ bool ethercat_configured = 0; char ethercat_conf_file[BUFFER_SIZE]; bool run_modbus = 0; uint16_t modbus_port = 502; +bool run_snap7 = 0; bool run_dnp3 = 0; uint16_t dnp3_port = 20000; bool run_enip = 0; @@ -263,6 +266,13 @@ void processCommand(unsigned char *buffer, int client_fd) sprintf(log_msg, "DNP3 server was stopped\n"); log(log_msg); } + if (run_snap7) + { + run_snap7 = 0; + stopSnap7(); + sprintf(log_msg, "Snap7 server was stopped\n"); + log(log_msg); + } run_openplc = 0; processing_command = false; } @@ -314,6 +324,40 @@ void processCommand(unsigned char *buffer, int client_fd) } processing_command = false; } + else if (strncmp(buffer, "start_snap7()", 13) == 0) + { + processing_command = true; + sprintf(log_msg, "Issued start_snap7() command\n"); + log(log_msg); + if (run_snap7) + { + sprintf(log_msg, "Snap7 server already active. Restarting it\n"); + log(log_msg); + //Stop Modbus server + run_snap7 = 0; + stopSnap7(); + sprintf(log_msg, "Snap7 server was stopped\n"); + log(log_msg); + } + //Start Modbus server + run_snap7 = 1; + startSnap7(); + processing_command = false; + } + else if (strncmp(buffer, "stop_snap7()", 12) == 0) + { + processing_command = true; + sprintf(log_msg, "Issued stop_snap7() command\n"); + log(log_msg); + if (run_snap7) + { + run_snap7 = 0; + stopSnap7(); + sprintf(log_msg, "Snap7 server was stopped\n"); + log(log_msg); + } + processing_command = false; + } else if (strncmp(buffer, "start_dnp3(", 11) == 0) { processing_command = true; diff --git a/webserver/core/main.cpp b/webserver/core/main.cpp index 1153033..64c6df3 100644 --- a/webserver/core/main.cpp +++ b/webserver/core/main.cpp @@ -37,9 +37,7 @@ #include "ethercat_src.h" #endif -#ifdef _snap7 #include "oplc_snap7.h" -#endif #define OPLC_CYCLE 50000000 @@ -119,10 +117,11 @@ int main(int argc,char **argv) readPersistentStorage(); //pthread_t persistentThread; //pthread_create(&persistentThread, NULL, persistentStorage, NULL); - -#ifdef _snap7 + + //====================================================== + // S7 PROTOCOL INITIALIZATION + //====================================================== initializeSnap7(); -#endif @@ -253,10 +252,7 @@ int main(int argc,char **argv) ethercat_terminate_src(); #endif -#ifdef _snap7 finalizeSnap7(); -#endif - printf("Disabling outputs\n"); disableOutputs(); updateBuffersOut(); diff --git a/webserver/openplc.db b/webserver/openplc.db index f806365..ee6c63f 100644 Binary files a/webserver/openplc.db and b/webserver/openplc.db differ diff --git a/webserver/openplc.py b/webserver/openplc.py index 44c5aee..6980c97 100644 --- a/webserver/openplc.py +++ b/webserver/openplc.py @@ -196,6 +196,12 @@ class runtime: def stop_modbus(self): return self._rpc(f'stop_modbus()') + def start_snap7(self): + return self._rpc(f'start_snap7()') + + def stop_snap7(self): + return self._rpc(f'stop_snap7()') + def start_dnp3(self, port_num): return self._rpc(f'start_dnp3({port_num})') diff --git a/webserver/pages.py b/webserver/pages.py index 56c6e7f..25fcdf9 100644 --- a/webserver/pages.py +++ b/webserver/pages.py @@ -1202,6 +1202,8 @@ settings_tail = """ var pstorage_text = document.getElementById('pstorage_thread_poll'); var auto_run_checkbox = document.getElementById('auto_run'); var auto_run_text = document.getElementById('auto_run_text'); + var snap7_run_checkbox = document.getElementById('snap7_run'); + var snap7_run_text = document.getElementById('snap7_run_text'); if (modbus_checkbox.checked == true) { @@ -1247,6 +1249,14 @@ settings_tail = """ { auto_run_text.value = 'false'; } + if (snap7_run_checkbox.checked == true) + { + snap7_run_text.value = 'true'; + } + else + { + snap7_run_text.value = 'false'; + } } document.getElementById('modbus_server').onchange = function() @@ -1274,6 +1284,11 @@ settings_tail = """ setupCheckboxes(); } + document.getElementById('snap7_run').onchange = function() + { + setupCheckboxes(); + } + function validateForm() { var modbus_checkbox = document.forms["uploadForm"]["modbus_server"].checked; diff --git a/webserver/scripts/compile_program.sh b/webserver/scripts/compile_program.sh index a0dd073..c5f8379 100755 --- a/webserver/scripts/compile_program.sh +++ b/webserver/scripts/compile_program.sh @@ -10,7 +10,6 @@ cd scripts &>/dev/null OPENPLC_PLATFORM=$(cat openplc_platform) ETHERCAT_OPT=$(cat ethercat) OPENPLC_DRIVER=$(cat openplc_driver) -S7_OPT=$(cat s7protocol) #store the active program filename echo "$1" > ../active_program @@ -30,26 +29,9 @@ if [ "$ETHERCAT_OPT" = "ethercat" ]; then sed -i '7s/^/#include "ethercat_src.h" /' Res0.c fi -#check for S7 Protocol option -if [ "$S7_OPT" = "s7protocol" ]; then - S7_LIB="-lsnap7" - S7_DEF="-D _snap7" - S7_WLIB="snap7.lib" - cp -f ../utils/snap7_src/wrapper/oplc_snap7.* ./core - if [ "$OPENPLC_PLATFORM" = "win" ]; then - cp -f ../utils/snap7_src/build/bin/win64/snap7.* ./core - fi - echo "Including Siemens S7 Protocol via snap7" -else - S7_LIB="" - S7_DEF="" - S7_WLIB="" - # remove snap7 references (if any) - rm -f ./core/oplc_snap7.* - if [ "$OPENPLC_PLATFORM" = "win" ]; then - rm -f ./core/snap7.* - fi -fi +# I prefer copying every time these two (small files) because could be useful to have a copy of them for testing +echo "Including Siemens S7 Protocol via snap7" +cp -f ../utils/snap7_src/wrapper/oplc_snap7.* ./core echo "Moving Files..." mv -f POUS.c POUS.h LOCATED_VARIABLES.h VARIABLES.csv Config0.c Config0.h Res0.c ./core/ @@ -86,7 +68,7 @@ if [ "$OPENPLC_PLATFORM" = "win" ]; then echo "Generating glueVars..." ./glue_generator echo "Compiling main program..." - g++ *.cpp *.o -o openplc -I ./lib -pthread -fpermissive -I /usr/local/include/modbus -L /usr/local/lib $S7_WLIB -lmodbus -w $S7_DEF + g++ *.cpp *.o -o openplc -I ./lib -pthread -fpermissive -I /usr/local/include/modbus -L /usr/local/lib snap7.lib -lmodbus -w if [ $? -ne 0 ]; then echo "Error compiling C files" echo "Compilation finished with errors!" @@ -99,9 +81,9 @@ elif [ "$OPENPLC_PLATFORM" = "linux" ]; then echo "Compiling for Linux" echo "Generating object files..." if [ "$OPENPLC_DRIVER" = "sl_rp4" ]; then - g++ -std=gnu++11 -I ./lib -c Config0.c $S7_LIB -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w -DSL_RP4 + g++ -std=gnu++11 -I ./lib -c Config0.c -lsnap7 -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w -DSL_RP4 else - g++ -std=gnu++11 -I ./lib -c Config0.c $S7_LIB -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w + g++ -std=gnu++11 -I ./lib -c Config0.c -lsnap7 -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w fi if [ $? -ne 0 ]; then echo "Error compiling C files" @@ -109,9 +91,9 @@ elif [ "$OPENPLC_PLATFORM" = "linux" ]; then exit 1 fi if [ "$OPENPLC_DRIVER" = "sl_rp4" ]; then - g++ -std=gnu++11 -I ./lib -c Res0.c $S7_LIB -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w $ETHERCAT_INC $S7_DEF -DSL_RP4 + g++ -std=gnu++11 -I ./lib -c Res0.c -lsnap7 -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w $ETHERCAT_INC -DSL_RP4 else - g++ -std=gnu++11 -I ./lib -c Res0.c $S7_LIB -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w $ETHERCAT_INC $S7_DEF + g++ -std=gnu++11 -I ./lib -c Res0.c -lsnap7 -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w $ETHERCAT_INC fi if [ $? -ne 0 ]; then echo "Error compiling C files" @@ -122,9 +104,9 @@ elif [ "$OPENPLC_PLATFORM" = "linux" ]; then ./glue_generator echo "Compiling main program..." if [ "$OPENPLC_DRIVER" = "sl_rp4" ]; then - g++ -std=gnu++11 *.cpp *.o -o openplc -I ./lib -pthread -fpermissive `pkg-config --cflags --libs libmodbus` $S7_LIB -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w $ETHERCAT_INC $S7_DEF -DSL_RP4 + g++ -std=gnu++11 *.cpp *.o -o openplc -I ./lib -pthread -fpermissive `pkg-config --cflags --libs libmodbus` -lsnap7 -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w $ETHERCAT_INC -DSL_RP4 else - g++ -std=gnu++11 *.cpp *.o -o openplc -I ./lib -pthread -fpermissive `pkg-config --cflags --libs libmodbus` $S7_LIB -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w $ETHERCAT_INC $S7_DEF + g++ -std=gnu++11 *.cpp *.o -o openplc -I ./lib -pthread -fpermissive `pkg-config --cflags --libs libmodbus` -lsnap7 -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w $ETHERCAT_INC fi if [ $? -ne 0 ]; then echo "Error compiling C files" @@ -138,9 +120,9 @@ elif [ "$OPENPLC_PLATFORM" = "rpi" ]; then echo "Compiling for Raspberry Pi" echo "Generating object files..." if [ "$OPENPLC_DRIVER" = "sequent" ]; then - g++ -std=gnu++11 -I ./lib -c Config0.c $S7_LIB -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w -DSEQUENT + g++ -std=gnu++11 -I ./lib -c Config0.c -lsnap7 -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w -DSEQUENT else - g++ -std=gnu++11 -I ./lib -c Config0.c $S7_LIB -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w + g++ -std=gnu++11 -I ./lib -c Config0.c -lsnap7 -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w fi if [ $? -ne 0 ]; then echo "Error compiling C files" @@ -148,9 +130,9 @@ elif [ "$OPENPLC_PLATFORM" = "rpi" ]; then exit 1 fi if [ "$OPENPLC_DRIVER" = "sequent" ]; then - g++ -std=gnu++11 -I ./lib -c Res0.c $S7_LIB -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w -DSEQUENT + g++ -std=gnu++11 -I ./lib -c Res0.c -lsnap7 -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w -DSEQUENT else - g++ -std=gnu++11 -I ./lib -c Res0.c $S7_LIB -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w + g++ -std=gnu++11 -I ./lib -c Res0.c -lsnap7 -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w fi if [ $? -ne 0 ]; then echo "Error compiling C files" @@ -161,9 +143,9 @@ elif [ "$OPENPLC_PLATFORM" = "rpi" ]; then ./glue_generator echo "Compiling main program..." if [ "$OPENPLC_DRIVER" = "sequent" ]; then - g++ -DSEQUENT -std=gnu++11 *.cpp *.o -o openplc -I ./lib -lrt -lpigpio -lpthread -fpermissive `pkg-config --cflags --libs libmodbus` $S7_LIB -lasiodnp3 -lasiopal -lopendnp3 -lopenpal $S7_DEF -w + g++ -DSEQUENT -std=gnu++11 *.cpp *.o -o openplc -I ./lib -lrt -lpigpio -lpthread -fpermissive `pkg-config --cflags --libs libmodbus` -lsnap7 -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w else - g++ -std=gnu++11 *.cpp *.o -o openplc -I ./lib -lrt -lpigpio -lpthread -fpermissive `pkg-config --cflags --libs libmodbus` $S7_LIB -lasiodnp3 -lasiopal -lopendnp3 -lopenpal $S7_DEF -w + g++ -std=gnu++11 *.cpp *.o -o openplc -I ./lib -lrt -lpigpio -lpthread -fpermissive `pkg-config --cflags --libs libmodbus` -lsnap7 -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w fi if [ $? -ne 0 ]; then echo "Error compiling C files" @@ -177,13 +159,13 @@ elif [ "$OPENPLC_PLATFORM" = "opi" ]; then WIRINGOP_INC="-I/usr/local/include -L/usr/local/lib -lwiringPi -lwiringPiDev" echo "Compiling for Orange Pi" echo "Generating object files..." - g++ -std=gnu++11 -I ./lib -c Config0.c $S7_LIB -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w $WIRINGOP_INC + g++ -std=gnu++11 -I ./lib -c Config0.c -lsnap7 -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w $WIRINGOP_INC if [ $? -ne 0 ]; then echo "Error compiling C files" echo "Compilation finished with errors!" exit 1 fi - g++ -std=gnu++11 -I ./lib -c Res0.c $S7_LIB -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w $WIRINGOP_INC + g++ -std=gnu++11 -I ./lib -c Res0.c -lsnap7 -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w $WIRINGOP_INC if [ $? -ne 0 ]; then echo "Error compiling C files" echo "Compilation finished with errors!" @@ -192,7 +174,7 @@ elif [ "$OPENPLC_PLATFORM" = "opi" ]; then echo "Generating glueVars..." ./glue_generator echo "Compiling main program..." - g++ -std=gnu++11 *.cpp *.o -o openplc -I ./lib -lrt -lpthread -fpermissive `pkg-config --cflags --libs libmodbus` $S7_LIB -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w $S7_DEF $WIRINGOP_INC + g++ -std=gnu++11 *.cpp *.o -o openplc -I ./lib -lrt -lpthread -fpermissive `pkg-config --cflags --libs libmodbus` -lsnap7 -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w $WIRINGOP_INC if [ $? -ne 0 ]; then echo "Error compiling C files" echo "Compilation finished with errors!" diff --git a/webserver/scripts/s7protocol b/webserver/scripts/s7protocol deleted file mode 100644 index 62fd0aa..0000000 --- a/webserver/scripts/s7protocol +++ /dev/null @@ -1 +0,0 @@ -#s7protocol \ No newline at end of file diff --git a/webserver/webserver.py b/webserver/webserver.py index 7823b6d..b3defec 100644 --- a/webserver/webserver.py +++ b/webserver/webserver.py @@ -93,6 +93,13 @@ def configure_runtime(): else: print("Disabling EtherNet/IP") openplc_runtime.stop_enip() + elif (row[0] == "snap7"): + if (row[1] != "false"): + print("Enabling S7 Protocol") + openplc_runtime.start_snap7() + else: + print("Disabling S7 Protocol") + openplc_runtime.stop_snap7() elif (row[0] == "Pstorage_polling"): if (row[1] != "disabled"): print("Enabling Persistent Storage with polling rate of " + str(int(row[1])) + " seconds") @@ -2188,6 +2195,9 @@ def settings(): slave_polling = str(row[1]) elif (row[0] == "Slave_timeout"): slave_timeout = str(row[1]) + elif (row[0] == "snap7"): + start_snap7 = str(row[1]) + if (modbus_port == 'disabled'): return_str += """ @@ -2206,6 +2216,25 @@ def settings(): return_str += """
+
+
+ + """ + else: + return_str += """ + + + + """ + + return_str += """

- """ - + """ + return_str += """

Slave Devices

@@ -2318,11 +2347,12 @@ def settings(): enip_port = flask.request.form.get('enip_server_port') pstorage_poll = flask.request.form.get('pstorage_thread_poll') start_run = flask.request.form.get('auto_run_text') + start_snap7 = flask.request.form.get('snap7_run_text') slave_polling = flask.request.form.get('slave_polling_period') slave_timeout = flask.request.form.get('slave_timeout') device_hostname = flask.request.form.get('device_hostname') - (modbus_port, dnp3_port, enip_port, pstorage_poll, start_run, slave_polling, slave_timeout, device_hostname) = sanitize_input(modbus_port, dnp3_port, enip_port, pstorage_poll, start_run, slave_polling, slave_timeout, device_hostname) + (modbus_port, dnp3_port, enip_port, pstorage_poll, start_run, start_snap7, slave_polling, slave_timeout, device_hostname) = sanitize_input(modbus_port, dnp3_port, enip_port, pstorage_poll, start_run, start_snap7, slave_polling, slave_timeout, device_hostname) # Change hostname if needed current_hostname = socket.gethostname() @@ -2369,6 +2399,13 @@ def settings(): cur.execute("UPDATE Settings SET Value = 'false' WHERE Key = 'Start_run_mode'") conn.commit() + if (start_snap7 == 'true'): + cur.execute("UPDATE Settings SET Value = 'true' WHERE Key = 'snap7'") + conn.commit() + else: + cur.execute("UPDATE Settings SET Value = 'false' WHERE Key = 'snap7'") + conn.commit() + cur.execute("UPDATE Settings SET Value = ? WHERE Key = 'Slave_polling'", (str(slave_polling),)) conn.commit()