From a705a8a6780a879290fb1a3ea727072a49fc9562 Mon Sep 17 00:00:00 2001 From: Rainer Kordmaa Date: Fri, 11 Nov 2022 18:16:34 +0200 Subject: [PATCH 01/13] ignore CMakeFiles --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 7f4628e..32801f2 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ CMakeSettings.json webserver/bin webserver/build obj/ +utils/dnp3_src/CMakeFiles/ From 75be134d3483dc4bcf59604e8bc8486278bd0a25 Mon Sep 17 00:00:00 2001 From: Rainer Kordmaa Date: Fri, 11 Nov 2022 19:57:07 +0200 Subject: [PATCH 02/13] Checks and initializes default database if needed --- webserver/check_openplc_db.py | 156 ++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 webserver/check_openplc_db.py diff --git a/webserver/check_openplc_db.py b/webserver/check_openplc_db.py new file mode 100644 index 0000000..b895567 --- /dev/null +++ b/webserver/check_openplc_db.py @@ -0,0 +1,156 @@ +############################################################################## +# +# Checks and initializes default database if needed +# +############################################################################## + + +import sqlite3 +from sqlite3 import Error +import os + +builddir = r"build/" +dbfile = r"build/openplc.db" + +createTablePrograms = r"""CREATE TABLE `Programs` ( + `Prog_ID` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + `Name` TEXT NOT NULL, + `Description` TEXT, + `File` TEXT NOT NULL, + `Date_upload` INTEGER NOT NULL +)""" + +insertblankProgram = r"INSERT INTO Programs VALUES (1, 'Blank Program', 'Dummy empty program', 'blank_program.st', 1527184953)" + +createTableSettings = r"""CREATE TABLE `Settings` ( + `Key` TEXT NOT NULL UNIQUE, + `Value` TEXT NOT NULL, + PRIMARY KEY(`Key`) +)""" + + +createTableSlave_dev = r"""CREATE TABLE "Slave_dev" ( + "dev_id" INTEGER NOT NULL UNIQUE, + "dev_name" TEXT NOT NULL UNIQUE, + "dev_type" TEXT NOT NULL, + "slave_id" INTEGER NOT NULL, + "com_port" TEXT, + "baud_rate" INTEGER, + "parity" TEXT, + "data_bits" INTEGER, + "stop_bits" INTEGER, + "ip_address" TEXT, + "ip_port" INTEGER, + "di_start" INTEGER NOT NULL, + "di_size" INTEGER NOT NULL, + "coil_start" INTEGER NOT NULL, + "coil_size" INTEGER NOT NULL, + "ir_start" INTEGER NOT NULL, + "ir_size" INTEGER NOT NULL, + "hr_read_start" INTEGER NOT NULL, + "hr_read_size" INTEGER NOT NULL, + "hr_write_start" INTEGER NOT NULL, + "hr_write_size" INTEGER NOT NULL, + "pause" INTEGER, + PRIMARY KEY("dev_id" AUTOINCREMENT) +)""" + +createTableUsers = r"""CREATE TABLE "Users" ( + `user_id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + `name` TEXT NOT NULL, + `username` TEXT NOT NULL UNIQUE, + `email` TEXT, + `password` TEXT NOT NULL, + `pict_file` TEXT +)""" + +insertDefaultUser = r"INSERT INTO Users VALUES (10, 'OpenPLC User', 'openplc', 'openplc@openplc.com','openplc', NULL)" + + + +gettablesQuery = r"""SELECT name FROM sqlite_master + WHERE type='table';""" +getsettingsQuery = r"""SELECT Key FROM Settings""" + +# return true if created fresh table +def checkTableExists(conn, tablename, tablecreatecommand): + cur = conn.cursor() + cur.execute(gettablesQuery) + rows = cur.fetchall() + for row in rows: + if row[0] == tablename: + cur.close() + return False + print(tablename, " didn't exist, creating") + cur.execute(tablecreatecommand) + cur.close() + return True + +def checkTablePrograms(conn): + if checkTableExists(conn, "Programs", createTablePrograms): + cur = conn.cursor() + cur.execute(insertblankProgram) + cur.close() + return + +def checkTableUsers(conn): + if checkTableExists(conn, "Users", createTableUsers): + cur = conn.cursor() + cur.execute(insertDefaultUser) + cur.close() + return + +# if code has new features, old database might not have required settings +def checkSettingExists(conn, setting, defaultvalue): + cur = conn.cursor() + cur.execute(getsettingsQuery) + rows = cur.fetchall() + for row in rows: + if row[0] == setting: + cur.close() + return False + print(setting, " didn't exist, creating") + cur.execute("INSERT INTO Settings VALUES (?, ?)", (setting, defaultvalue)) + cur.close() + return + +def checkTableSettings(conn): + checkTableExists(conn, "Settings", createTableSettings) + checkSettingExists(conn, 'Modbus_port', '502') + checkSettingExists(conn, 'Dnp3_port', '20000') + checkSettingExists(conn, 'Start_run_mode', 'false') + checkSettingExists(conn, 'Slave_polling', '100') + checkSettingExists(conn, 'Slave_timeout', '1000') + checkSettingExists(conn, 'Enip_port', '44818') + checkSettingExists(conn, 'Pstorage_polling', 'disabled') + return + +def checkTableSlave_dev(conn): + checkTableExists(conn, "Slave_dev", createTableSlave_dev) + return + +def create_connection(): + """ create a database connection to a SQLite database """ + if not os.path.exists(builddir): + os.mkdir(builddir) + + conn = None + try: + conn = sqlite3.connect(dbfile) + checkTablePrograms(conn) + checkTableUsers(conn) + checkTableSettings(conn) + checkTableSlave_dev(conn) + except Error as e: + print(sqlite3.version) + print(e) + finally: + if conn: + # NB, if you close without commiting, last actions might not be saved + conn.commit() + return conn + + +if __name__ == '__main__': + conn = create_connection() + conn.close() \ No newline at end of file From ebc8ca9a8bcf2eb0d2f5aa0c0b912fee116de921 Mon Sep 17 00:00:00 2001 From: Rainer Kordmaa Date: Fri, 11 Nov 2022 21:25:16 +0200 Subject: [PATCH 03/13] added hook for ethercat configure --- webserver/core/interactive_server.cpp | 44 +++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/webserver/core/interactive_server.cpp b/webserver/core/interactive_server.cpp index 1147874..879d393 100644 --- a/webserver/core/interactive_server.cpp +++ b/webserver/core/interactive_server.cpp @@ -36,8 +36,11 @@ #include #include "ladder.h" +#define BUFFER_SIZE 1024 //Global Variables +bool ethercat_configured = 0; +char ethercat_conf_file[BUFFER_SIZE]; bool run_modbus = 0; uint16_t modbus_port = 502; bool run_dnp3 = 0; @@ -58,6 +61,12 @@ pthread_t dnp3_thread; pthread_t enip_thread; pthread_t pstorage_thread; +//----------------------------------------------------------------------------- +// Configure Ethercat +//----------------------------------------------------------------------------- +int configureEthercat(){ + return 1; +} //----------------------------------------------------------------------------- // Start the Modbus Thread //----------------------------------------------------------------------------- @@ -111,6 +120,28 @@ int readCommandArgument(unsigned char *command) return atoi(argument); } +//----------------------------------------------------------------------------- +// Read string argument from a command function +//----------------------------------------------------------------------------- +unsigned char *readCommandArgumentStr(unsigned char *command) +{ + int i = 0; + int j = 0; + unsigned char *argument; + argument = (unsigned char *)malloc(1024 * sizeof(unsigned char)); + + while (command[i] != '(' && command[i] != '\0') i++; + if (command[i] == '(') i++; + while (command[i] != ')' && command[i] != '\0') + { + argument[j] = command[i]; + i++; + j++; + argument[j] = '\0'; + } + + return argument; +} //----------------------------------------------------------------------------- // Create the socket and bind it. Returns the file descriptor for the socket @@ -235,6 +266,19 @@ void processCommand(unsigned char *buffer, int client_fd) run_openplc = 0; processing_command = false; } + else if (strncmp(buffer, "start_ethercat(", 15) == 0) + { + processing_command = true; + char *argument; + argument = (char)readCommandArgumentStr(buffer); + strcpy(ethercat_conf_file, argument); + free(argument); + sprintf(log_msg, "Issued start_ethercat() command to start with config: %s\n", ethercat_conf_file); + log(log_msg); + //Configure ethercat + ethercat_configured = configureEthercat(); + processing_command = false; + } else if (strncmp(buffer, "start_modbus(", 13) == 0) { processing_command = true; From a5075901303644bb1b3d91c7072d3ebc76e7de20 Mon Sep 17 00:00:00 2001 From: Rainer Kordmaa Date: Sat, 12 Nov 2022 15:29:15 +0200 Subject: [PATCH 04/13] include ethercat erc --- background_installer.sh | 8 ++++++++ webserver/core/main.cpp | 2 ++ webserver/scripts/compile_program.sh | 2 +- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/background_installer.sh b/background_installer.sh index 976f329..a9df6cb 100755 --- a/background_installer.sh +++ b/background_installer.sh @@ -98,6 +98,14 @@ function install_all_libs { fi cd ../.. + echo "" + echo "[EtherCAT]" + cd utils/ethercat_src + mkdir build + cmake -S . -B build/ + cmake --build build/ + cd ../.. + echo "" echo "[OPEN DNP3]" cd utils/dnp3_src diff --git a/webserver/core/main.cpp b/webserver/core/main.cpp index 97ef94f..3ff06c1 100644 --- a/webserver/core/main.cpp +++ b/webserver/core/main.cpp @@ -32,6 +32,7 @@ #include "iec_types.h" #include "ladder.h" +#include "ethercat_src.h" #define OPLC_CYCLE 50000000 @@ -209,6 +210,7 @@ int main(int argc,char **argv) //====================================================== // HARDWARE INITIALIZATION //====================================================== + ethercat_configure("../utils/ethercat_src/conf/ethercatcfg.txt"); initializeHardware(); initializeMB(); initCustomLayer(); diff --git a/webserver/scripts/compile_program.sh b/webserver/scripts/compile_program.sh index 1993678..9ccb7c7 100755 --- a/webserver/scripts/compile_program.sh +++ b/webserver/scripts/compile_program.sh @@ -78,7 +78,7 @@ elif [ "$OPENPLC_PLATFORM" = "linux" ]; then echo "Generating glueVars..." ./glue_generator echo "Compiling main program..." - g++ -std=gnu++11 *.cpp *.o -o openplc -I ./lib -pthread -fpermissive `pkg-config --cflags --libs libmodbus` -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w + g++ -std=gnu++11 *.cpp *.o -o openplc -I ./lib -pthread -fpermissive `pkg-config --cflags --libs libmodbus` -lasiodnp3 -lasiopal -lopendnp3 -lopenpal -w -L../../utils/ethercat_src/build/lib -lethercat_src -I../../utils/ethercat_src/src if [ $? -ne 0 ]; then echo "Error compiling C files" echo "Compilation finished with errors!" From edb5516d9b56029628246618597a1af91c75e705 Mon Sep 17 00:00:00 2001 From: Rainer Kordmaa Date: Sat, 12 Nov 2022 15:41:41 +0200 Subject: [PATCH 05/13] ethercat install and ldconfig --- background_installer.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/background_installer.sh b/background_installer.sh index a9df6cb..f49110a 100755 --- a/background_installer.sh +++ b/background_installer.sh @@ -104,6 +104,8 @@ function install_all_libs { mkdir build cmake -S . -B build/ cmake --build build/ + sudo cmake --install build/ + sudo /sbin/ldconfig -v cd ../.. echo "" From 85ed4f0b41d5cb713de6a278911ca582daf66203 Mon Sep 17 00:00:00 2001 From: Rainer Kordmaa Date: Sat, 12 Nov 2022 17:38:42 +0200 Subject: [PATCH 06/13] ethercat_callcyclic --- webserver/core/main.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/webserver/core/main.cpp b/webserver/core/main.cpp index 3ff06c1..4e91cc4 100644 --- a/webserver/core/main.cpp +++ b/webserver/core/main.cpp @@ -266,6 +266,10 @@ int main(int argc,char **argv) updateBuffersIn(); //read input image pthread_mutex_lock(&bufferLock); //lock mutex + if(ethercat_callcyclic(BUFFER_SIZE, (uint8_t*)bool_input, (uint8_t*)bool_output, (uint8_t*)byte_input, (uint8_t*)byte_output, (uint16_t*)int_input, (uint16_t*)int_output)){ + printf("EtherCAT cyclic failed\n"); + break; + } updateCustomIn(); updateBuffersIn_MB(); //update input image table with data from slave devices handleSpecialFunctions(); From b021107e02df5d445fb752dd8eca030802471afe Mon Sep 17 00:00:00 2001 From: Rainer Kordmaa Date: Sat, 12 Nov 2022 18:04:17 +0200 Subject: [PATCH 07/13] ethercat_src added to fixes --- .gitmodules | 3 +++ utils/ethercat_src | 1 + 2 files changed, 4 insertions(+) create mode 100644 .gitmodules create mode 160000 utils/ethercat_src diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..33da6b5 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "utils/ethercat_src"] + path = utils/ethercat_src + url = https://github.com/r2k-in-the-vortex/ethercat_src.git diff --git a/utils/ethercat_src b/utils/ethercat_src new file mode 160000 index 0000000..906092f --- /dev/null +++ b/utils/ethercat_src @@ -0,0 +1 @@ +Subproject commit 906092f407638c426ed7d37e4ad1b6db7333649f From f182c8de0c3e04e2929afb3632630c4b907428c1 Mon Sep 17 00:00:00 2001 From: Rainer Kordmaa Date: Sat, 12 Nov 2022 18:57:40 +0200 Subject: [PATCH 08/13] mkdir -p --- background_installer.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/background_installer.sh b/background_installer.sh index f49110a..6c44cee 100755 --- a/background_installer.sh +++ b/background_installer.sh @@ -101,7 +101,7 @@ function install_all_libs { echo "" echo "[EtherCAT]" cd utils/ethercat_src - mkdir build + mkdir -p build cmake -S . -B build/ cmake --build build/ sudo cmake --install build/ From b9de85b76cb0ab76fbec4fefc13d41d739a4cddb Mon Sep 17 00:00:00 2001 From: Rainer Kordmaa Date: Sat, 12 Nov 2022 19:00:55 +0200 Subject: [PATCH 09/13] build fixes --- background_installer.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/background_installer.sh b/background_installer.sh index 6c44cee..12ef97b 100755 --- a/background_installer.sh +++ b/background_installer.sh @@ -102,10 +102,10 @@ function install_all_libs { echo "[EtherCAT]" cd utils/ethercat_src mkdir -p build - cmake -S . -B build/ - cmake --build build/ + sudo cmake -S . -B build/ + sudo cmake --build build/ sudo cmake --install build/ - sudo /sbin/ldconfig -v + sudo /sbin/ldconfig -v | grep ethercat cd ../.. echo "" From 537c39ee99d558ba08d818d628a418990b622d2b Mon Sep 17 00:00:00 2001 From: Rainer Kordmaa Date: Sat, 12 Nov 2022 19:54:10 +0200 Subject: [PATCH 10/13] fix --- utils/ethercat_src | 2 +- webserver/core/main.cpp | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/utils/ethercat_src b/utils/ethercat_src index 906092f..71d757c 160000 --- a/utils/ethercat_src +++ b/utils/ethercat_src @@ -1 +1 @@ -Subproject commit 906092f407638c426ed7d37e4ad1b6db7333649f +Subproject commit 71d757c1aac30e2af0054e005e95d14079e711c6 diff --git a/webserver/core/main.cpp b/webserver/core/main.cpp index 4e91cc4..6c09591 100644 --- a/webserver/core/main.cpp +++ b/webserver/core/main.cpp @@ -266,9 +266,15 @@ int main(int argc,char **argv) updateBuffersIn(); //read input image pthread_mutex_lock(&bufferLock); //lock mutex - if(ethercat_callcyclic(BUFFER_SIZE, (uint8_t*)bool_input, (uint8_t*)bool_output, (uint8_t*)byte_input, (uint8_t*)byte_output, (uint16_t*)int_input, (uint16_t*)int_output)){ + if(ethercat_callcyclic(BUFFER_SIZE, (uint8_t**)bool_input, (uint8_t**)bool_output, (uint8_t*)byte_input, (uint8_t*)byte_output, (uint16_t*)int_input, (uint16_t*)int_output)){ printf("EtherCAT cyclic failed\n"); break; + } + if(bool_output[0][0]){ + printf("%QX0.0 := TRUE %i\n", bool_output[0]); + } + else{ + printf("%QX0.0 := FALSE\n"); } updateCustomIn(); updateBuffersIn_MB(); //update input image table with data from slave devices From c66b467407ec703eaf8ac818eb324575aaa58575 Mon Sep 17 00:00:00 2001 From: Rainer Kordmaa Date: Sat, 12 Nov 2022 20:14:22 +0200 Subject: [PATCH 11/13] fix --- utils/ethercat_src | 2 +- webserver/core/main.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/utils/ethercat_src b/utils/ethercat_src index 71d757c..9e7007f 160000 --- a/utils/ethercat_src +++ b/utils/ethercat_src @@ -1 +1 @@ -Subproject commit 71d757c1aac30e2af0054e005e95d14079e711c6 +Subproject commit 9e7007f5f0466431c6eff65e9e860f445961f6c8 diff --git a/webserver/core/main.cpp b/webserver/core/main.cpp index 6c09591..54e429c 100644 --- a/webserver/core/main.cpp +++ b/webserver/core/main.cpp @@ -266,12 +266,12 @@ int main(int argc,char **argv) updateBuffersIn(); //read input image pthread_mutex_lock(&bufferLock); //lock mutex - if(ethercat_callcyclic(BUFFER_SIZE, (uint8_t**)bool_input, (uint8_t**)bool_output, (uint8_t*)byte_input, (uint8_t*)byte_output, (uint16_t*)int_input, (uint16_t*)int_output)){ + if(ethercat_callcyclic(BUFFER_SIZE, (uint8_t***)bool_input, (uint8_t***)bool_output, (uint8_t**)byte_input, (uint8_t**)byte_output, (uint16_t**)int_input, (uint16_t**)int_output)){ printf("EtherCAT cyclic failed\n"); break; } - if(bool_output[0][0]){ - printf("%QX0.0 := TRUE %i\n", bool_output[0]); + if(&bool_output[0][0]){ + printf("%QX0.0 := TRUE %i\n", &bool_output[0]); } else{ printf("%QX0.0 := FALSE\n"); From c8f003bbca349430ecc3f3a8cecbd3da9b81b0d5 Mon Sep 17 00:00:00 2001 From: Rainer Kordmaa Date: Sat, 12 Nov 2022 23:16:56 +0200 Subject: [PATCH 12/13] Ethercat doing a thing --- webserver/core/main.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/webserver/core/main.cpp b/webserver/core/main.cpp index 54e429c..9f350df 100644 --- a/webserver/core/main.cpp +++ b/webserver/core/main.cpp @@ -266,15 +266,10 @@ int main(int argc,char **argv) updateBuffersIn(); //read input image pthread_mutex_lock(&bufferLock); //lock mutex + if(ethercat_callcyclic(BUFFER_SIZE, (uint8_t***)bool_input, (uint8_t***)bool_output, (uint8_t**)byte_input, (uint8_t**)byte_output, (uint16_t**)int_input, (uint16_t**)int_output)){ printf("EtherCAT cyclic failed\n"); break; - } - if(&bool_output[0][0]){ - printf("%QX0.0 := TRUE %i\n", &bool_output[0]); - } - else{ - printf("%QX0.0 := FALSE\n"); } updateCustomIn(); updateBuffersIn_MB(); //update input image table with data from slave devices From 757d934a50a9893e4a4cc924fbb30aedd746e379 Mon Sep 17 00:00:00 2001 From: Rainer Kordmaa Date: Sun, 13 Nov 2022 10:27:12 +0200 Subject: [PATCH 13/13] callbacks instead --- webserver/core/main.cpp | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/webserver/core/main.cpp b/webserver/core/main.cpp index 9f350df..4316e24 100644 --- a/webserver/core/main.cpp +++ b/webserver/core/main.cpp @@ -182,6 +182,15 @@ void handleSpecialFunctions() //insert other special functions below } +// pointers to IO *array[const][const] from cpp to c and back again don't work as expected, so instead callbacks +u_int8_t *bool_input_call_back(int a, int b){ return bool_input[a][b]; } +u_int8_t *bool_output_call_back(int a, int b){ return bool_output[a][b]; } +u_int8_t *byte_input_call_back(int a){ return byte_input[a]; } +u_int8_t *byte_output_call_back(int a){ return byte_output[a]; } +u_int16_t *int_input_call_back(int a){ return int_input[a]; } +u_int16_t *int_output_call_back(int a){ return int_output[a]; } + + int main(int argc,char **argv) { unsigned char log_msg[1000]; @@ -227,6 +236,8 @@ int main(int argc,char **argv) readPersistentStorage(); //pthread_t persistentThread; //pthread_create(&persistentThread, NULL, persistentStorage, NULL); + + #ifdef __linux__ //====================================================== @@ -263,11 +274,25 @@ int main(int argc,char **argv) //attached to the user variables glueVars(); + boolvar_call_back bool_input_callback = bool_input_call_back; + boolvar_call_back bool_output_callback = bool_output_call_back; + int8var_call_back byte_input_callback = byte_input_call_back; + int8var_call_back byte_output_callback = byte_output_call_back; + int16var_call_back int_input_callback = int_input_call_back; + int16var_call_back int_output_callback = int_output_call_back; + updateBuffersIn(); //read input image pthread_mutex_lock(&bufferLock); //lock mutex - if(ethercat_callcyclic(BUFFER_SIZE, (uint8_t***)bool_input, (uint8_t***)bool_output, (uint8_t**)byte_input, (uint8_t**)byte_output, (uint16_t**)int_input, (uint16_t**)int_output)){ + + if(ethercat_callcyclic(BUFFER_SIZE, + bool_input_callback, + bool_output_callback, + byte_input_callback, + byte_output_callback, + int_input_callback, + int_output_callback)){ printf("EtherCAT cyclic failed\n"); break; }