mirror of
https://github.com/slsdetectorgroup/slsDetectorPackage.git
synced 2025-07-14 03:41:49 +02:00
Merge pull request #1167 from slsdetectorgroup/dev/multirxr_proper_cleanup_on_ctrlc
Dev/multirxr proper cleanup on ctrl + c and versioning
This commit is contained in:
@ -152,12 +152,12 @@ int main(int argc, char *argv[]) {
|
|||||||
strcpy(version, APIXILINXCTB);
|
strcpy(version, APIXILINXCTB);
|
||||||
#endif
|
#endif
|
||||||
LOG(logINFO, ("SLS Detector Server Version: %s\n", version));
|
LOG(logINFO, ("SLS Detector Server Version: %s\n", version));
|
||||||
exit(EXIT_SUCCESS);
|
return EXIT_SUCCESS;
|
||||||
|
|
||||||
case 'p':
|
case 'p':
|
||||||
if (sscanf(optarg, "%d", &portno) != 1) {
|
if (sscanf(optarg, "%d", &portno) != 1) {
|
||||||
LOG(logERROR, ("Cannot scan port argument\n%s", helpMessage));
|
LOG(logERROR, ("Cannot scan port argument\n%s", helpMessage));
|
||||||
exit(EXIT_FAILURE);
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
LOG(logINFO, ("Detected port: %d\n", portno));
|
LOG(logINFO, ("Detected port: %d\n", portno));
|
||||||
break;
|
break;
|
||||||
@ -188,7 +188,7 @@ int main(int argc, char *argv[]) {
|
|||||||
ignoreConfigFileFlag = 1;
|
ignoreConfigFileFlag = 1;
|
||||||
#else
|
#else
|
||||||
LOG(logERROR, ("No server config files for this detector\n"));
|
LOG(logERROR, ("No server config files for this detector\n"));
|
||||||
exit(EXIT_FAILURE);
|
return EXIT_FAILURE;
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -196,11 +196,11 @@ int main(int argc, char *argv[]) {
|
|||||||
#if !defined(VIRTUAL) && !defined(EIGERD)
|
#if !defined(VIRTUAL) && !defined(EIGERD)
|
||||||
LOG(logERROR, ("Cannot set master via the detector server for this "
|
LOG(logERROR, ("Cannot set master via the detector server for this "
|
||||||
"detector\n"));
|
"detector\n"));
|
||||||
exit(EXIT_FAILURE);
|
return EXIT_FAILURE;
|
||||||
#elif defined(GOTTHARD2D) || defined(EIGERD) || defined(MYTHEN3D)
|
#elif defined(GOTTHARD2D) || defined(EIGERD) || defined(MYTHEN3D)
|
||||||
if (sscanf(optarg, "%d", &masterCommandLine) != 1) {
|
if (sscanf(optarg, "%d", &masterCommandLine) != 1) {
|
||||||
LOG(logERROR, ("Cannot scan master argument\n%s", helpMessage));
|
LOG(logERROR, ("Cannot scan master argument\n%s", helpMessage));
|
||||||
exit(EXIT_FAILURE);
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
if (masterCommandLine == 1) {
|
if (masterCommandLine == 1) {
|
||||||
LOG(logINFO, ("Detector Master mode\n"));
|
LOG(logINFO, ("Detector Master mode\n"));
|
||||||
@ -209,7 +209,7 @@ int main(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
LOG(logERROR, ("No master implemented for this detector server\n"));
|
LOG(logERROR, ("No master implemented for this detector server\n"));
|
||||||
exit(EXIT_FAILURE);
|
return EXIT_FAILURE;
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -217,7 +217,7 @@ int main(int argc, char *argv[]) {
|
|||||||
#ifdef EIGERD
|
#ifdef EIGERD
|
||||||
if (sscanf(optarg, "%d", &topCommandLine) != 1) {
|
if (sscanf(optarg, "%d", &topCommandLine) != 1) {
|
||||||
LOG(logERROR, ("Cannot scan top argument\n%s", helpMessage));
|
LOG(logERROR, ("Cannot scan top argument\n%s", helpMessage));
|
||||||
exit(EXIT_FAILURE);
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
if (topCommandLine == 1) {
|
if (topCommandLine == 1) {
|
||||||
LOG(logINFO, ("Detector Top mode\n"));
|
LOG(logINFO, ("Detector Top mode\n"));
|
||||||
@ -226,16 +226,16 @@ int main(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
LOG(logERROR, ("No top implemented for this detector server\n"));
|
LOG(logERROR, ("No top implemented for this detector server\n"));
|
||||||
exit(EXIT_FAILURE);
|
return EXIT_FAILURE;
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'h':
|
case 'h':
|
||||||
printf("%s", helpMessage);
|
printf("%s", helpMessage);
|
||||||
exit(EXIT_SUCCESS);
|
return EXIT_SUCCESS;
|
||||||
default:
|
default:
|
||||||
printf("\n%s", helpMessage);
|
printf("\n%s", helpMessage);
|
||||||
exit(EXIT_FAILURE);
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -376,5 +376,5 @@ int main(int argc, char *argv[]) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
LOG(logINFO, ("Goodbye!\n"));
|
LOG(logINFO, ("Goodbye!\n"));
|
||||||
return 0;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -84,7 +84,7 @@ int main(int argc, char *argv[]) {
|
|||||||
c.call(parser.command(), parser.arguments(), parser.detector_id(),
|
c.call(parser.command(), parser.arguments(), parser.detector_id(),
|
||||||
action, std::cout, parser.receiver_id());
|
action, std::cout, parser.receiver_id());
|
||||||
} catch (sls::RuntimeError &e) {
|
} catch (sls::RuntimeError &e) {
|
||||||
exit(EXIT_FAILURE);
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
exit(EXIT_SUCCESS);
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
@ -13,6 +13,7 @@ set(SOURCES
|
|||||||
src/Arping.cpp
|
src/Arping.cpp
|
||||||
src/MasterAttributes.cpp
|
src/MasterAttributes.cpp
|
||||||
src/MasterFileUtility.cpp
|
src/MasterFileUtility.cpp
|
||||||
|
src/CommandLineOptions.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(PUBLICHEADERS
|
set(PUBLICHEADERS
|
||||||
@ -51,6 +52,10 @@ target_link_libraries(slsReceiverObject
|
|||||||
slsProjectWarnings #don't propagate warnigns
|
slsProjectWarnings #don't propagate warnigns
|
||||||
)
|
)
|
||||||
|
|
||||||
|
target_compile_definitions(slsReceiverObject
|
||||||
|
PRIVATE $<$<BOOL:${SLS_USE_TESTS}>:SLS_USE_TESTS>
|
||||||
|
)
|
||||||
|
|
||||||
# HDF5
|
# HDF5
|
||||||
if (SLS_USE_HDF5)
|
if (SLS_USE_HDF5)
|
||||||
if (HDF5_FOUND)
|
if (HDF5_FOUND)
|
||||||
@ -86,6 +91,7 @@ set_target_properties(slsReceiverStatic PROPERTIES
|
|||||||
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
|
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
|
||||||
PUBLIC_HEADER "${PUBLICHEADERS}"
|
PUBLIC_HEADER "${PUBLICHEADERS}"
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND RECEIVER_LIBRARY_TARGETS slsReceiverStatic)
|
list(APPEND RECEIVER_LIBRARY_TARGETS slsReceiverStatic)
|
||||||
|
|
||||||
|
|
||||||
|
@ -13,22 +13,12 @@ class Receiver : private virtual slsDetectorDefs {
|
|||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
* Starts up a Receiver server. Reads configuration file, options, and
|
* Starts up a Receiver server.
|
||||||
* assembles a Receiver using TCP and UDP detector interfaces
|
* Assembles a Receiver using TCP and UDP detector interfaces
|
||||||
* throws an exception in case of failure
|
* throws an exception in case of failure
|
||||||
* @param argc from command line
|
* @param port TCP/IP port number
|
||||||
* @param argv from command line
|
|
||||||
*/
|
*/
|
||||||
Receiver(int argc, char *argv[]);
|
Receiver(uint16_t port = 1954);
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
* Starts up a Receiver server. Reads configuration file, options, and
|
|
||||||
* assembles a Receiver using TCP and UDP detector interfaces
|
|
||||||
* throws an exception in case of failure
|
|
||||||
* @param tcpip_port_no TCP/IP port number
|
|
||||||
*/
|
|
||||||
Receiver(uint16_t tcpip_port_no = 1954);
|
|
||||||
|
|
||||||
~Receiver();
|
~Receiver();
|
||||||
|
|
||||||
|
347
slsReceiverSoftware/src/CommandLineOptions.cpp
Normal file
347
slsReceiverSoftware/src/CommandLineOptions.cpp
Normal file
@ -0,0 +1,347 @@
|
|||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-other
|
||||||
|
// Copyright (C) 2021 Contributors to the SLS Detector Package
|
||||||
|
|
||||||
|
#include "CommandLineOptions.h"
|
||||||
|
#include "sls/ToString.h"
|
||||||
|
#include "sls/logger.h"
|
||||||
|
#include "sls/sls_detector_defs.h"
|
||||||
|
#include "sls/versionAPI.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
CommandLineOptions::CommandLineOptions(AppType app)
|
||||||
|
: appType_(app), optString_(buildOptString()),
|
||||||
|
longOptions_(buildOptionList()) {}
|
||||||
|
|
||||||
|
/** for testing */
|
||||||
|
ParsedOptions CommandLineOptions::parse(const std::vector<std::string> &args) {
|
||||||
|
std::vector<char *> argv;
|
||||||
|
argv.reserve(args.size());
|
||||||
|
for (const auto &arg : args) {
|
||||||
|
argv.push_back(const_cast<char *>(arg.c_str()));
|
||||||
|
}
|
||||||
|
int argc = static_cast<int>(argv.size());
|
||||||
|
return parse(argc, argv.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
ParsedOptions CommandLineOptions::parse(int argc, char *argv[]) {
|
||||||
|
CommonOptions base;
|
||||||
|
MultiReceiverOptions multi;
|
||||||
|
FrameSyncOptions frame;
|
||||||
|
base.port = DEFAULT_TCP_RX_PORTNO;
|
||||||
|
|
||||||
|
optind = 0; // reset getopt
|
||||||
|
int opt, option_index = 0;
|
||||||
|
|
||||||
|
while ((opt = getopt_long(argc, argv, optString_.c_str(),
|
||||||
|
longOptions_.data(), &option_index)) != -1) {
|
||||||
|
switch (opt) {
|
||||||
|
case 'v':
|
||||||
|
case 'h':
|
||||||
|
handleCommonOption(opt, optarg, base);
|
||||||
|
return base; // exit after version/help
|
||||||
|
case 'p':
|
||||||
|
case 'u':
|
||||||
|
handleCommonOption(opt, optarg, base);
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
case 'n':
|
||||||
|
case 't':
|
||||||
|
handleAppSpecificOption(opt, optarg, base, multi, frame);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw sls::RuntimeError("Invalid arguments." + getHelpMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// remaining arguments
|
||||||
|
if (optind < argc) {
|
||||||
|
|
||||||
|
// deprecated and current options => invalid
|
||||||
|
if (base.port != DEFAULT_TCP_RX_PORTNO || multi.numReceivers != 1 ||
|
||||||
|
frame.numReceivers != 1 || multi.callbackEnabled != false ||
|
||||||
|
frame.printHeaders != false) {
|
||||||
|
LOG(sls::logWARNING) << "Cannot use both deprecated options and "
|
||||||
|
"the valid options simultaneously. Please "
|
||||||
|
"move away from the deprecated options.\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// unsupported deprecated arguments
|
||||||
|
if (appType_ == AppType::SingleReceiver) {
|
||||||
|
throw sls::RuntimeError("Invalid arguments." + getHelpMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse deprecated arguments
|
||||||
|
std::vector<std::string> args(argv, argv + argc);
|
||||||
|
auto [p, n, o] = ParseDeprecated(args);
|
||||||
|
// set options
|
||||||
|
base.port = p;
|
||||||
|
if (appType_ == AppType::MultiReceiver) {
|
||||||
|
multi.numReceivers = n;
|
||||||
|
multi.callbackEnabled = o;
|
||||||
|
} else if (appType_ == AppType::FrameSynchronizer) {
|
||||||
|
frame.numReceivers = n;
|
||||||
|
frame.printHeaders = o;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logging
|
||||||
|
LOG(sls::logINFO) << "TCP Port: " << base.port;
|
||||||
|
if (appType_ == AppType::MultiReceiver) {
|
||||||
|
LOG(sls::logINFO) << "Number of receivers: " << multi.numReceivers;
|
||||||
|
LOG(sls::logINFO) << "Callback enabled: " << multi.callbackEnabled;
|
||||||
|
} else if (appType_ == AppType::FrameSynchronizer) {
|
||||||
|
LOG(sls::logINFO) << "Number of receivers: " << frame.numReceivers;
|
||||||
|
LOG(sls::logINFO) << "Print headers: " << frame.printHeaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (appType_) {
|
||||||
|
case AppType::SingleReceiver:
|
||||||
|
return base;
|
||||||
|
case AppType::MultiReceiver:
|
||||||
|
static_cast<CommonOptions &>(multi) = base;
|
||||||
|
return multi;
|
||||||
|
case AppType::FrameSynchronizer:
|
||||||
|
static_cast<CommonOptions &>(frame) = base;
|
||||||
|
return frame;
|
||||||
|
default:
|
||||||
|
throw sls::RuntimeError("Unknown AppType in CommandLineOptions::parse");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<option> CommandLineOptions::buildOptionList() const {
|
||||||
|
std::vector<option> opts = {
|
||||||
|
{"version", no_argument, nullptr, 'v'},
|
||||||
|
{"help", no_argument, nullptr, 'h'},
|
||||||
|
{"port", required_argument, nullptr, 'p'},
|
||||||
|
{"uid", required_argument, nullptr, 'u'},
|
||||||
|
};
|
||||||
|
switch (appType_) {
|
||||||
|
case AppType::SingleReceiver:
|
||||||
|
opts.push_back({"rx_tcpport", required_argument, nullptr, 't'});
|
||||||
|
break;
|
||||||
|
case AppType::MultiReceiver:
|
||||||
|
opts.push_back({"num-receivers", required_argument, nullptr, 'n'});
|
||||||
|
opts.push_back({"callback", no_argument, nullptr, 'c'});
|
||||||
|
break;
|
||||||
|
case AppType::FrameSynchronizer:
|
||||||
|
opts.push_back({"num-receivers", required_argument, nullptr, 'n'});
|
||||||
|
opts.push_back({"print-headers", no_argument, nullptr, 'c'});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
opts.push_back({nullptr, 0, nullptr, 0}); // null-terminator for getopt
|
||||||
|
return opts;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CommandLineOptions::buildOptString() const {
|
||||||
|
std::string optstr = "vhp:u:";
|
||||||
|
if (appType_ == AppType::MultiReceiver ||
|
||||||
|
appType_ == AppType::FrameSynchronizer)
|
||||||
|
optstr += "cn:";
|
||||||
|
if (appType_ == AppType::SingleReceiver)
|
||||||
|
optstr += "t:";
|
||||||
|
return optstr;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t CommandLineOptions::parsePort(const char *optarg) {
|
||||||
|
uint16_t val = 0;
|
||||||
|
try {
|
||||||
|
val = sls::StringTo<uint16_t>(optarg);
|
||||||
|
} catch (...) {
|
||||||
|
throw sls::RuntimeError("Could not parse port number " +
|
||||||
|
std::string(optarg));
|
||||||
|
}
|
||||||
|
if (val < 1024) {
|
||||||
|
throw sls::RuntimeError(
|
||||||
|
"Invalid/ privileged port number parsed. Min: 1024.");
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t CommandLineOptions::parseNumReceivers(const char *optarg) {
|
||||||
|
uint16_t val = 0;
|
||||||
|
try {
|
||||||
|
val = sls::StringTo<uint16_t>(optarg);
|
||||||
|
} catch (...) {
|
||||||
|
throw sls::RuntimeError("Could not parse number of receivers " +
|
||||||
|
std::string(optarg));
|
||||||
|
}
|
||||||
|
if (val == 0 || val > MAX_RECEIVERS) {
|
||||||
|
throw sls::RuntimeError(
|
||||||
|
"Invalid number of receivers parsed. Options: 1 - " +
|
||||||
|
std::to_string(MAX_RECEIVERS));
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
uid_t CommandLineOptions::parseUID(const char *optarg) {
|
||||||
|
uid_t val = -1;
|
||||||
|
try {
|
||||||
|
val = sls::StringTo<uid_t>(optarg);
|
||||||
|
} catch (...) {
|
||||||
|
throw sls::RuntimeError("Could not parse UID " + std::string(optarg));
|
||||||
|
}
|
||||||
|
if (val == static_cast<uid_t>(-1)) {
|
||||||
|
throw sls::RuntimeError(
|
||||||
|
"Could not parse UID. Expected a valid user ID." +
|
||||||
|
std::string(optarg));
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandLineOptions::handleCommonOption(int opt, const char *optarg,
|
||||||
|
CommonOptions &base) {
|
||||||
|
switch (opt) {
|
||||||
|
case 'v':
|
||||||
|
base.versionRequested = true;
|
||||||
|
std::cout << getVersion() << std::endl;
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
base.helpRequested = true;
|
||||||
|
std::cout << getHelpMessage() << std::endl;
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
base.port = parsePort(optarg);
|
||||||
|
break;
|
||||||
|
case 'u':
|
||||||
|
base.userid = parseUID(optarg);
|
||||||
|
setEffectiveUID(base.userid);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandLineOptions::handleAppSpecificOption(int opt, const char *optarg,
|
||||||
|
CommonOptions &base,
|
||||||
|
MultiReceiverOptions &multi,
|
||||||
|
FrameSyncOptions &frame) {
|
||||||
|
switch (opt) {
|
||||||
|
|
||||||
|
case 'c':
|
||||||
|
if (appType_ == AppType::MultiReceiver)
|
||||||
|
multi.callbackEnabled = true;
|
||||||
|
else if (appType_ == AppType::FrameSynchronizer)
|
||||||
|
frame.printHeaders = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'n': {
|
||||||
|
auto val = parseNumReceivers(optarg);
|
||||||
|
if (appType_ == AppType::MultiReceiver)
|
||||||
|
multi.numReceivers = val;
|
||||||
|
else if (appType_ == AppType::FrameSynchronizer)
|
||||||
|
frame.numReceivers = val;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 't':
|
||||||
|
LOG(sls::logWARNING) << "Deprecated option '-t' and '--rx_tcport'. Use "
|
||||||
|
"'p' or '--port' instead.";
|
||||||
|
base.port = parsePort(optarg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* maintain backward compatibility of [start port] [num receivers] [optional
|
||||||
|
* arg] */
|
||||||
|
std::tuple<uint16_t, uint16_t, bool>
|
||||||
|
CommandLineOptions::ParseDeprecated(const std::vector<std::string> &args) {
|
||||||
|
|
||||||
|
size_t nargs = args.size();
|
||||||
|
if (nargs != 1 && nargs != 3 && nargs != 4) {
|
||||||
|
throw sls::RuntimeError("Invalid number of arguments.");
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG(sls::logWARNING)
|
||||||
|
<< "Deprecated options will be removed in future versions. "
|
||||||
|
"Please use the new options.\n";
|
||||||
|
|
||||||
|
// default deprecated values
|
||||||
|
if (nargs == 1) {
|
||||||
|
return std::make_tuple(DEFAULT_TCP_RX_PORTNO, 1, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse deprecated arguments
|
||||||
|
uint16_t p = parsePort(args[1].c_str());
|
||||||
|
uint16_t n = parseNumReceivers(args[2].c_str());
|
||||||
|
bool o = false;
|
||||||
|
if (nargs == 4) {
|
||||||
|
try {
|
||||||
|
o = sls::StringTo<bool>(args[3].c_str());
|
||||||
|
} catch (...) {
|
||||||
|
throw sls::RuntimeError("Invalid optional argument "
|
||||||
|
"parsed. Expected 1 (true) or "
|
||||||
|
"0 (false).");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::make_tuple(p, n, o);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CommandLineOptions::getTypeString() const {
|
||||||
|
switch (appType_) {
|
||||||
|
case AppType::SingleReceiver:
|
||||||
|
return "slsReceiver";
|
||||||
|
case AppType::MultiReceiver:
|
||||||
|
return "slsMultiReceiver";
|
||||||
|
case AppType::FrameSynchronizer:
|
||||||
|
return "slsFrameSynchronizer";
|
||||||
|
default:
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CommandLineOptions::getVersion() const {
|
||||||
|
return getTypeString() + " Version: " + APIRECEIVER;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CommandLineOptions::getHelpMessage() const {
|
||||||
|
switch (appType_) {
|
||||||
|
case AppType::SingleReceiver:
|
||||||
|
return std::string("\nUsage: ") + getTypeString() + " Options:\n" +
|
||||||
|
"\t-v, --version : Version.\n" +
|
||||||
|
"\t-p, --port : TCP port to communicate with client "
|
||||||
|
"for "
|
||||||
|
"configuration. Non-zero and 16 bit.\n" +
|
||||||
|
"\t-u, --uid : Set effective user id if receiver "
|
||||||
|
"started "
|
||||||
|
"with privileges. \n\n";
|
||||||
|
|
||||||
|
case AppType::MultiReceiver:
|
||||||
|
return std::string("\nUsage: " + getTypeString() + " Options:\n") +
|
||||||
|
"\t-v, --version : Version.\n" +
|
||||||
|
"\t-n, --num-receivers : Number of receivers.\n" +
|
||||||
|
"\t-p, --port : TCP port to communicate with client "
|
||||||
|
"for "
|
||||||
|
"configuration. Non-zero and 16 bit.\n" +
|
||||||
|
"\t-c, --callback : Enable dummy callbacks for debugging. "
|
||||||
|
"Disabled by default. \n" +
|
||||||
|
"\t-u, --uid : Set effective user id if receiver "
|
||||||
|
"started "
|
||||||
|
"with privileges. \n\n";
|
||||||
|
|
||||||
|
case AppType::FrameSynchronizer:
|
||||||
|
return std::string("\nUsage: " + getTypeString() + " Options:\n") +
|
||||||
|
"\t-v, --version : Version.\n" +
|
||||||
|
"\t-n, --num-receivers : Number of receivers.\n" +
|
||||||
|
"\t-p, --port : TCP port to communicate with client "
|
||||||
|
"for "
|
||||||
|
"configuration. Non-zero and 16 bit.\n" +
|
||||||
|
"\t-c, --print-headers : Print callback headers for debugging. "
|
||||||
|
"Disabled by default.\n" +
|
||||||
|
"\t-u, --uid : Set effective user id if receiver "
|
||||||
|
"started "
|
||||||
|
"with privileges. \n\n";
|
||||||
|
}
|
||||||
|
throw sls::RuntimeError("Unknown AppType for help message");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandLineOptions::setEffectiveUID(uid_t uid) {
|
||||||
|
if (geteuid() == uid) {
|
||||||
|
LOG(sls::logINFO) << "Process already has the same Effective UID "
|
||||||
|
<< uid;
|
||||||
|
} else {
|
||||||
|
if (seteuid(uid) != 0 || geteuid() != uid) {
|
||||||
|
throw sls::RuntimeError("Could not set Effective UID");
|
||||||
|
}
|
||||||
|
LOG(sls::logINFO) << "Process Effective UID changed to " << uid;
|
||||||
|
}
|
||||||
|
}
|
63
slsReceiverSoftware/src/CommandLineOptions.h
Normal file
63
slsReceiverSoftware/src/CommandLineOptions.h
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-other
|
||||||
|
// Copyright (C) 2021 Contributors to the SLS Detector Package
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <string>
|
||||||
|
#include <tuple>
|
||||||
|
#include <variant>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
enum class AppType { MultiReceiver, SingleReceiver, FrameSynchronizer };
|
||||||
|
|
||||||
|
struct CommonOptions {
|
||||||
|
uint16_t port = -1;
|
||||||
|
uid_t userid = -1;
|
||||||
|
bool versionRequested = false;
|
||||||
|
bool helpRequested = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MultiReceiverOptions : CommonOptions {
|
||||||
|
uint16_t numReceivers = 1;
|
||||||
|
bool callbackEnabled = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FrameSyncOptions : CommonOptions {
|
||||||
|
uint16_t numReceivers = 1;
|
||||||
|
bool printHeaders = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
using ParsedOptions =
|
||||||
|
std::variant<CommonOptions, MultiReceiverOptions, FrameSyncOptions>;
|
||||||
|
|
||||||
|
class CommandLineOptions {
|
||||||
|
public:
|
||||||
|
explicit CommandLineOptions(AppType app);
|
||||||
|
ParsedOptions parse(const std::vector<std::string> &args); // for testing
|
||||||
|
ParsedOptions parse(int argc, char *argv[]);
|
||||||
|
std::string getTypeString() const;
|
||||||
|
std::string getVersion() const;
|
||||||
|
std::string getHelpMessage() const;
|
||||||
|
static void setEffectiveUID(uid_t uid);
|
||||||
|
static std::tuple<uint16_t, uint16_t, bool>
|
||||||
|
ParseDeprecated(const std::vector<std::string> &args);
|
||||||
|
|
||||||
|
private:
|
||||||
|
AppType appType_;
|
||||||
|
std::string optString_;
|
||||||
|
std::vector<option> longOptions_;
|
||||||
|
std::vector<option> buildOptionList() const;
|
||||||
|
std::string buildOptString() const;
|
||||||
|
|
||||||
|
static uint16_t parsePort(const char *optarg);
|
||||||
|
static uint16_t parseNumReceivers(const char *optarg);
|
||||||
|
static uid_t parseUID(const char *optarg);
|
||||||
|
void handleCommonOption(int opt, const char *optarg, CommonOptions &base);
|
||||||
|
void handleAppSpecificOption(int opt, const char *optarg,
|
||||||
|
CommonOptions &base,
|
||||||
|
MultiReceiverOptions &multi,
|
||||||
|
FrameSyncOptions &frame);
|
||||||
|
|
||||||
|
static constexpr uint16_t MAX_RECEIVERS = 1000;
|
||||||
|
};
|
@ -5,16 +5,17 @@
|
|||||||
* reconstructing image. Sample python script for pull socket for this combiner
|
* reconstructing image. Sample python script for pull socket for this combiner
|
||||||
* in python/scripts folder. TODO: Not handling empty frames from one socket
|
* in python/scripts folder. TODO: Not handling empty frames from one socket
|
||||||
*/
|
*/
|
||||||
|
#include "CommandLineOptions.h"
|
||||||
#include "sls/Receiver.h"
|
#include "sls/Receiver.h"
|
||||||
#include "sls/ToString.h"
|
#include "sls/ToString.h"
|
||||||
#include "sls/container_utils.h"
|
#include "sls/container_utils.h"
|
||||||
#include "sls/logger.h"
|
#include "sls/logger.h"
|
||||||
|
#include "sls/network_utils.h"
|
||||||
#include "sls/sls_detector_defs.h"
|
#include "sls/sls_detector_defs.h"
|
||||||
|
|
||||||
#include <csignal> //SIGINT
|
#include <csignal> //SIGINT
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <iostream>
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <semaphore.h>
|
#include <semaphore.h>
|
||||||
@ -28,9 +29,14 @@
|
|||||||
#include <zmq.h>
|
#include <zmq.h>
|
||||||
|
|
||||||
std::vector<std::thread> threads;
|
std::vector<std::thread> threads;
|
||||||
std::vector<sem_t *> semaphores;
|
|
||||||
sls::TLogLevel printHeadersLevel = sls::logDEBUG;
|
sls::TLogLevel printHeadersLevel = sls::logDEBUG;
|
||||||
|
|
||||||
|
// gettid added in glibc 2.30
|
||||||
|
#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 30
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
#define gettid() syscall(SYS_gettid)
|
||||||
|
#endif
|
||||||
|
|
||||||
/** Define Colors to print data call back in different colors for different
|
/** Define Colors to print data call back in different colors for different
|
||||||
* recievers */
|
* recievers */
|
||||||
#define PRINT_IN_COLOR(c, f, ...) \
|
#define PRINT_IN_COLOR(c, f, ...) \
|
||||||
@ -57,16 +63,6 @@ struct FrameStatus {
|
|||||||
};
|
};
|
||||||
FrameStatus *global_frame_status = nullptr;
|
FrameStatus *global_frame_status = nullptr;
|
||||||
|
|
||||||
/**
|
|
||||||
* Control+C Interrupt Handler
|
|
||||||
* to let all the processes know to exit properly
|
|
||||||
*/
|
|
||||||
void sigInterruptHandler(int p) {
|
|
||||||
for (size_t i = 0; i != semaphores.size(); ++i) {
|
|
||||||
sem_post(semaphores[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void cleanup() {
|
void cleanup() {
|
||||||
if (global_frame_status) {
|
if (global_frame_status) {
|
||||||
std::lock_guard<std::mutex> lock(global_frame_status->mtx);
|
std::lock_guard<std::mutex> lock(global_frame_status->mtx);
|
||||||
@ -86,19 +82,6 @@ void cleanup() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* prints usage of this example program
|
|
||||||
*/
|
|
||||||
std::string getHelpMessage() {
|
|
||||||
std::ostringstream os;
|
|
||||||
os << "\nUsage:\n"
|
|
||||||
"./slsFrameSynchronizer [start tcp port] [num recevers] [print "
|
|
||||||
"callback headers (optional)]\n"
|
|
||||||
<< "\t - tcp port has to be non-zero and 16 bit\n"
|
|
||||||
<< "\t - print callback headers option is 0 (disabled) by default\n";
|
|
||||||
return os.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
void zmq_free(void *data, void *hint) { delete[] static_cast<char *>(data); }
|
void zmq_free(void *data, void *hint) { delete[] static_cast<char *>(data); }
|
||||||
|
|
||||||
void print_frames(const PortFrameMap &frame_port_map) {
|
void print_frames(const PortFrameMap &frame_port_map) {
|
||||||
@ -512,83 +495,46 @@ void GetDataCallback(slsDetectorDefs::sls_receiver_header &header,
|
|||||||
sem_post(&stat->available);
|
sem_post(&stat->available);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<sem_t> semaphores;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Example of main program using the Receiver class
|
* Control+C Interrupt Handler
|
||||||
*
|
* to let all the processes know to exit properly
|
||||||
* - Defines in file for:
|
* Only the main thread will call this handler
|
||||||
* - Default Number of receivers is 1
|
|
||||||
* - Default Start TCP port is 1954
|
|
||||||
*/
|
*/
|
||||||
|
void sigInterruptHandler(int p) {
|
||||||
|
(void)signal; // suppress unused warning if needed
|
||||||
|
for (auto &s : semaphores) {
|
||||||
|
sem_post(&s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
CommandLineOptions cli(AppType::FrameSynchronizer);
|
||||||
/** - set default values */
|
ParsedOptions opts;
|
||||||
int numReceivers = 1;
|
try {
|
||||||
uint16_t startTCPPort = DEFAULT_TCP_RX_PORTNO;
|
opts = cli.parse(argc, argv);
|
||||||
bool printHeaders = false;
|
} catch (sls::RuntimeError &e) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
/** - get number of receivers and start tcp port from command line
|
}
|
||||||
* arguments */
|
auto &f = std::get<FrameSyncOptions>(opts);
|
||||||
if (argc > 1) {
|
if (f.versionRequested || f.helpRequested) {
|
||||||
try {
|
return EXIT_SUCCESS;
|
||||||
if (argc == 3 || argc == 4) {
|
|
||||||
startTCPPort = sls::StringTo<uint16_t>(argv[1]);
|
|
||||||
if (startTCPPort == 0) {
|
|
||||||
throw std::runtime_error("Invalid start tcp port");
|
|
||||||
}
|
|
||||||
numReceivers = std::stoi(argv[2]);
|
|
||||||
if (numReceivers > 1024) {
|
|
||||||
cprintf(RED,
|
|
||||||
"Did you mix up the order of the arguments?\n%s\n",
|
|
||||||
getHelpMessage().c_str());
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
if (numReceivers == 0) {
|
|
||||||
cprintf(RED, "Invalid number of receivers.\n%s\n",
|
|
||||||
getHelpMessage().c_str());
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
if (argc == 4) {
|
|
||||||
printHeaders = sls::StringTo<bool>(argv[3]);
|
|
||||||
if (printHeaders) {
|
|
||||||
printHeadersLevel = sls::logINFOBLUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
throw std::runtime_error("Invalid number of arguments");
|
|
||||||
} catch (const std::exception &e) {
|
|
||||||
cprintf(RED, "Error: %s\n%s\n", e.what(), getHelpMessage().c_str());
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cprintf(RESET, "Number of Receivers: %d\n", numReceivers);
|
LOG(sls::logINFOBLUE) << "Current Process [ Tid: " << gettid() << ']';
|
||||||
cprintf(RESET, "Start TCP Port: %hu\n", startTCPPort);
|
|
||||||
cprintf(RESET, "Print Callback Headers: %s\n\n",
|
|
||||||
(printHeaders ? "Enabled" : "Disabled"));
|
|
||||||
|
|
||||||
/** - Catch signal SIGINT to close files and call destructors properly */
|
// close files on ctrl+c
|
||||||
struct sigaction sa;
|
sls::setupSignalHandler(SIGINT, sigInterruptHandler);
|
||||||
sa.sa_flags = 0; // no flags
|
// handle locally on socket crash
|
||||||
sa.sa_handler = sigInterruptHandler; // handler function
|
sls::setupSignalHandler(SIGPIPE, SIG_IGN);
|
||||||
sigemptyset(&sa.sa_mask); // dont block additional signals during invocation
|
|
||||||
// of handler
|
semaphores.resize(f.numReceivers);
|
||||||
if (sigaction(SIGINT, &sa, nullptr) == -1) {
|
for (auto &s : semaphores) {
|
||||||
cprintf(RED, "Could not set handler function for SIGINT\n");
|
sem_init(&s, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** - Ignore SIG_PIPE, prevents global signal handler, handle locally,
|
FrameStatus stat{true, false, f.numReceivers};
|
||||||
instead of a server crashing due to client crash when writing, it just
|
|
||||||
gives error */
|
|
||||||
struct sigaction asa;
|
|
||||||
asa.sa_flags = 0; // no flags
|
|
||||||
asa.sa_handler = SIG_IGN; // handler function
|
|
||||||
sigemptyset(&asa.sa_mask); // dont block additional signals during
|
|
||||||
// invocation of handler
|
|
||||||
if (sigaction(SIGPIPE, &asa, nullptr) == -1) {
|
|
||||||
cprintf(RED, "Could not set handler function for SIGPIPE\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
FrameStatus stat{true, false, numReceivers};
|
|
||||||
// store pointer for signal handler
|
// store pointer for signal handler
|
||||||
global_frame_status = &stat;
|
global_frame_status = &stat;
|
||||||
|
|
||||||
@ -596,34 +542,44 @@ int main(int argc, char *argv[]) {
|
|||||||
void *user_data = static_cast<void *>(&stat);
|
void *user_data = static_cast<void *>(&stat);
|
||||||
std::thread combinerThread(Correlate, &stat);
|
std::thread combinerThread(Correlate, &stat);
|
||||||
|
|
||||||
for (int i = 0; i != numReceivers; ++i) {
|
std::exception_ptr threadException = nullptr;
|
||||||
sem_t *semaphore = new sem_t;
|
for (int i = 0; i != f.numReceivers; ++i) {
|
||||||
sem_init(semaphore, 1, 0);
|
uint16_t port = f.port + i;
|
||||||
semaphores.push_back(semaphore);
|
sem_t *semaphore = &semaphores[i];
|
||||||
|
threads.emplace_back(
|
||||||
|
[i, semaphore, port, user_data, &threadException]() {
|
||||||
|
LOG(sls::logINFOBLUE)
|
||||||
|
<< "Thread " << i << " [ Tid: " << gettid() << ']';
|
||||||
|
try {
|
||||||
|
sls::Receiver receiver(port);
|
||||||
|
receiver.registerCallBackStartAcquisition(
|
||||||
|
StartAcquisitionCallback, user_data);
|
||||||
|
receiver.registerCallBackAcquisitionFinished(
|
||||||
|
AcquisitionFinishedCallback, user_data);
|
||||||
|
receiver.registerCallBackRawDataReady(GetDataCallback,
|
||||||
|
user_data);
|
||||||
|
|
||||||
uint16_t port = startTCPPort + i;
|
/** - as long as no Ctrl+C */
|
||||||
threads.emplace_back([i, semaphore, port, user_data]() {
|
// each child shares the common semaphore
|
||||||
sls::Receiver receiver(port);
|
sem_wait(semaphore);
|
||||||
receiver.registerCallBackStartAcquisition(StartAcquisitionCallback,
|
} catch (...) {
|
||||||
user_data);
|
// capture exception and raise SIGINT to exit gracefully
|
||||||
receiver.registerCallBackAcquisitionFinished(
|
threadException = std::current_exception();
|
||||||
AcquisitionFinishedCallback, user_data);
|
raise(SIGINT);
|
||||||
receiver.registerCallBackRawDataReady(GetDataCallback, user_data);
|
}
|
||||||
/** - as long as no Ctrl+C */
|
LOG(sls::logINFOBLUE)
|
||||||
sem_wait(semaphore);
|
<< "Exiting Thread " << i << " [ Tid: " << gettid() << " ]";
|
||||||
sem_destroy(semaphore);
|
});
|
||||||
delete semaphore;
|
|
||||||
|
|
||||||
// clean up frames
|
|
||||||
if (i == 0)
|
|
||||||
cleanup();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &thread : threads) {
|
for (auto &t : threads) {
|
||||||
thread.join();
|
t.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (auto &s : semaphores)
|
||||||
|
sem_destroy(&s);
|
||||||
|
cleanup();
|
||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(stat.mtx);
|
std::lock_guard<std::mutex> lock(stat.mtx);
|
||||||
stat.terminate = true;
|
stat.terminate = true;
|
||||||
@ -632,6 +588,19 @@ int main(int argc, char *argv[]) {
|
|||||||
combinerThread.join();
|
combinerThread.join();
|
||||||
sem_destroy(&stat.available);
|
sem_destroy(&stat.available);
|
||||||
|
|
||||||
|
if (threadException) {
|
||||||
|
try {
|
||||||
|
std::rethrow_exception(threadException);
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
LOG(sls::logERROR)
|
||||||
|
<< "Unhandled exception from thread: " << e.what();
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
} catch (...) {
|
||||||
|
LOG(sls::logERROR) << "Unknown exception occurred in thread";
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LOG(sls::logINFOBLUE) << "Goodbye!";
|
LOG(sls::logINFOBLUE) << "Goodbye!";
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -2,15 +2,16 @@
|
|||||||
// Copyright (C) 2021 Contributors to the SLS Detector Package
|
// Copyright (C) 2021 Contributors to the SLS Detector Package
|
||||||
/* Creates the slsMultiReceiver for running multiple receivers form a single
|
/* Creates the slsMultiReceiver for running multiple receivers form a single
|
||||||
* binary */
|
* binary */
|
||||||
|
#include "CommandLineOptions.h"
|
||||||
#include "sls/Receiver.h"
|
#include "sls/Receiver.h"
|
||||||
#include "sls/ToString.h"
|
#include "sls/ToString.h"
|
||||||
#include "sls/container_utils.h"
|
#include "sls/container_utils.h"
|
||||||
#include "sls/logger.h"
|
#include "sls/logger.h"
|
||||||
|
#include "sls/network_utils.h"
|
||||||
#include "sls/sls_detector_defs.h"
|
#include "sls/sls_detector_defs.h"
|
||||||
|
|
||||||
#include <csignal> //SIGINT
|
#include <csignal> //SIGINT
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <iostream>
|
|
||||||
#include <semaphore.h>
|
#include <semaphore.h>
|
||||||
#include <sys/wait.h> //wait
|
#include <sys/wait.h> //wait
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@ -26,28 +27,6 @@
|
|||||||
#define PRINT_IN_COLOR(c, f, ...) \
|
#define PRINT_IN_COLOR(c, f, ...) \
|
||||||
printf("\033[%dm" f RESET, 30 + c + 1, ##__VA_ARGS__)
|
printf("\033[%dm" f RESET, 30 + c + 1, ##__VA_ARGS__)
|
||||||
|
|
||||||
sem_t semaphore;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Control+C Interrupt Handler
|
|
||||||
* to let all the processes know to exit properly
|
|
||||||
*/
|
|
||||||
void sigInterruptHandler(int p) { sem_post(&semaphore); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* prints usage of this example program
|
|
||||||
*/
|
|
||||||
std::string getHelpMessage() {
|
|
||||||
std::ostringstream os;
|
|
||||||
os << "\nUsage:\n"
|
|
||||||
<< "./slsMultiReceiver [start tcp port] [num recevers] [call back "
|
|
||||||
"option (optional)]\n"
|
|
||||||
<< "\t - tcp port has to be non-zero and 16 bit\n"
|
|
||||||
<< "\t - call back option is 0 (disabled) by default, 1 prints frame "
|
|
||||||
"header for debugging\n";
|
|
||||||
return os.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start Acquisition Call back (slsMultiReceiver writes data if file write
|
* Start Acquisition Call back (slsMultiReceiver writes data if file write
|
||||||
* enabled) if registerCallBackRawDataReady or
|
* enabled) if registerCallBackRawDataReady or
|
||||||
@ -146,99 +125,48 @@ void GetData(slsDetectorDefs::sls_receiver_header &header,
|
|||||||
// header->packetsMask.to_string().c_str(),
|
// header->packetsMask.to_string().c_str(),
|
||||||
((uint8_t)(*((uint8_t *)(dataPointer)))), imageSize);
|
((uint8_t)(*((uint8_t *)(dataPointer)))), imageSize);
|
||||||
|
|
||||||
// // example of how to use roi or modify data that is later written to file
|
// if data is modified, can affect size
|
||||||
// slsDetectorDefs::ROI roi{0, 10, 0, 20};
|
// only reduction in size allowed, not increase
|
||||||
// int width = roi.xmax - roi.xmin;
|
// imageSize = 26000;
|
||||||
// int height = roi.ymax - roi.ymin;
|
|
||||||
// uint8_t *destPtr = (uint8_t *)dataPointer;
|
|
||||||
// for (int irow = roi.ymin; irow < roi.ymax; ++irow) {
|
|
||||||
// memcpy(destPtr,
|
|
||||||
// ((uint8_t *)(dataPointer + irow * callbackHeader.shape.x +
|
|
||||||
// roi.xmin)),
|
|
||||||
// width);
|
|
||||||
// destPtr += width;
|
|
||||||
// }
|
|
||||||
// memcpy((uint8_t*)dataPointer, (uint8_t*)dataPointer
|
|
||||||
// // setting roi for eg. changes size
|
|
||||||
// imageSize = width * height;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sem_t semaphore;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Example of main program using the Receiver class
|
* Control+C Interrupt Handler
|
||||||
*
|
* to let all the processes know to exit properly
|
||||||
* - Defines in file for:
|
* All child processes will call the handler (parent process set to ignore)
|
||||||
* - Default Number of receivers is 1
|
|
||||||
* - Default Start TCP port is 1954
|
|
||||||
*/
|
*/
|
||||||
|
void sigInterruptHandler(int signal) {
|
||||||
|
(void)signal; // suppress unused warning if needed
|
||||||
|
sem_post(&semaphore);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
/** - set default values */
|
CommandLineOptions cli(AppType::MultiReceiver);
|
||||||
int numReceivers = 1;
|
ParsedOptions opts;
|
||||||
uint16_t startTCPPort = DEFAULT_TCP_RX_PORTNO;
|
try {
|
||||||
int withCallback = 0;
|
opts = cli.parse(argc, argv);
|
||||||
|
} catch (sls::RuntimeError &e) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
auto &m = std::get<MultiReceiverOptions>(opts);
|
||||||
|
if (m.versionRequested || m.helpRequested) {
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG(sls::logINFOBLUE) << "Current Process [ Tid: " << gettid() << ']';
|
||||||
|
|
||||||
|
// close files on ctrl+c
|
||||||
|
sls::setupSignalHandler(SIGINT, sigInterruptHandler);
|
||||||
|
// handle locally on socket crash
|
||||||
|
sls::setupSignalHandler(SIGPIPE, SIG_IGN);
|
||||||
|
|
||||||
sem_init(&semaphore, 1, 0);
|
sem_init(&semaphore, 1, 0);
|
||||||
|
|
||||||
/** - get number of receivers and start tcp port from command line
|
/** - loop over receivers */
|
||||||
* arguments */
|
for (int i = 0; i < m.numReceivers; ++i) {
|
||||||
if (argc > 1) {
|
|
||||||
try {
|
|
||||||
if (argc == 3 || argc == 4) {
|
|
||||||
startTCPPort = sls::StringTo<uint16_t>(argv[1]);
|
|
||||||
if (startTCPPort == 0) {
|
|
||||||
throw std::runtime_error("Invalid start tcp port");
|
|
||||||
}
|
|
||||||
numReceivers = std::stoi(argv[2]);
|
|
||||||
if (numReceivers > 1024) {
|
|
||||||
cprintf(RED,
|
|
||||||
"Did you mix up the order of the arguments?\n%s\n",
|
|
||||||
getHelpMessage().c_str());
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
if (numReceivers == 0) {
|
|
||||||
cprintf(RED, "Invalid number of receivers.\n%s\n",
|
|
||||||
getHelpMessage().c_str());
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
if (argc == 4) {
|
|
||||||
withCallback = std::stoi(argv[3]);
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
throw std::runtime_error("Invalid number of arguments");
|
|
||||||
} catch (const std::exception &e) {
|
|
||||||
cprintf(RED, "Error: %s\n%s\n", e.what(), getHelpMessage().c_str());
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cprintf(BLUE, "Parent Process Created [ Tid: %ld ]\n", (long)gettid());
|
|
||||||
cprintf(RESET, "Number of Receivers: %d\n", numReceivers);
|
|
||||||
cprintf(RESET, "Start TCP Port: %hu\n", startTCPPort);
|
|
||||||
cprintf(RESET, "Callback Enable: %d\n", withCallback);
|
|
||||||
|
|
||||||
/** - Catch signal SIGINT to close files and call destructors properly */
|
|
||||||
struct sigaction sa;
|
|
||||||
sa.sa_flags = 0; // no flags
|
|
||||||
sa.sa_handler = sigInterruptHandler; // handler function
|
|
||||||
sigemptyset(&sa.sa_mask); // dont block additional signals during invocation
|
|
||||||
// of handler
|
|
||||||
if (sigaction(SIGINT, &sa, nullptr) == -1) {
|
|
||||||
cprintf(RED, "Could not set handler function for SIGINT\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/** - Ignore SIG_PIPE, prevents global signal handler, handle locally,
|
|
||||||
instead of a server crashing due to client crash when writing, it just
|
|
||||||
gives error */
|
|
||||||
struct sigaction asa;
|
|
||||||
asa.sa_flags = 0; // no flags
|
|
||||||
asa.sa_handler = SIG_IGN; // handler function
|
|
||||||
sigemptyset(&asa.sa_mask); // dont block additional signals during
|
|
||||||
// invocation of handler
|
|
||||||
if (sigaction(SIGPIPE, &asa, nullptr) == -1) {
|
|
||||||
cprintf(RED, "Could not set handler function for SIGPIPE\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/** - loop over number of receivers */
|
|
||||||
for (int i = 0; i < numReceivers; ++i) {
|
|
||||||
|
|
||||||
/** - fork process to create child process */
|
/** - fork process to create child process */
|
||||||
pid_t pid = fork();
|
pid_t pid = fork();
|
||||||
@ -246,86 +174,89 @@ int main(int argc, char *argv[]) {
|
|||||||
/** - if fork failed, raise SIGINT and properly destroy all child
|
/** - if fork failed, raise SIGINT and properly destroy all child
|
||||||
* processes */
|
* processes */
|
||||||
if (pid < 0) {
|
if (pid < 0) {
|
||||||
cprintf(RED, "fork() failed. Killing all the receiver objects\n");
|
LOG(sls::logERROR)
|
||||||
|
<< "fork() failed. Killing all the receiver objects";
|
||||||
raise(SIGINT);
|
raise(SIGINT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** - if child process */
|
/** - if child process */
|
||||||
else if (pid == 0) {
|
else if (pid == 0) {
|
||||||
cprintf(BLUE, "Child process %d [ Tid: %ld ]\n", i, (long)gettid());
|
LOG(sls::logINFOBLUE)
|
||||||
|
<< "Child process " << i << " [ Tid: " << gettid() << ']';
|
||||||
|
|
||||||
std::unique_ptr<sls::Receiver> receiver = nullptr;
|
|
||||||
try {
|
try {
|
||||||
receiver = sls::make_unique<sls::Receiver>(startTCPPort + i);
|
uint16_t port = m.port + i;
|
||||||
|
sls::Receiver receiver(port);
|
||||||
|
|
||||||
|
/** - register callbacks. remember to set file write enable
|
||||||
|
* to 0 (using the client) if we should not write files and you
|
||||||
|
* will write data using the callbacks */
|
||||||
|
if (m.callbackEnabled) {
|
||||||
|
|
||||||
|
/** - Call back for start acquisition */
|
||||||
|
LOG(sls::logINFOBLUE) << "Registering StartAcq()";
|
||||||
|
receiver.registerCallBackStartAcquisition(StartAcq,
|
||||||
|
nullptr);
|
||||||
|
|
||||||
|
/** - Call back for acquisition finished */
|
||||||
|
LOG(sls::logINFOBLUE)
|
||||||
|
<< "Registering AcquisitionFinished()";
|
||||||
|
receiver.registerCallBackAcquisitionFinished(
|
||||||
|
AcquisitionFinished, nullptr);
|
||||||
|
|
||||||
|
/* - Call back for raw data */
|
||||||
|
LOG(sls::logINFOBLUE) << "Registering GetData()";
|
||||||
|
receiver.registerCallBackRawDataReady(GetData, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** - as long as no Ctrl+C */
|
||||||
|
// each child process gets a copy of the semaphore
|
||||||
|
sem_wait(&semaphore);
|
||||||
|
sem_destroy(&semaphore);
|
||||||
|
LOG(sls::logINFOBLUE)
|
||||||
|
<< "Exiting Child Process [ Tid: " << gettid() << ']';
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
|
sem_destroy(&semaphore);
|
||||||
LOG(sls::logINFOBLUE)
|
LOG(sls::logINFOBLUE)
|
||||||
<< "Exiting Child Process [ Tid: " << gettid() << " ]";
|
<< "Exiting Child Process [ Tid: " << gettid() << " ]";
|
||||||
throw;
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
/** - register callbacks. remember to set file write enable to 0
|
|
||||||
* (using the client) if we should not write files and you will
|
|
||||||
* write data using the callbacks */
|
|
||||||
if (withCallback) {
|
|
||||||
|
|
||||||
/** - Call back for start acquisition */
|
|
||||||
cprintf(BLUE, "Registering StartAcq()\n");
|
|
||||||
receiver->registerCallBackStartAcquisition(StartAcq, nullptr);
|
|
||||||
|
|
||||||
/** - Call back for acquisition finished */
|
|
||||||
cprintf(BLUE, "Registering AcquisitionFinished()\n");
|
|
||||||
receiver->registerCallBackAcquisitionFinished(
|
|
||||||
AcquisitionFinished, nullptr);
|
|
||||||
|
|
||||||
/* - Call back for raw data */
|
|
||||||
cprintf(BLUE, "Registering GetData() \n");
|
|
||||||
receiver->registerCallBackRawDataReady(GetData, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** - as long as no Ctrl+C */
|
|
||||||
sem_wait(&semaphore);
|
|
||||||
sem_destroy(&semaphore);
|
|
||||||
cprintf(BLUE, "Exiting Child Process [ Tid: %ld ]\n",
|
|
||||||
(long)gettid());
|
|
||||||
exit(EXIT_SUCCESS);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** - Parent process ignores SIGINT (exits only when all child process
|
/** - Parent process ignores SIGINT and waits for all the child processes to
|
||||||
* exits) */
|
* handle the signal */
|
||||||
sa.sa_flags = 0; // no flags
|
sls::setupSignalHandler(SIGINT, SIG_IGN);
|
||||||
sa.sa_handler = SIG_IGN; // handler function
|
|
||||||
sigemptyset(&sa.sa_mask); // dont block additional signals during invocation
|
|
||||||
// of handler
|
|
||||||
if (sigaction(SIGINT, &sa, nullptr) == -1) {
|
|
||||||
cprintf(RED, "Could not set handler function for SIGINT\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/** - Print Ready and Instructions how to exit */
|
/** - Print Ready and Instructions how to exit */
|
||||||
std::cout << "Ready ... \n";
|
std::cout << "Ready ... \n";
|
||||||
cprintf(RESET, "\n[ Press \'Ctrl+c\' to exit ]\n");
|
LOG(sls::logINFO) << "\n[ Press \'Ctrl+c\' to exit ]";
|
||||||
|
|
||||||
/** - Parent process waits for all child processes to exit */
|
/** - Parent process waits for all child processes to exit */
|
||||||
for (;;) {
|
for (;;) {
|
||||||
pid_t childPid = waitpid(-1, nullptr, 0);
|
int status;
|
||||||
|
pid_t childPid = waitpid(-1, &status, 0);
|
||||||
|
|
||||||
// no child closed
|
// no child closed
|
||||||
if (childPid == -1) {
|
if (childPid == -1) {
|
||||||
if (errno == ECHILD) {
|
if (errno == ECHILD) {
|
||||||
cprintf(GREEN, "All Child Processes have been closed\n");
|
LOG(sls::logINFOGREEN)
|
||||||
|
<< "All Child Processes have been closed";
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
cprintf(RED, "Unexpected error from waitpid(): (%s)\n",
|
LOG(sls::logERROR)
|
||||||
strerror(errno));
|
<< "Unexpected error from waitpid(): " << strerror(errno);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// child closed
|
if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
|
||||||
cprintf(BLUE, "Exiting Child Process [ Tid: %ld ]\n",
|
std::cerr << "Child " << childPid << " failed\n";
|
||||||
(long int)childPid);
|
kill(0, SIGINT); // signal other children to exit
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "Goodbye!\n";
|
std::cout << "Goodbye!\n";
|
||||||
return 0;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -27,106 +27,14 @@ namespace sls {
|
|||||||
|
|
||||||
Receiver::~Receiver() = default;
|
Receiver::~Receiver() = default;
|
||||||
|
|
||||||
Receiver::Receiver(int argc, char *argv[]) : tcpipInterface(nullptr) {
|
Receiver::Receiver(uint16_t port) {
|
||||||
|
validatePortNumber(port);
|
||||||
// options
|
#ifdef SLS_USE_TESTS
|
||||||
uint16_t tcpip_port_no = 1954;
|
if (port == 65535) {
|
||||||
uid_t userid = -1;
|
throw sls::RuntimeError("Throwing for testing purposes. ");
|
||||||
|
|
||||||
// parse command line for config
|
|
||||||
static struct option long_options[] = {
|
|
||||||
// These options set a flag.
|
|
||||||
//{"verbose", no_argument, &verbose_flag, 1},
|
|
||||||
// These options don’t set a flag. We distinguish them by their indices.
|
|
||||||
{"rx_tcpport", required_argument, nullptr,
|
|
||||||
't'}, // TODO change or backward compatible to "port, p"?
|
|
||||||
{"uid", required_argument, nullptr, 'u'},
|
|
||||||
{"version", no_argument, nullptr, 'v'},
|
|
||||||
{"help", no_argument, nullptr, 'h'},
|
|
||||||
{nullptr, 0, nullptr, 0}};
|
|
||||||
|
|
||||||
// initialize global optind variable (required when instantiating multiple
|
|
||||||
// receivers in the same process)
|
|
||||||
optind = 1;
|
|
||||||
// getopt_long stores the option index here.
|
|
||||||
int option_index = 0;
|
|
||||||
int c = 0;
|
|
||||||
|
|
||||||
std::string help_message =
|
|
||||||
"\nUsage: " + std::string(argv[0]) + " [arguments]\n" +
|
|
||||||
"Possible arguments are:\n" +
|
|
||||||
"\t-t, --rx_tcpport <port> : TCP Communication Port with "
|
|
||||||
"client. Non-zero and 16 bit.\n" +
|
|
||||||
"\t-u, --uid <user id> : Set effective user id if receiver "
|
|
||||||
"\n" +
|
|
||||||
"\t started with privileges. \n\n";
|
|
||||||
|
|
||||||
while (c != -1) {
|
|
||||||
c = getopt_long(argc, argv, "hvf:t:u:", long_options, &option_index);
|
|
||||||
|
|
||||||
// Detect the end of the options.
|
|
||||||
if (c == -1)
|
|
||||||
break;
|
|
||||||
|
|
||||||
switch (c) {
|
|
||||||
|
|
||||||
case 't':
|
|
||||||
try {
|
|
||||||
tcpip_port_no = sls::StringTo<uint16_t>(optarg);
|
|
||||||
validatePortNumber(tcpip_port_no);
|
|
||||||
} catch (...) {
|
|
||||||
throw RuntimeError("Could not scan TCP port number." +
|
|
||||||
help_message);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'u':
|
|
||||||
if (sscanf(optarg, "%u", &userid) != 1) {
|
|
||||||
throw RuntimeError("Could not scan uid" + help_message);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'v':
|
|
||||||
std::cout << "SLS Receiver Version: " << APIRECEIVER << std::endl;
|
|
||||||
LOG(logINFOBLUE) << "Exiting [ Tid: " << gettid() << " ]";
|
|
||||||
exit(EXIT_SUCCESS);
|
|
||||||
|
|
||||||
case 'h':
|
|
||||||
std::cout << help_message << std::endl;
|
|
||||||
exit(EXIT_SUCCESS);
|
|
||||||
default:
|
|
||||||
throw RuntimeError(help_message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
// set effective id if provided
|
tcpipInterface = make_unique<ClientInterface>(port);
|
||||||
if (userid != static_cast<uid_t>(-1)) {
|
|
||||||
if (geteuid() == userid) {
|
|
||||||
LOG(logINFO) << "Process already has the same Effective UID "
|
|
||||||
<< userid;
|
|
||||||
} else {
|
|
||||||
if (seteuid(userid) != 0) {
|
|
||||||
std::ostringstream oss;
|
|
||||||
oss << "Could not set Effective UID to " << userid;
|
|
||||||
throw RuntimeError(oss.str());
|
|
||||||
}
|
|
||||||
if (geteuid() != userid) {
|
|
||||||
std::ostringstream oss;
|
|
||||||
oss << "Could not set Effective UID to " << userid << ". Got "
|
|
||||||
<< geteuid();
|
|
||||||
throw RuntimeError(oss.str());
|
|
||||||
}
|
|
||||||
LOG(logINFO) << "Process Effective UID changed to " << userid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// might throw an exception
|
|
||||||
tcpipInterface = make_unique<ClientInterface>(tcpip_port_no);
|
|
||||||
}
|
|
||||||
|
|
||||||
Receiver::Receiver(uint16_t tcpip_port_no) {
|
|
||||||
// might throw an exception
|
|
||||||
tcpipInterface = make_unique<ClientInterface>(tcpip_port_no);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Receiver::getReceiverVersion() {
|
std::string Receiver::getReceiverVersion() {
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
// SPDX-License-Identifier: LGPL-3.0-or-other
|
// SPDX-License-Identifier: LGPL-3.0-or-other
|
||||||
// Copyright (C) 2021 Contributors to the SLS Detector Package
|
// Copyright (C) 2021 Contributors to the SLS Detector Package
|
||||||
/* slsReceiver */
|
/* slsReceiver */
|
||||||
|
#include "CommandLineOptions.h"
|
||||||
#include "sls/Receiver.h"
|
#include "sls/Receiver.h"
|
||||||
|
#include "sls/ToString.h"
|
||||||
#include "sls/container_utils.h"
|
#include "sls/container_utils.h"
|
||||||
#include "sls/logger.h"
|
#include "sls/logger.h"
|
||||||
|
#include "sls/network_utils.h"
|
||||||
#include "sls/sls_detector_defs.h"
|
#include "sls/sls_detector_defs.h"
|
||||||
|
|
||||||
#include <csignal> //SIGINT
|
#include <csignal> //SIGINT
|
||||||
@ -18,44 +21,49 @@
|
|||||||
|
|
||||||
sem_t semaphore;
|
sem_t semaphore;
|
||||||
|
|
||||||
void sigInterruptHandler(int p) { sem_post(&semaphore); }
|
/**
|
||||||
|
* Control+C Interrupt Handler
|
||||||
|
* to let all the other process know to exit properly
|
||||||
|
*/
|
||||||
|
void sigInterruptHandler(int signal) {
|
||||||
|
(void)signal; // suppress unused warning if needed
|
||||||
|
sem_post(&semaphore);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
|
CommandLineOptions cli(AppType::SingleReceiver);
|
||||||
|
ParsedOptions opts;
|
||||||
|
try {
|
||||||
|
opts = cli.parse(argc, argv);
|
||||||
|
} catch (sls::RuntimeError &e) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
auto &o = std::get<CommonOptions>(opts);
|
||||||
|
if (o.versionRequested || o.helpRequested) {
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG(sls::logINFOBLUE) << "Current Process [ Tid: " << gettid() << " ]";
|
||||||
|
|
||||||
|
// close files on ctrl+c
|
||||||
|
sls::setupSignalHandler(SIGINT, sigInterruptHandler);
|
||||||
|
// handle locally on socket crash
|
||||||
|
sls::setupSignalHandler(SIGPIPE, SIG_IGN);
|
||||||
|
|
||||||
sem_init(&semaphore, 1, 0);
|
sem_init(&semaphore, 1, 0);
|
||||||
|
|
||||||
LOG(sls::logINFOBLUE) << "Created [ Tid: " << gettid() << " ]";
|
|
||||||
|
|
||||||
// Catch signal SIGINT to close files and call destructors properly
|
|
||||||
struct sigaction sa;
|
|
||||||
sa.sa_flags = 0; // no flags
|
|
||||||
sa.sa_handler = sigInterruptHandler; // handler function
|
|
||||||
sigemptyset(&sa.sa_mask); // dont block additional signals during invocation
|
|
||||||
// of handler
|
|
||||||
if (sigaction(SIGINT, &sa, nullptr) == -1) {
|
|
||||||
LOG(sls::logERROR) << "Could not set handler function for SIGINT";
|
|
||||||
}
|
|
||||||
|
|
||||||
// if socket crash, ignores SISPIPE, prevents global signal handler
|
|
||||||
// subsequent read/write to socket gives error - must handle locally
|
|
||||||
struct sigaction asa;
|
|
||||||
asa.sa_flags = 0; // no flags
|
|
||||||
asa.sa_handler = SIG_IGN; // handler function
|
|
||||||
sigemptyset(&asa.sa_mask); // dont block additional signals during
|
|
||||||
// invocation of handler
|
|
||||||
if (sigaction(SIGPIPE, &asa, nullptr) == -1) {
|
|
||||||
LOG(sls::logERROR) << "Could not set handler function for SIGPIPE";
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
sls::Receiver r(argc, argv);
|
sls::Receiver r(o.port);
|
||||||
LOG(sls::logINFO) << "[ Press \'Ctrl+c\' to exit ]";
|
LOG(sls::logINFO) << "[ Press \'Ctrl+c\' to exit ]";
|
||||||
sem_wait(&semaphore);
|
sem_wait(&semaphore);
|
||||||
sem_destroy(&semaphore);
|
sem_destroy(&semaphore);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
// pass
|
sem_destroy(&semaphore);
|
||||||
|
LOG(sls::logINFOBLUE) << "Exiting [ Tid: " << gettid() << " ]";
|
||||||
|
throw;
|
||||||
}
|
}
|
||||||
LOG(sls::logINFOBLUE) << "Exiting [ Tid: " << gettid() << " ]";
|
LOG(sls::logINFOBLUE) << "Exiting [ Tid: " << gettid() << " ]";
|
||||||
LOG(sls::logINFO) << "Exiting Receiver";
|
LOG(sls::logINFO) << "Exiting Receiver";
|
||||||
return 0;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ target_sources(tests PRIVATE
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/test-GeneralData.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/test-GeneralData.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/test-CircularFifo.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/test-CircularFifo.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/test-ArrangeDataBasedOnBitList.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/test-ArrangeDataBasedOnBitList.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/test-Apps.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(tests PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../src>")
|
target_include_directories(tests PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../src>")
|
||||||
|
328
slsReceiverSoftware/tests/test-Apps.cpp
Normal file
328
slsReceiverSoftware/tests/test-Apps.cpp
Normal file
@ -0,0 +1,328 @@
|
|||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-other
|
||||||
|
// Copyright (C) 2021 Contributors to the SLS Detector Package
|
||||||
|
#include "CommandLineOptions.h"
|
||||||
|
#include "catch.hpp"
|
||||||
|
#include "sls/versionAPI.h"
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
namespace sls {
|
||||||
|
|
||||||
|
template <typename T, typename U> constexpr bool is_type() {
|
||||||
|
return std::is_same_v<std::decay_t<U>, T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("CommandLineOption construction", "[.rxcmdcall]") {
|
||||||
|
CommandLineOptions s(AppType::SingleReceiver);
|
||||||
|
REQUIRE(s.getTypeString() == "slsReceiver");
|
||||||
|
REQUIRE(s.getVersion() ==
|
||||||
|
std::string("slsReceiver Version: ") + APIRECEIVER);
|
||||||
|
REQUIRE_NOTHROW(s.getHelpMessage());
|
||||||
|
|
||||||
|
CommandLineOptions m(AppType::MultiReceiver);
|
||||||
|
REQUIRE(m.getTypeString() == "slsMultiReceiver");
|
||||||
|
REQUIRE(m.getVersion() ==
|
||||||
|
std::string("slsMultiReceiver Version: ") + APIRECEIVER);
|
||||||
|
REQUIRE_NOTHROW(m.getHelpMessage());
|
||||||
|
|
||||||
|
CommandLineOptions f(AppType::FrameSynchronizer);
|
||||||
|
REQUIRE(f.getTypeString() == "slsFrameSynchronizer");
|
||||||
|
REQUIRE(f.getVersion() ==
|
||||||
|
std::string("slsFrameSynchronizer Version: ") + APIRECEIVER);
|
||||||
|
REQUIRE_NOTHROW(f.getHelpMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Validate common options", "[.rxcmdcall]") {
|
||||||
|
std::string uidStr = std::to_string(getuid());
|
||||||
|
|
||||||
|
for (auto app : {AppType::SingleReceiver, AppType::MultiReceiver,
|
||||||
|
AppType::FrameSynchronizer}) {
|
||||||
|
CommandLineOptions s(app);
|
||||||
|
REQUIRE_NOTHROW(s.parse({}));
|
||||||
|
REQUIRE_NOTHROW(s.parse({"", "-v"}));
|
||||||
|
REQUIRE_NOTHROW(s.parse({"", "-h"}));
|
||||||
|
REQUIRE_NOTHROW(s.parse({"", "-h", "gdfg"})); // ignored extra args
|
||||||
|
REQUIRE_NOTHROW(s.parse({"", "-p", "1955"}));
|
||||||
|
REQUIRE_NOTHROW(s.parse({"", "-u", uidStr}));
|
||||||
|
REQUIRE_NOTHROW(s.parse({"", "-p", "1234", "-u", uidStr}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Validate specific options", "[.rxcmdcall]") {
|
||||||
|
std::string uidStr = std::to_string(getuid());
|
||||||
|
|
||||||
|
CommandLineOptions s(AppType::SingleReceiver);
|
||||||
|
REQUIRE_NOTHROW(s.parse({"", "-t", "1955"}));
|
||||||
|
REQUIRE_THROWS(s.parse({"", "-c"}));
|
||||||
|
REQUIRE_THROWS(s.parse({"", "-n", "2"}));
|
||||||
|
REQUIRE_THROWS(s.parse({"", "-m", "2"}));
|
||||||
|
|
||||||
|
for (auto app : {AppType::MultiReceiver, AppType::FrameSynchronizer}) {
|
||||||
|
CommandLineOptions m(app);
|
||||||
|
REQUIRE_NOTHROW(m.parse({"", "-c"}));
|
||||||
|
REQUIRE_NOTHROW(m.parse({"", "-n", "2"}));
|
||||||
|
REQUIRE_NOTHROW(
|
||||||
|
m.parse({"", "-p", "1234", "-u", uidStr, "-c", "-n", "2"}));
|
||||||
|
REQUIRE_THROWS(m.parse({"", "-t", "1955"}));
|
||||||
|
REQUIRE_THROWS(m.parse({"", "-m", "2"}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Parse version and help", "[.rxcmdcall]") {
|
||||||
|
for (auto app : {AppType::SingleReceiver, AppType::MultiReceiver,
|
||||||
|
AppType::FrameSynchronizer}) {
|
||||||
|
CommandLineOptions s(app);
|
||||||
|
auto opts = s.parse({});
|
||||||
|
std::visit(
|
||||||
|
[](const auto &o) {
|
||||||
|
REQUIRE(o.versionRequested == false); // default
|
||||||
|
REQUIRE(o.helpRequested == false); // default
|
||||||
|
},
|
||||||
|
opts);
|
||||||
|
|
||||||
|
opts = s.parse({"", "-v"});
|
||||||
|
std::visit(
|
||||||
|
[](const auto &o) {
|
||||||
|
REQUIRE(o.versionRequested == true);
|
||||||
|
REQUIRE(o.helpRequested == false);
|
||||||
|
},
|
||||||
|
opts);
|
||||||
|
|
||||||
|
opts = s.parse({"", "-h"});
|
||||||
|
std::visit(
|
||||||
|
[](const auto &o) {
|
||||||
|
REQUIRE(o.versionRequested == false);
|
||||||
|
REQUIRE(o.helpRequested == true);
|
||||||
|
},
|
||||||
|
opts);
|
||||||
|
|
||||||
|
opts = s.parse({"", "-h", "-v"});
|
||||||
|
std::visit(
|
||||||
|
[](const auto &o) {
|
||||||
|
REQUIRE(o.versionRequested == false); // exits after help
|
||||||
|
REQUIRE(o.helpRequested == true);
|
||||||
|
},
|
||||||
|
opts);
|
||||||
|
|
||||||
|
opts = s.parse({"", "-v", "-h"});
|
||||||
|
std::visit(
|
||||||
|
[](const auto &o) {
|
||||||
|
REQUIRE(o.helpRequested == false); // exits after version
|
||||||
|
REQUIRE(o.versionRequested == true);
|
||||||
|
},
|
||||||
|
opts);
|
||||||
|
|
||||||
|
opts = s.parse({"", "-v", "-h", "sdfsf"}); // ignores extra args
|
||||||
|
std::visit(
|
||||||
|
[](const auto &o) {
|
||||||
|
REQUIRE(o.helpRequested == false); // exits after version
|
||||||
|
REQUIRE(o.versionRequested == true);
|
||||||
|
},
|
||||||
|
opts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Parse port and uid", "[.rxcmdcall]") {
|
||||||
|
uid_t uid = getuid();
|
||||||
|
std::string uidStr = std::to_string(uid);
|
||||||
|
uid_t invalidUid = uid + 1000;
|
||||||
|
std::string invalidUidStr = std::to_string(invalidUid);
|
||||||
|
|
||||||
|
for (auto app : {AppType::SingleReceiver, AppType::MultiReceiver,
|
||||||
|
AppType::FrameSynchronizer}) {
|
||||||
|
CommandLineOptions s(app);
|
||||||
|
REQUIRE_THROWS(
|
||||||
|
s.parse({"", "-p", "1234", "-u", invalidUidStr})); // invalid uid
|
||||||
|
REQUIRE_THROWS(s.parse({"", "-p", "500"})); // invalid port
|
||||||
|
|
||||||
|
auto opts = s.parse({"", "-p", "1234", "-u", uidStr});
|
||||||
|
std::visit(
|
||||||
|
[&](const auto &o) {
|
||||||
|
REQUIRE(o.port == 1234);
|
||||||
|
REQUIRE(o.userid == uid);
|
||||||
|
},
|
||||||
|
opts);
|
||||||
|
|
||||||
|
opts = s.parse({"", "-p", "5678"});
|
||||||
|
std::visit(
|
||||||
|
[](const auto &o) {
|
||||||
|
REQUIRE(o.port == 5678);
|
||||||
|
REQUIRE(o.userid == static_cast<uid_t>(-1)); // default
|
||||||
|
},
|
||||||
|
opts);
|
||||||
|
|
||||||
|
opts = s.parse({});
|
||||||
|
std::visit(
|
||||||
|
[](const auto &o) {
|
||||||
|
REQUIRE(o.port == 1954); // default
|
||||||
|
REQUIRE(o.userid == static_cast<uid_t>(-1)); // default
|
||||||
|
},
|
||||||
|
opts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Parse num receivers and opt arg (Specific opt)", "[.rxcmdcall]") {
|
||||||
|
for (auto app : {AppType::MultiReceiver, AppType::FrameSynchronizer}) {
|
||||||
|
CommandLineOptions s(app);
|
||||||
|
|
||||||
|
REQUIRE_THROWS(s.parse({"", "-n", "0"})); // invalid number of receivers
|
||||||
|
REQUIRE_THROWS(s.parse({"", "-n", "1001"})); // exceeds max receivers
|
||||||
|
REQUIRE_NOTHROW(s.parse({"", "-n", "10"})); // valid
|
||||||
|
|
||||||
|
auto opts = s.parse({""});
|
||||||
|
std::visit(
|
||||||
|
[](const auto &o) {
|
||||||
|
using T = decltype(o);
|
||||||
|
if constexpr (is_type<MultiReceiverOptions, T>()) {
|
||||||
|
REQUIRE(o.numReceivers == 1); // default
|
||||||
|
REQUIRE(o.callbackEnabled == false);
|
||||||
|
|
||||||
|
} else if constexpr (is_type<FrameSyncOptions, T>()) {
|
||||||
|
REQUIRE(o.numReceivers == 1); // default
|
||||||
|
REQUIRE(o.printHeaders == false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
opts);
|
||||||
|
|
||||||
|
opts = s.parse({"", "-n", "5"});
|
||||||
|
std::visit(
|
||||||
|
[](const auto &o) {
|
||||||
|
using T = decltype(o);
|
||||||
|
if constexpr (is_type<MultiReceiverOptions, T>()) {
|
||||||
|
REQUIRE(o.numReceivers == 5);
|
||||||
|
REQUIRE(o.callbackEnabled == false); // default
|
||||||
|
|
||||||
|
} else if constexpr (is_type<FrameSyncOptions, T>()) {
|
||||||
|
REQUIRE(o.numReceivers == 5);
|
||||||
|
REQUIRE(o.printHeaders == false); // default
|
||||||
|
}
|
||||||
|
},
|
||||||
|
opts);
|
||||||
|
|
||||||
|
opts = s.parse({"", "-c", "-n", "3"});
|
||||||
|
std::visit(
|
||||||
|
[](const auto &o) {
|
||||||
|
using T = decltype(o);
|
||||||
|
if constexpr (is_type<MultiReceiverOptions, T>()) {
|
||||||
|
REQUIRE(o.numReceivers == 3);
|
||||||
|
REQUIRE(o.callbackEnabled == true);
|
||||||
|
|
||||||
|
} else if constexpr (is_type<FrameSyncOptions, T>()) {
|
||||||
|
REQUIRE(o.numReceivers == 3);
|
||||||
|
REQUIRE(o.printHeaders == true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
opts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Parse deprecated options", "[.rxcmdcall]") {
|
||||||
|
for (auto app : {AppType::SingleReceiver, AppType::MultiReceiver,
|
||||||
|
AppType::FrameSynchronizer}) {
|
||||||
|
CommandLineOptions s(app);
|
||||||
|
// argc 3 or 4, invalid
|
||||||
|
REQUIRE_THROWS(s.parse({"", "1954"}));
|
||||||
|
REQUIRE_THROWS(s.parse({
|
||||||
|
"",
|
||||||
|
"1954",
|
||||||
|
}));
|
||||||
|
// argc 3 or 4
|
||||||
|
if (app == AppType::SingleReceiver) {
|
||||||
|
REQUIRE_THROWS(
|
||||||
|
s.parse({"", "1954", "1"})); // deprecated unsupported
|
||||||
|
} else {
|
||||||
|
REQUIRE_THROWS(s.parse({"", "1954", "1", "1", "-p",
|
||||||
|
"1954"})); // mix deprecated and current
|
||||||
|
REQUIRE_THROWS(
|
||||||
|
s.parse({"", "1954", "1", "-c"})); // mix deprecated and current
|
||||||
|
REQUIRE_THROWS(s.parse(
|
||||||
|
{"", "1954", "1", "-n", "34"})); // mix deprecated and current
|
||||||
|
REQUIRE_THROWS(s.parse({"", "110", "1954"})); // mix order
|
||||||
|
REQUIRE_THROWS(s.parse({"", "1023", "10"})); // privileged port
|
||||||
|
REQUIRE_THROWS(s.parse({"", "2000", "0"})); // invalid num receivers
|
||||||
|
REQUIRE_THROWS(
|
||||||
|
s.parse({"", "2000", "1001"})); // invalid num receivers
|
||||||
|
REQUIRE_THROWS(s.parse({"", "1954", "1", "2"})); // invalid 3rd opt
|
||||||
|
|
||||||
|
REQUIRE_NOTHROW(s.parse({""}));
|
||||||
|
REQUIRE_NOTHROW(s.parse({"", "1954", "1"}));
|
||||||
|
REQUIRE_NOTHROW(s.parse({"", "1954", "1", "0"}));
|
||||||
|
REQUIRE_NOTHROW(s.parse({"", "1954", "1", "1"}));
|
||||||
|
|
||||||
|
// default
|
||||||
|
auto opts = s.parse({""});
|
||||||
|
std::visit(
|
||||||
|
[](const auto &o) {
|
||||||
|
using T = decltype(o);
|
||||||
|
if constexpr (is_type<MultiReceiverOptions, T>()) {
|
||||||
|
REQUIRE(o.port == 1954);
|
||||||
|
REQUIRE(o.numReceivers == 1);
|
||||||
|
REQUIRE(o.callbackEnabled == false);
|
||||||
|
|
||||||
|
} else if constexpr (is_type<FrameSyncOptions, T>()) {
|
||||||
|
REQUIRE(o.port == 1954);
|
||||||
|
REQUIRE(o.numReceivers == 1);
|
||||||
|
REQUIRE(o.printHeaders == false); // default
|
||||||
|
}
|
||||||
|
},
|
||||||
|
opts);
|
||||||
|
|
||||||
|
opts = s.parse({"", "1958", "10"});
|
||||||
|
std::visit(
|
||||||
|
[](const auto &o) {
|
||||||
|
using T = decltype(o);
|
||||||
|
if constexpr (is_type<MultiReceiverOptions, T>()) {
|
||||||
|
REQUIRE(o.port == 1958);
|
||||||
|
REQUIRE(o.numReceivers == 10);
|
||||||
|
REQUIRE(o.callbackEnabled == false); // default
|
||||||
|
|
||||||
|
} else if constexpr (is_type<FrameSyncOptions, T>()) {
|
||||||
|
REQUIRE(o.port == 1958);
|
||||||
|
REQUIRE(o.numReceivers == 10);
|
||||||
|
REQUIRE(o.printHeaders == false); // default
|
||||||
|
}
|
||||||
|
},
|
||||||
|
opts);
|
||||||
|
|
||||||
|
opts = s.parse({"", "1958", "10", "1"});
|
||||||
|
std::visit(
|
||||||
|
[](const auto &o) {
|
||||||
|
using T = decltype(o);
|
||||||
|
if constexpr (is_type<MultiReceiverOptions, T>()) {
|
||||||
|
REQUIRE(o.port == 1958);
|
||||||
|
REQUIRE(o.numReceivers == 10);
|
||||||
|
REQUIRE(o.callbackEnabled == true); // default
|
||||||
|
|
||||||
|
} else if constexpr (is_type<FrameSyncOptions, T>()) {
|
||||||
|
REQUIRE(o.port == 1958);
|
||||||
|
REQUIRE(o.numReceivers == 10);
|
||||||
|
REQUIRE(o.printHeaders == true); // default
|
||||||
|
}
|
||||||
|
},
|
||||||
|
opts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// test function directly
|
||||||
|
// nargs can be 1, 3 or 4
|
||||||
|
REQUIRE_THROWS(CommandLineOptions::ParseDeprecated({"", ""}));
|
||||||
|
REQUIRE_THROWS(CommandLineOptions::ParseDeprecated({"", "", "", "", ""}));
|
||||||
|
|
||||||
|
// default
|
||||||
|
auto [p, n, o] = CommandLineOptions::ParseDeprecated({""});
|
||||||
|
REQUIRE(p == 1954);
|
||||||
|
REQUIRE(n == 1);
|
||||||
|
REQUIRE(o == false);
|
||||||
|
|
||||||
|
std::tie(p, n, o) = CommandLineOptions::ParseDeprecated({"", "1955", "6"});
|
||||||
|
REQUIRE(p == 1955);
|
||||||
|
REQUIRE(n == 6);
|
||||||
|
REQUIRE(o == false);
|
||||||
|
|
||||||
|
std::tie(p, n, o) =
|
||||||
|
CommandLineOptions::ParseDeprecated({"", "1955", "6", "1"});
|
||||||
|
REQUIRE(p == 1955);
|
||||||
|
REQUIRE(n == 6);
|
||||||
|
REQUIRE(o == true);
|
||||||
|
}
|
||||||
|
} // namespace sls
|
@ -90,4 +90,5 @@ MacAddr InterfaceNameToMac(const std::string &inf);
|
|||||||
IpAddr InterfaceNameToIp(const std::string &ifn);
|
IpAddr InterfaceNameToIp(const std::string &ifn);
|
||||||
void validatePortNumber(uint16_t port);
|
void validatePortNumber(uint16_t port);
|
||||||
void validatePortRange(uint16_t startPort, int numPorts);
|
void validatePortRange(uint16_t startPort, int numPorts);
|
||||||
|
void setupSignalHandler(int signal, void (*handler)(int));
|
||||||
} // namespace sls
|
} // namespace sls
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
// SPDX-License-Identifier: LGPL-3.0-or-other
|
// SPDX-License-Identifier: LGPL-3.0-or-other
|
||||||
// Copyright (C) 2021 Contributors to the SLS Detector Package
|
// Copyright (C) 2021 Contributors to the SLS Detector Package
|
||||||
|
#include "sls/network_utils.h"
|
||||||
#include "sls/sls_detector_exceptions.h"
|
#include "sls/sls_detector_exceptions.h"
|
||||||
|
|
||||||
#include "sls/network_utils.h"
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <csignal>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <ifaddrs.h>
|
#include <ifaddrs.h>
|
||||||
@ -205,17 +206,27 @@ MacAddr InterfaceNameToMac(const std::string &inf) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void validatePortNumber(uint16_t port) {
|
void validatePortNumber(uint16_t port) {
|
||||||
// random local port. might work if internal = bad practise
|
if (port < 1024 || port > std::numeric_limits<uint16_t>::max()) {
|
||||||
if (port == 0) {
|
throw RuntimeError(std::string("Invalid port number ") +
|
||||||
throw RuntimeError("Invalid port number. Must be between 1 - 65535.");
|
std::to_string(port) +
|
||||||
|
". Must be between 1024 - 65535.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void validatePortRange(uint16_t startPort, int numPorts) {
|
void validatePortRange(uint16_t startPort, int numPorts) {
|
||||||
validatePortNumber(startPort);
|
validatePortNumber(startPort);
|
||||||
if ((startPort + numPorts) > std::numeric_limits<uint16_t>::max()) {
|
validatePortNumber(startPort + numPorts - 1);
|
||||||
throw RuntimeError("Invalid port range. Must be between 1 - 65535.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setupSignalHandler(int signal, void (*handler)(int)) {
|
||||||
|
// Catch signal SIGINT to close files and call destructors properly
|
||||||
|
struct sigaction sa {};
|
||||||
|
sa.sa_handler = handler;
|
||||||
|
sigemptyset(&sa.sa_mask); // dont block additional signals
|
||||||
|
sa.sa_flags = 0;
|
||||||
|
if (sigaction(signal, &sa, nullptr) == -1) {
|
||||||
|
throw RuntimeError("Could not set handler for " +
|
||||||
|
std::string(strsignal(signal)));
|
||||||
|
}
|
||||||
|
}
|
||||||
} // namespace sls
|
} // namespace sls
|
||||||
|
Reference in New Issue
Block a user