Skip to content

Commit

Permalink
Merge pull request #31 from NikolasK-source/main
Browse files Browse the repository at this point in the history
update to 1.5.0
  • Loading branch information
NikolasK-source authored Jan 18, 2024
2 parents 61e716d + 9071ecc commit 4eebfdd
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 64 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.13.4 FATAL_ERROR)
# ======================================================================================================================

# project
project(Modbus_TCP_client_shm LANGUAGES CXX VERSION 1.4.0)
project(Modbus_TCP_client_shm LANGUAGES CXX VERSION 1.5.0)

# settings
set(Target "modbus-tcp-client-shm") # Executable name (without file extension!)
Expand Down
2 changes: 1 addition & 1 deletion libs/cxxsemaphore
Submodule cxxsemaphore updated 298 files
2 changes: 1 addition & 1 deletion libs/cxxshm
25 changes: 25 additions & 0 deletions src/license.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -585,4 +585,29 @@ void print_licenses(std::ostream &o) {
o << " LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM," << std::endl;
o << " OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE" << std::endl;
o << " SOFTWARE." << std::endl;
o << std::endl;
o << std::endl;
o << "cxxsemaphore Library (https://github.com/NikolasK-source/cxxsemaphore)" << std::endl;
o << std::endl;
o << " MIT License" << std::endl;
o << std::endl;
o << " Copyright (c) 2023 Nikolas Koesling" << std::endl;
o << std::endl;
o << " Permission is hereby granted, free of charge, to any person obtaining a copy" << std::endl;
o << " of this software and associated documentation files (the \"Software\"), to deal" << std::endl;
o << " in the Software without restriction, including without limitation the rights" << std::endl;
o << " to use, copy, modify, merge, publish, distribute, sublicense, and/or sell" << std::endl;
o << " copies of the Software, and to permit persons to whom the Software is" << std::endl;
o << " furnished to do so, subject to the following conditions:" << std::endl;
o << std::endl;
o << " The above copyright notice and this permission notice shall be included in all" << std::endl;
o << " copies or substantial portions of the Software." << std::endl;
o << std::endl;
o << " THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR" << std::endl;
o << " IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY," << std::endl;
o << " FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE" << std::endl;
o << " AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER" << std::endl;
o << " LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM," << std::endl;
o << " OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE" << std::endl;
o << " SOFTWARE." << std::endl;
}
120 changes: 66 additions & 54 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,82 +110,73 @@ int main(int argc, char **argv) {
#endif

// all command line arguments
// clang-format off
options.add_options()("i,host",
"host to listen for incoming connections",
cxxopts::value<std::string>()->default_value("any"))
("p,service",
options.add_options()(
"i,host", "host to listen for incoming connections", cxxopts::value<std::string>()->default_value("any"));
options.add_options()("p,service",
"service or port to listen for incoming connections",
cxxopts::value<std::string>()->default_value("502"))
("n,name-prefix",
"shared memory name prefix",
cxxopts::value<std::string>()->default_value("modbus_"))
("do-registers",
cxxopts::value<std::string>()->default_value("502"));
options.add_options()(
"n,name-prefix", "shared memory name prefix", cxxopts::value<std::string>()->default_value("modbus_"));
options.add_options()("do-registers",
"number of digital output registers",
cxxopts::value<std::size_t>()->default_value("65536"))
("di-registers",
"number of digital input registers",
cxxopts::value<std::size_t>()->default_value("65536"))
("ao-registers",
"number of analog output registers",
cxxopts::value<std::size_t>()->default_value("65536"))
("ai-registers",
"number of analog input registers",
cxxopts::value<std::size_t>()->default_value("65536"))
("m,monitor",
"output all incoming and outgoing packets to stdout")
("c,connections",
cxxopts::value<std::size_t>()->default_value("65536"));
options.add_options()(
"di-registers", "number of digital input registers", cxxopts::value<std::size_t>()->default_value("65536"));
options.add_options()(
"ao-registers", "number of analog output registers", cxxopts::value<std::size_t>()->default_value("65536"));
options.add_options()(
"ai-registers", "number of analog input registers", cxxopts::value<std::size_t>()->default_value("65536"));
options.add_options()("m,monitor", "output all incoming and outgoing packets to stdout");
options.add_options()("c,connections",
"number of allowed simultaneous Modbus Server connections.",
cxxopts::value<std::size_t>()->default_value("1"))
("r,reconnect",
"do not terminate if no Modbus Server is connected anymore.")
("byte-timeout",
cxxopts::value<std::size_t>()->default_value("1"));
options.add_options()("r,reconnect", "do not terminate if no Modbus Server is connected anymore.");
options.add_options()("byte-timeout",
"timeout interval in seconds between two consecutive bytes of the same message. "
"In most cases it is sufficient to set the response timeout. "
"Fractional values are possible.",
cxxopts::value<double>())
("response-timeout",
"In most cases it is sufficient to set the response timeout. "
"Fractional values are possible.",
cxxopts::value<double>());
options.add_options()("response-timeout",
"set the timeout interval in seconds used to wait for a response. "
"When a byte timeout is set, if the elapsed time for the first byte of response is longer "
"than the given timeout, a timeout is detected. "
"When byte timeout is disabled, the full confirmation response must be received before "
"expiration of the response timeout. "
"Fractional values are possible.",
cxxopts::value<double>())
cxxopts::value<double>());
#ifdef OS_LINUX
("t,tcp-timeout",
options.add_options()("t,tcp-timeout",
"tcp timeout in seconds. Set to 0 to use the system defaults (not recommended).",
cxxopts::value<std::size_t>()->default_value("5"))
cxxopts::value<std::size_t>()->default_value("5"));
#endif
("force",
options.add_options()("force",
"Force the use of the shared memory even if it already exists. "
"Do not use this option per default! "
"It should only be used if the shared memory of an improperly terminated instance continues "
"to exist as an orphan and is no longer used.")
("s,separate",
"to exist as an orphan and is no longer used.");
options.add_options()("s,separate",
"Use a separate shared memory for requests with the specified client id. "
"The client id (as hex value) is appended to the shared memory prefix (e.g. modbus_fc_DO)"
". You can specify multiple client ids by separating them with ','. "
"Use --separate-all to generate separate shared memories for all possible client ids.",
cxxopts::value<std::vector<std::uint8_t>>())
("separate-all",
cxxopts::value<std::vector<std::uint8_t>>());
options.add_options()("separate-all",
"like --separate, but for all client ids (creates 1028 shared memory files! "
"check/set 'ulimit -n' before using this option.)")
("semaphore",
"check/set 'ulimit -n' before using this option.)");
options.add_options()("semaphore",
"protect the shared memory with a named semaphore against simultaneous access",
cxxopts::value<std::string>())
("semaphore-force",
cxxopts::value<std::string>());
options.add_options()("semaphore-force",
"Force the use of the semaphore even if it already exists. "
"Do not use this option per default! "
"It should only be used if the semaphore of an improperly terminated instance continues "
"to exist as an orphan and is no longer used.")
("h,help",
"print usage")
("version",
"print version information")
("license",
"show licences");
// clang-format on
"to exist as an orphan and is no longer used.");
options.add_options()("b,permissions",
"permission bits that are applied when creating a shared memory.",
cxxopts::value<std::string>()->default_value("0640"));
options.add_options()("h,help", "print usage");
options.add_options()("version", "print version information");
options.add_options()("license", "show licences");

// parse arguments
cxxopts::ParseResult args;
Expand Down Expand Up @@ -213,6 +204,7 @@ int main(int argc, char **argv) {
std::cout << " - cxxopts by jarro2783 (https://github.com/jarro2783/cxxopts)" << std::endl;
std::cout << " - libmodbus by Stéphane Raimbault (https://github.com/stephane/libmodbus)" << std::endl;
std::cout << " - cxxshm (https://github.com/NikolasK-source/cxxshm)" << std::endl;
std::cout << " - cxxsemaphore (https://github.com/NikolasK-source/cxxsemaphore)" << std::endl;
return EX_OK;
}

Expand Down Expand Up @@ -274,6 +266,23 @@ int main(int argc, char **argv) {

const auto FORCE_SHM = args.count("force") > 0;

mode_t shm_permissions = 0660;
{
const auto shm_permissions_str = args["permissions"].as<std::string>();
bool fail = false;
std::size_t idx = 0;
try {
shm_permissions = std::stoul(shm_permissions_str, &idx, 0);
} catch (const std::exception &) { fail = true; }
fail = fail || idx != shm_permissions_str.size();

if (fail || (~static_cast<mode_t>(0x1FF) & shm_permissions) != 0) {
std::cerr << Print_Time::iso << " ERROR: Invalid file permissions \"" << shm_permissions_str << '"'
<< std::endl;
return EX_USAGE;
}
}

// check ulimit
std::size_t min_files =
CONNECTIONS + 5; // number of connections + stderr + stdout + stdin + signal_fd + server socket
Expand Down Expand Up @@ -303,7 +312,8 @@ int main(int argc, char **argv) {
args["ao-registers"].as<std::size_t>(),
args["ai-registers"].as<std::size_t>(),
args["name-prefix"].as<std::string>(),
FORCE_SHM);
FORCE_SHM,
shm_permissions);
} catch (const std::system_error &e) {
std::cerr << Print_Time::iso << " ERROR: " << e.what() << std::endl;
return EX_OSERR;
Expand All @@ -325,7 +335,8 @@ int main(int argc, char **argv) {
args["ao-registers"].as<std::size_t>(),
args["ai-registers"].as<std::size_t>(),
sstr.str(),
FORCE_SHM));
FORCE_SHM,
shm_permissions));
mb_mappings[i] = separate_mappings.back()->get_mapping();
} catch (const std::system_error &e) {
std::cerr << Print_Time::iso << " ERROR: " << e.what() << std::endl;
Expand All @@ -352,7 +363,8 @@ int main(int argc, char **argv) {
args["ao-registers"].as<std::size_t>(),
args["ai-registers"].as<std::size_t>(),
sstr.str(),
FORCE_SHM));
FORCE_SHM,
shm_permissions));
mb_mappings[a] = separate_mappings.back()->get_mapping();
} catch (const std::system_error &e) {
std::cerr << Print_Time::iso << " ERROR: " << e.what() << std::endl;
Expand Down
12 changes: 7 additions & 5 deletions src/modbus_shm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ Shm_Mapping::Shm_Mapping(std::size_t nb_bits,
std::size_t nb_registers,
std::size_t nb_input_registers,
const std::string &prefix,
bool force) {
bool force,
mode_t permissions) {
// check argument ranges
if (nb_bits > 0x10000 || !nb_bits) throw std::invalid_argument("invalid number of digital output registers.");
if (nb_input_bits > 0x10000 || !nb_input_bits)
Expand All @@ -36,10 +37,11 @@ Shm_Mapping::Shm_Mapping(std::size_t nb_bits,
mapping.nb_input_registers = static_cast<int>(nb_input_registers);

// create shm objects
shm_data[DO] = std::make_unique<cxxshm::SharedMemory>(prefix + "DO", nb_bits, false, !force);
shm_data[DI] = std::make_unique<cxxshm::SharedMemory>(prefix + "DI", nb_input_bits, false, !force);
shm_data[AO] = std::make_unique<cxxshm::SharedMemory>(prefix + "AO", 2 * nb_registers, false, !force);
shm_data[AI] = std::make_unique<cxxshm::SharedMemory>(prefix + "AI", 2 * nb_input_registers, false, !force);
shm_data[DO] = std::make_unique<cxxshm::SharedMemory>(prefix + "DO", nb_bits, false, !force, permissions);
shm_data[DI] = std::make_unique<cxxshm::SharedMemory>(prefix + "DI", nb_input_bits, false, !force, permissions);
shm_data[AO] = std::make_unique<cxxshm::SharedMemory>(prefix + "AO", 2 * nb_registers, false, !force, permissions);
shm_data[AI] =
std::make_unique<cxxshm::SharedMemory>(prefix + "AI", 2 * nb_input_registers, false, !force, permissions);

// set shm objects as modbus register storage
mapping.tab_bits = static_cast<uint8_t *>(shm_data[DO]->get_addr());
Expand Down
6 changes: 4 additions & 2 deletions src/modbus_shm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,15 @@ class Shm_Mapping final {
* @param nb_input_registers number of analog input registers (AI)
* @param shm_name_prefix name prefix of the created shared memory object
* @param force do not fail if the shared memory exist, but use the existing shared memory
* @param permissions shared memory file permissions
*/
Shm_Mapping(std::size_t nb_bits,
std::size_t nb_input_bits,
std::size_t nb_registers,
std::size_t nb_input_registers,
const std::string &shm_name_prefix = "modbus_",
bool force = false);
const std::string &shm_name_prefix,
bool force,
mode_t permissions);

~Shm_Mapping() = default;

Expand Down

0 comments on commit 4eebfdd

Please sign in to comment.