bring back missing scanconfig default, better help texts, add "off" to scanconfig, better argument combination validation
This commit is contained in:
+46
-56
@@ -156,6 +156,9 @@ static Network* s_network = nullptr;
|
|||||||
/** the (optionally corrected) config path for retrieving configuration files from. */
|
/** the (optionally corrected) config path for retrieving configuration files from. */
|
||||||
static string s_configPath = CONFIG_PATH;
|
static string s_configPath = CONFIG_PATH;
|
||||||
|
|
||||||
|
/** whether scanConfig or configPath were set by arguments. */
|
||||||
|
static bool s_scanConfigOrPathSet = false;
|
||||||
|
|
||||||
/** the documentation of the program. */
|
/** the documentation of the program. */
|
||||||
static const char argpdoc[] =
|
static const char argpdoc[] =
|
||||||
"A daemon for communication with eBUS heating systems.";
|
"A daemon for communication with eBUS heating systems.";
|
||||||
@@ -199,9 +202,11 @@ static const char argpdoc[] =
|
|||||||
static const struct argp_option argpoptions[] = {
|
static const struct argp_option argpoptions[] = {
|
||||||
{nullptr, 0, nullptr, 0, "Device options:", 1 },
|
{nullptr, 0, nullptr, 0, "Device options:", 1 },
|
||||||
{"device", 'd', "DEV", 0, "Use DEV as eBUS device ("
|
{"device", 'd', "DEV", 0, "Use DEV as eBUS device ("
|
||||||
"\"enh:DEVICE\" or \"enh:IP:PORT\" for enhanced device, "
|
"prefix \"ens:\" for enhanced high speed device or "
|
||||||
"\"ens:DEVICE\" for enhanced high speed serial device, "
|
"\"enh:\" for enhanced device, with "
|
||||||
"\"DEVICE\" for serial device, or \"[udp:]IP:PORT\" for network device) [/dev/ttyUSB0]", 0 },
|
"\"IP:PORT\" for network device or "
|
||||||
|
"\"DEVICE\" for serial device"
|
||||||
|
") [/dev/ttyUSB0]", 0 },
|
||||||
{"nodevicecheck", 'n', nullptr, 0, "Skip serial eBUS device test", 0 },
|
{"nodevicecheck", 'n', nullptr, 0, "Skip serial eBUS device test", 0 },
|
||||||
{"readonly", 'r', nullptr, 0, "Only read from device, never write to it", 0 },
|
{"readonly", 'r', nullptr, 0, "Only read from device, never write to it", 0 },
|
||||||
{"initsend", O_INISND, nullptr, 0, "Send an initial escape symbol after connecting device", 0 },
|
{"initsend", O_INISND, nullptr, 0, "Send an initial escape symbol after connecting device", 0 },
|
||||||
@@ -210,9 +215,13 @@ static const struct argp_option argpoptions[] = {
|
|||||||
{nullptr, 0, nullptr, 0, "Message configuration options:", 2 },
|
{nullptr, 0, nullptr, 0, "Message configuration options:", 2 },
|
||||||
{"configpath", 'c', "PATH", 0, "Read CSV config files from PATH (local folder or HTTPS URL) ["
|
{"configpath", 'c', "PATH", 0, "Read CSV config files from PATH (local folder or HTTPS URL) ["
|
||||||
CONFIG_PATH "]", 0 },
|
CONFIG_PATH "]", 0 },
|
||||||
{"scanconfig", 's', "ADDR", OPTION_ARG_OPTIONAL, "Pick CSV config files matching initial scan (ADDR="
|
{"scanconfig", 's', "ADDR", OPTION_ARG_OPTIONAL, "Pick CSV config files matching initial scan ADDR: "
|
||||||
"\"none\" or empty for no initial scan message, \"full\" for full scan, or a single hex address to scan, "
|
"empty for broadcast ident message (default when configpath is not given), "
|
||||||
"default is broadcast ident message). If combined with --checkconfig, you can add scan message data as "
|
"\"none\" for no initial scan message, "
|
||||||
|
"\"full\" for full scan, "
|
||||||
|
"a single hex address to scan, or "
|
||||||
|
"\"off\" for not picking CSV files by scan result (default when configpath is given).\n"
|
||||||
|
"If combined with --checkconfig, you can add scan message data as "
|
||||||
"arguments for checking a particular scan configuration, e.g. \"FF08070400/0AB5454850303003277201\".", 0 },
|
"arguments for checking a particular scan configuration, e.g. \"FF08070400/0AB5454850303003277201\".", 0 },
|
||||||
{"configlang", O_CFGLNG, "LANG", 0,
|
{"configlang", O_CFGLNG, "LANG", 0,
|
||||||
"Prefer LANG in multilingual configuration files [system default language]", 0 },
|
"Prefer LANG in multilingual configuration files [system default language]", 0 },
|
||||||
@@ -230,7 +239,7 @@ static const struct argp_option argpoptions[] = {
|
|||||||
#endif // HAVE_SSL
|
#endif // HAVE_SSL
|
||||||
|
|
||||||
{nullptr, 0, nullptr, 0, "eBUS options:", 3 },
|
{nullptr, 0, nullptr, 0, "eBUS options:", 3 },
|
||||||
{"address", 'a', "ADDR", 0, "Use ADDR as own bus address [31]", 0 },
|
{"address", 'a', "ADDR", 0, "Use hex ADDR as own master bus address [31]", 0 },
|
||||||
{"answer", O_ANSWER, nullptr, 0, "Actively answer to requests from other masters", 0 },
|
{"answer", O_ANSWER, nullptr, 0, "Actively answer to requests from other masters", 0 },
|
||||||
{"acquiretimeout", O_ACQTIM, "MSEC", 0, "Stop bus acquisition after MSEC ms [10]", 0 },
|
{"acquiretimeout", O_ACQTIM, "MSEC", 0, "Stop bus acquisition after MSEC ms [10]", 0 },
|
||||||
{"acquireretries", O_ACQRET, "COUNT", 0, "Retry bus acquisition COUNT times [3]", 0 },
|
{"acquireretries", O_ACQRET, "COUNT", 0, "Retry bus acquisition COUNT times [3]", 0 },
|
||||||
@@ -301,18 +310,9 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
|
|||||||
opt->noDeviceCheck = true;
|
opt->noDeviceCheck = true;
|
||||||
break;
|
break;
|
||||||
case 'r': // --readonly
|
case 'r': // --readonly
|
||||||
if (opt->answer || opt->generateSyn || opt->initialSend
|
|
||||||
|| (opt->scanConfig && opt->initialScan != 0 && opt->initialScan != ESC)) {
|
|
||||||
argp_error(state, "cannot combine readonly with answer/generatesyn/initsend/scanconfig=*");
|
|
||||||
return EINVAL;
|
|
||||||
}
|
|
||||||
opt->readOnly = true;
|
opt->readOnly = true;
|
||||||
break;
|
break;
|
||||||
case O_INISND: // --initsend
|
case O_INISND: // --initsend
|
||||||
if (opt->readOnly) {
|
|
||||||
argp_error(state, "cannot combine readonly with answer/generatesyn/initsend/scanconfig=*");
|
|
||||||
return EINVAL;
|
|
||||||
}
|
|
||||||
opt->initialSend = true;
|
opt->initialSend = true;
|
||||||
break;
|
break;
|
||||||
case O_DEVLAT: // --latency=10
|
case O_DEVLAT: // --latency=10
|
||||||
@@ -331,18 +331,19 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
|
|||||||
return EINVAL;
|
return EINVAL;
|
||||||
}
|
}
|
||||||
s_configPath = arg;
|
s_configPath = arg;
|
||||||
|
s_scanConfigOrPathSet = true;
|
||||||
break;
|
break;
|
||||||
case 's': // --scanconfig[=ADDR] (ADDR=<empty>|full|<hexaddr>)
|
case 's': // --scanconfig[=ADDR] (ADDR=<empty>|none|full|<hexaddr>|off)
|
||||||
{
|
{
|
||||||
if (opt->pollInterval == 0) {
|
symbol_t initialScan = 0;
|
||||||
argp_error(state, "scanconfig without polling may lead to invalid files included for certain products!");
|
if (!arg || arg[0] == 0) {
|
||||||
return EINVAL;
|
initialScan = BROADCAST; // default for no or empty argument
|
||||||
}
|
} else if (strcmp("none", arg) == 0) {
|
||||||
symbol_t initialScan = ESC;
|
initialScan = ESC;
|
||||||
if (!arg || arg[0] == 0 || strcmp("none", arg) == 0) {
|
|
||||||
// no further setting needed
|
|
||||||
} else if (strcmp("full", arg) == 0) {
|
} else if (strcmp("full", arg) == 0) {
|
||||||
initialScan = SYN;
|
initialScan = SYN;
|
||||||
|
} else if (strcmp("off", arg) == 0) {
|
||||||
|
// zero turns scanConfig off
|
||||||
} else {
|
} else {
|
||||||
auto address = (symbol_t)parseInt(arg, 16, 0x00, 0xff, &result);
|
auto address = (symbol_t)parseInt(arg, 16, 0x00, 0xff, &result);
|
||||||
if (result != RESULT_OK || !isValidAddress(address)) {
|
if (result != RESULT_OK || !isValidAddress(address)) {
|
||||||
@@ -355,29 +356,18 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
|
|||||||
initialScan = address;
|
initialScan = address;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (opt->readOnly && initialScan != ESC) {
|
opt->scanConfig = initialScan != 0;
|
||||||
argp_error(state, "cannot combine readonly with answer/generatesyn/initsend/scanconfig=*");
|
|
||||||
return EINVAL;
|
|
||||||
}
|
|
||||||
opt->scanConfig = true;
|
|
||||||
opt->initialScan = initialScan;
|
opt->initialScan = initialScan;
|
||||||
|
s_scanConfigOrPathSet = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case O_CFGLNG: // --configlang=LANG
|
case O_CFGLNG: // --configlang=LANG
|
||||||
opt->preferLanguage = arg;
|
opt->preferLanguage = arg;
|
||||||
break;
|
break;
|
||||||
case O_CHKCFG: // --checkconfig
|
case O_CHKCFG: // --checkconfig
|
||||||
if (opt->injectMessages) {
|
|
||||||
argp_error(state, "invalid checkconfig");
|
|
||||||
return EINVAL;
|
|
||||||
}
|
|
||||||
opt->checkConfig = true;
|
opt->checkConfig = true;
|
||||||
break;
|
break;
|
||||||
case O_DMPCFG: // --dumpconfig[=json|csv]
|
case O_DMPCFG: // --dumpconfig[=json|csv]
|
||||||
if (opt->injectMessages) {
|
|
||||||
argp_error(state, "invalid checkconfig");
|
|
||||||
return EINVAL;
|
|
||||||
}
|
|
||||||
if (!arg || arg[0] == 0 || strcmp("csv", arg) == 0) {
|
if (!arg || arg[0] == 0 || strcmp("csv", arg) == 0) {
|
||||||
// no further flags
|
// no further flags
|
||||||
opt->dumpConfig = OF_DEFINITION;
|
opt->dumpConfig = OF_DEFINITION;
|
||||||
@@ -402,17 +392,9 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
|
|||||||
argp_error(state, "invalid pollinterval");
|
argp_error(state, "invalid pollinterval");
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
}
|
}
|
||||||
if (value == 0 && opt->scanConfig) {
|
|
||||||
argp_error(state, "scanconfig without polling may lead to invalid files included for certain products!");
|
|
||||||
return EINVAL;
|
|
||||||
}
|
|
||||||
opt->pollInterval = value;
|
opt->pollInterval = value;
|
||||||
break;
|
break;
|
||||||
case 'i': // --inject[=stop]
|
case 'i': // --inject[=stop]
|
||||||
if (opt->injectMessages || opt->checkConfig) {
|
|
||||||
argp_error(state, "invalid inject");
|
|
||||||
return EINVAL;
|
|
||||||
}
|
|
||||||
opt->injectMessages = true;
|
opt->injectMessages = true;
|
||||||
opt->stopAfterInject = arg && strcmp("stop", arg) == 0;
|
opt->stopAfterInject = arg && strcmp("stop", arg) == 0;
|
||||||
break;
|
break;
|
||||||
@@ -435,10 +417,6 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case O_ANSWER: // --answer
|
case O_ANSWER: // --answer
|
||||||
if (opt->readOnly) {
|
|
||||||
argp_error(state, "cannot combine readonly with answer/generatesyn/initsend/scanconfig=*");
|
|
||||||
return EINVAL;
|
|
||||||
}
|
|
||||||
opt->answer = true;
|
opt->answer = true;
|
||||||
break;
|
break;
|
||||||
case O_ACQTIM: // --acquiretimeout=10
|
case O_ACQTIM: // --acquiretimeout=10
|
||||||
@@ -482,10 +460,6 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
|
|||||||
opt->masterCount = value;
|
opt->masterCount = value;
|
||||||
break;
|
break;
|
||||||
case O_GENSYN: // --generatesyn
|
case O_GENSYN: // --generatesyn
|
||||||
if (opt->readOnly) {
|
|
||||||
argp_error(state, "cannot combine readonly with answer/generatesyn/initsend/scanconfig=*");
|
|
||||||
return EINVAL;
|
|
||||||
}
|
|
||||||
opt->generateSyn = true;
|
opt->generateSyn = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -681,6 +655,21 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
|
|||||||
return ARGP_ERR_UNKNOWN;
|
return ARGP_ERR_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// check for invalid arg combinations
|
||||||
|
if (opt->readOnly && (opt->answer || opt->generateSyn || opt->initialSend
|
||||||
|
|| (opt->scanConfig && opt->initialScan != ESC))) {
|
||||||
|
argp_error(state, "cannot combine readonly with answer/generatesyn/initsend/scanconfig");
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
if (opt->scanConfig && opt->pollInterval == 0) {
|
||||||
|
argp_error(state, "scanconfig without polling may lead to invalid files included for certain products!");
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
if (opt->injectMessages && (opt->checkConfig || opt->dumpConfig)) {
|
||||||
|
argp_error(state, "cannot combine inject with checkconfig/dumpconfig");
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
void shutdown(bool error = false);
|
void shutdown(bool error = false);
|
||||||
@@ -895,6 +884,10 @@ int main(int argc, char* argv[], char* envp[]) {
|
|||||||
setFacilitiesLogLevel(s_opt.logAreas, s_opt.logLevel);
|
setFacilitiesLogLevel(s_opt.logAreas, s_opt.logLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!s_opt.readOnly && !s_scanConfigOrPathSet) {
|
||||||
|
s_opt.scanConfig = true;
|
||||||
|
s_opt.initialScan = BROADCAST;
|
||||||
|
}
|
||||||
if (!s_configPath.empty() && s_configPath[s_configPath.length()-1] != '/') {
|
if (!s_configPath.empty() && s_configPath[s_configPath.length()-1] != '/') {
|
||||||
s_configPath += "/";
|
s_configPath += "/";
|
||||||
}
|
}
|
||||||
@@ -943,9 +936,6 @@ int main(int argc, char* argv[], char* envp[]) {
|
|||||||
logInfo(lf_main, "configPath URL is valid");
|
logInfo(lf_main, "configPath URL is valid");
|
||||||
configHttpClient->disconnect();
|
configHttpClient->disconnect();
|
||||||
}
|
}
|
||||||
if (!s_opt.readOnly && s_opt.scanConfig && s_opt.initialScan == 0) {
|
|
||||||
s_opt.initialScan = BROADCAST;
|
|
||||||
}
|
|
||||||
|
|
||||||
s_messageMap = new MessageMap(s_opt.checkConfig, lang);
|
s_messageMap = new MessageMap(s_opt.checkConfig, lang);
|
||||||
s_scanHelper = new ScanHelper(s_messageMap, s_configPath, configLocalPrefix, configUriPrefix,
|
s_scanHelper = new ScanHelper(s_messageMap, s_configPath, configLocalPrefix, configUriPrefix,
|
||||||
|
|||||||
+7
-2
@@ -41,8 +41,13 @@ typedef struct options {
|
|||||||
unsigned int extraLatency; //!< extra transfer latency in ms [0 for USB, 10 for IP]
|
unsigned int extraLatency; //!< extra transfer latency in ms [0 for USB, 10 for IP]
|
||||||
|
|
||||||
bool scanConfig; //!< pick configuration files matching initial scan
|
bool scanConfig; //!< pick configuration files matching initial scan
|
||||||
/** the initial address to scan for scanconfig
|
/**
|
||||||
* (@a ESC=none, 0xfe=broadcast ident, @a SYN=full scan, else: single slave address). */
|
* initial address(es) to scan:
|
||||||
|
* @a ESC=none (no explicit active scanning),
|
||||||
|
* 0xfe=broadcast ident,
|
||||||
|
* @a SYN=full scan (all slave addresses),
|
||||||
|
* else: single slave address.
|
||||||
|
*/
|
||||||
symbol_t initialScan;
|
symbol_t initialScan;
|
||||||
const char* preferLanguage; //!< preferred language in configuration files
|
const char* preferLanguage; //!< preferred language in configuration files
|
||||||
bool checkConfig; //!< check config files, then stop
|
bool checkConfig; //!< check config files, then stop
|
||||||
|
|||||||
Reference in New Issue
Block a user