diff --git a/.gitignore b/.gitignore index 4cfb858..2a9f16f 100644 --- a/.gitignore +++ b/.gitignore @@ -466,3 +466,5 @@ modules.order Module.symvers Mkfile.old dkms.conf +src/generated/version_info.cpp +src/generated/version_info.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 483cfaf..a921c89 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,7 +9,7 @@ cmake_minimum_required(VERSION 3.22.0 FATAL_ERROR) # ====================================================================================================================== # project -project(stdin-to-modbus-shm LANGUAGES CXX VERSION 1.4.0) +project(stdin-to-modbus-shm LANGUAGES CXX VERSION 1.5.0) # settings set(Target "stdin-to-modbus-shm") # Executable name (without file extension!) diff --git a/src/generated/version_info.cpp b/src/generated/version_info.cpp deleted file mode 100644 index 8cda9f1..0000000 --- a/src/generated/version_info.cpp +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (C) 2024 Nikolas Koesling . - * This program is free software. You can redistribute it and/or modify it under the terms of the MIT License. - */ - -// This file is automatically generated. DO NOT CHANGE! - -#include "version_info.hpp" - -const std::string stdin_to_modbus_shm_version_info::VERSION_STR(PROJECT_VERSION); -const std::string stdin_to_modbus_shm_version_info::NAME(PROJECT_NAME); -const std::string stdin_to_modbus_shm_version_info::COMPILER(COMPILER_INFO); -const std::string stdin_to_modbus_shm_version_info::SYSTEM(SYSTEM_INFO); -const std::string stdin_to_modbus_shm_version_info::GIT_HASH("920708ae1f4add3ee443228eb0ca0966a059ab8b-dirty"); diff --git a/src/generated/version_info.hpp b/src/generated/version_info.hpp deleted file mode 100644 index 8c57c82..0000000 --- a/src/generated/version_info.hpp +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2024 Nikolas Koesling . - * This program is free software. You can redistribute it and/or modify it under the terms of the MIT License. - */ - -// This file is automatically generated. DO NOT CHANGE! - -#include - -/** - * @brief struct that contains version information - * @details contains only static members --> no instance can be created - */ -struct stdin_to_modbus_shm_version_info { - //* Mayor version number - static constexpr int MAYOR = PROJECT_VERSION_MAJOR; - - //* Minor version number - static constexpr int MINOR = PROJECT_VERSION_MINOR; - - //* Patchlevel - static constexpr int PATCH = PROJECT_VERSION_PATCH; - - //* Complete version as string - static const std::string VERSION_STR; - - //* cmake project name - static const std::string NAME; - - //* compiler info - static const std::string COMPILER; - - //* host system info - static const std::string SYSTEM; - - /** - * @brief git hash - * @details - * Contains the complete git hash. - * If there are uncommitted changes in the repository, - * the suffix '-dirty' is appended - */ - static const std::string GIT_HASH; - -private: - stdin_to_modbus_shm_version_info() = default; -}; diff --git a/src/main.cpp b/src/main.cpp index 71df39c..b83dfd3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -62,7 +62,7 @@ constexpr std::array TERM_SIGNALS = {SIGINT, */ int main(int argc, char **argv) { const std::string exe_name = std::filesystem::path(*argv).filename().string(); - cxxopts::Options options(exe_name, "Read instructions from stdin and write them to a modbus shared memory"); + cxxopts::Options options(exe_name, "Read instructions from stdin and write them to a Modbus shared memory"); auto exit_usage = [&exe_name]() { std::cerr << "Use '" << exe_name << " --help' for more information.\n"; @@ -115,6 +115,11 @@ int main(int argc, char **argv) { options.add_options("shared memory")("semaphore-timeout", "maximum time (in seconds) to wait for semaphore (default: 0.1)", cxxopts::value()->default_value("0.1")); + options.add_options("shared_memory")( + "pid", + "terminate application if application with given pid is terminated. Provide " + "the pid of the Modbus client to terminate when the Modbus client is terminated.", + cxxopts::value()); // parse arguments cxxopts::ParseResult args; @@ -129,7 +134,7 @@ int main(int argc, char **argv) { std::cout << "Data input format: reg_type:address:value[:data_type]" << '\n'; std::cout << " reg_type : modbus register type: [do|di|ao|ai]" << '\n'; std::cout << " address : address of the target register: [0-" << MAX_MODBUS_REGS - 1 << "]" << '\n'; - std::cout << " The actual maximum register depends on the size of the modbus shared memory." + std::cout << " The actual maximum register depends on the size of the Modbus shared memory." << '\n'; std::cout << " value : value that is written to the target register" << '\n'; std::cout << " Some string constants are available. The input format depends on the type of " @@ -364,25 +369,25 @@ int main(int argc, char **argv) { // check shared mem if (shm_do->get_size() > MAX_MODBUS_REGS) { - std::cerr << "shared memory '" << shm_do->get_name() << "is to large to be a valid modbus shared memory." + std::cerr << "shared memory '" << shm_do->get_name() << "is to large to be a valid Modbus shared memory." << '\n'; return EX_SOFTWARE; } if (shm_di->get_size() > MAX_MODBUS_REGS) { - std::cerr << "shared memory '" << shm_di->get_name() << "' is to large to be a valid modbus shared memory." + std::cerr << "shared memory '" << shm_di->get_name() << "' is to large to be a valid Modbus shared memory." << '\n'; return EX_SOFTWARE; } if (shm_ao->get_size() / 2 > MAX_MODBUS_REGS) { - std::cerr << "shared memory '" << shm_ao->get_name() << "' is to large to be a valid modbus shared memory." + std::cerr << "shared memory '" << shm_ao->get_name() << "' is to large to be a valid Modbus shared memory." << '\n'; return EX_SOFTWARE; } if (shm_ai->get_size() / 2 > MAX_MODBUS_REGS) { - std::cerr << "shared memory '" << shm_ai->get_name() << "' is to large to be a valid modbus shared memory." + std::cerr << "shared memory '" << shm_ai->get_name() << "' is to large to be a valid Modbus shared memory." << '\n'; return EX_SOFTWARE; } @@ -395,13 +400,13 @@ int main(int argc, char **argv) { } if (shm_ao->get_size() % 2) { - std::cerr << "the size of shared memory '" << shm_ao->get_name() << "' is odd. It is not a valid modbus shm." + std::cerr << "the size of shared memory '" << shm_ao->get_name() << "' is odd. It is not a valid Modbus shm." << '\n'; return EX_SOFTWARE; } if (shm_ai->get_size() % 2) { - std::cerr << "the size of shared memory '" << shm_ai->get_name() << "' is odd. It is not a valid modbus shm." + std::cerr << "the size of shared memory '" << shm_ai->get_name() << "' is odd. It is not a valid Modbus shm." << '\n'; return EX_SOFTWARE; } @@ -425,6 +430,12 @@ int main(int argc, char **argv) { std::cerr << e.what() << '\n'; return EX_SOFTWARE; } + } else { + std::cerr << "WARNING: No semaphore specified.\n" + " Concurrent access to the shared memory is possible.\n" + " This can result in CORRUPTED DATA.\n" + " Use --semaphore to specify a semaphore that is provided by the Modbus client.\n"; + std::cerr << std::flush; } const double SEMAPHORE_TIMEOUT_S = args["semaphore-timeout"].as(); @@ -439,6 +450,22 @@ int main(int argc, char **argv) { static_cast(std::modf(SEMAPHORE_TIMEOUT_S, &modf_dummy) * 1'000'000), }; + // modbus client pid + pid_t mb_client_pid = 0; + bool use_mb_client_pid = false; + if (args.count("pid")) { + mb_client_pid = args["pid"].as(); + use_mb_client_pid = true; + } else { + std::cerr << "WARNING: No Modbus client pid provided.\n" + " Terminating the Modbus client application WILL NOT result in the termination of this " + "application.\n" + " This application WILL NOT connect to the shared memory of a restarted Modbus client.\n" + " Use --pid to specify the pid of the Modbus client.\n" + " Command line example: --pid $(pidof modbus-tcp-client-shm)\n" + << std::flush; + } + std::cout << std::fixed; auto last_time = std::chrono::steady_clock::now(); @@ -648,6 +675,20 @@ int main(int argc, char **argv) { input_thread.detach(); while (!terminate) { + if (use_mb_client_pid) { + // check if modbus client is still alive + int tmp = kill(mb_client_pid, 0); + if (tmp == -1) { + if (errno == ESRCH) { + std::cerr << "Modbus client (pid=" << mb_client_pid << ") no longer alive.\n" << std::flush; + } else { + perror("failed to send signal to the Modbus client"); + } + terminate = true; + break; + } + } + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // NOLINT }