From 5ccdb7654f8e750de61798a40039359e091aa714 Mon Sep 17 00:00:00 2001 From: john30 Date: Sun, 10 Apr 2016 13:03:58 +0200 Subject: [PATCH] added helper method extractDefaultsFromFilename() and support extracting default circuit name from file name --- src/ebusd/main.cpp | 87 +++++++++++++------------------- src/lib/ebus/filereader.h | 102 +++++++++++++++++++++++++++++++------- 2 files changed, 118 insertions(+), 71 deletions(-) diff --git a/src/ebusd/main.cpp b/src/ebusd/main.cpp index aa210a21..ff164c5e 100644 --- a/src/ebusd/main.cpp +++ b/src/ebusd/main.cpp @@ -612,7 +612,6 @@ static bool readTemplates(const string path, const string extension, bool availa * Read the configuration files from the specified path. * @param path the path from which to read the files. * @param extension the filename extension of the files to read. - * @param templates the available @a DataFieldTemplates. * @param messages the @a MessageMap to load the messages into. * @param recursive whether to load all files recursively. * @param verbose whether to verbosely log problems. @@ -673,7 +672,7 @@ result_t loadConfigFiles(MessageMap* messages, bool verbose, bool denyRecursive) if (result != RESULT_OK) logError(lf_main, "error executing instructions: %s, %s, %s", getResultCode(result), messages->getLastError().c_str(), log.str().c_str()); else if (log.tellp() > 0) - logNotice(lf_main, log.str().c_str()); + logInfo(lf_main, log.str().c_str()); logNotice(lf_main, "found messages: %d (%d conditional on %d conditions, %d poll, %d update)", messages->size(), messages->sizeConditional(), messages->sizeConditions(), messages->sizePoll(), messages->sizePassive()); @@ -698,7 +697,8 @@ result_t loadScanConfigFile(MessageMap* messages, unsigned char address, SymbolS } } DataFieldSet* identFields = DataFieldSet::getIdentFields(); - string path, prefix, ident, sw, hw; // path: cfgpath/MANUFACTURER, prefix: ZZ., ident: C[C[C[C[C]]]], SW: xxxx, HW: xxxx + string path, prefix, ident; // path: cfgpath/MANUFACTURER, prefix: ZZ., ident: C[C[C[C[C]]]], SW: xxxx, HW: xxxx + unsigned int sw, hw; ostringstream out; unsigned char offset = 0; size_t field = 0; @@ -721,13 +721,11 @@ result_t loadScanConfigFile(MessageMap* messages, unsigned char address, SymbolS ident = out.str(); out.str(""); offset = (unsigned char)(offset+(*identFields)[field++]->getLength(partType)); - result = (*identFields)[field]->read(partType, data, offset, out, 0); // software version number + result = (*identFields)[field]->read(partType, data, offset, sw, 0); // software version number } if (result==RESULT_OK) { - sw = out.str(); - out.str(""); offset = (unsigned char)(offset+(*identFields)[field++]->getLength(partType)); - result = (*identFields)[field]->read(partType, data, offset, out, 0); // hardware version number + result = (*identFields)[field]->read(partType, data, offset, hw, 0); // hardware version number } if (result!=RESULT_OK) { logError(lf_main, "unable to load scan config %2.2x: decode %s", address, getResultCode(result)); @@ -735,7 +733,6 @@ result_t loadScanConfigFile(MessageMap* messages, unsigned char address, SymbolS } vector files; bool hasTemplates = false; - hw = out.str(); // find files matching MANUFACTURER/ZZ.*csv in cfgpath result = collectConfigFiles(path, prefix, ".csv", files, NULL, &hasTemplates); logDebug(lf_main, "found %d matching scan config files from %s with prefix %s: %s", files.size(), path.c_str(), prefix.c_str(), getResultCode(result)); @@ -747,8 +744,6 @@ result_t loadScanConfigFile(MessageMap* messages, unsigned char address, SymbolS logError(lf_main, "unable to load scan config %2.2x: no file from %s with prefix %s found", address, path.c_str(), prefix.c_str()); return RESULT_ERR_NOTFOUND; } - - // complete name: cfgpath/MANUFACTURER/ZZ[.C[C[C[C[C]]]]][.index][.*][.SWxxxx][.HWxxxx][.*].csv for (string::iterator it = ident.begin(); it!=ident.end(); it++) { if (::isspace(*it)) { ident.erase(it--); @@ -756,59 +751,47 @@ result_t loadScanConfigFile(MessageMap* messages, unsigned char address, SymbolS *it = (char)::tolower(*it); } } - size_t prefixLen = path.length()+1+prefix.length()-1; + // complete name: cfgpath/MANUFACTURER/ZZ[.C[C[C[C[C]]]]][.circuit][.suffix][.*][.SWxxxx][.HWxxxx][.*].csv size_t bestMatch = 0; string best; for (vector::iterator it = files.begin(); it!=files.end(); it++) { string name = *it; - name = name.substr(prefixLen, name.length()-prefixLen+1-strlen(".csv")); // .*. + unsigned char checkDest; + string checkIdent, useCircuit, useSuffix; + unsigned int checkSw, checkHw; + if (!FileReader::extractDefaultsFromFilename(name.substr(path.length()+1), checkDest, checkIdent, useCircuit, useSuffix, checkSw, checkHw)) { + continue; + } + if (address!=checkDest || (checkSw!=UINT_MAX && sw!=checkSw) || (checkHw!=UINT_MAX && hw!=checkHw)) { + continue; + } size_t match = 1; - if (name.length()>2) { // more than just "." - size_t pos = name.rfind(".SW"); // check for ".SWxxxx." - if (pos!=string::npos && name.find(".", pos+1)==pos+7) { - if (name.substr(pos+3, 4)==sw) - match += 6; - else { - continue; // SW mismatch + if (!checkIdent.empty()) { + string remain = ident; + bool matches = false; + while (remain.length()>0 && remain.length()>=checkIdent.length()) { + if (checkIdent==remain) { + matches = true; + break; } + if (remain[remain.length()-1]<'0' || remain[remain.length()-1]>'9') + break; + remain.erase(remain.length()-1); // remove trailing digit } - pos = name.rfind(".HW"); // check for ".HWxxxx." - if (pos!=string::npos && name.find(".", pos+1)==pos+7) { - if (name.substr(pos+3, 4)==hw) - match += 6; - else { - continue; // HW mismatch - } - } - pos = name.find(".", 1); // check for ".C[C[C[C[C]]]]." - if (ident.length()>0 && pos!=string::npos && pos>1 && pos<=6) { // up to 5 chars between two "."s, immediately after "ZZ." - string check = name.substr(1, pos-1); - string remain = ident; - bool matches = false; - while (remain.length()>0 && remain.length()>=check.length()) { - if (check==remain) { - matches = true; - break; - } - if (remain[remain.length()-1]<'0' || remain[remain.length()-1]>'9') - break; - remain.erase(remain.length()-1); // remove trailing digit - } - if (matches) - match += remain.length(); - else { - continue; // IDENT mismatch - } + if (!matches) { + continue; // IDENT mismatch } + match += remain.length(); } if (match>=bestMatch) { bestMatch = match; - best = *it; + best = name; } + break; } - if (best.length()==0) { - logError(lf_main, "unable to load scan config %2.2x: no file from %s with prefix %s matches ID \"%s\", SW%s, HW%s", address, path.c_str(), prefix.c_str(), ident.c_str(), sw.c_str(), hw.c_str()); + if (best.empty()) { + logError(lf_main, "unable to load scan config %2.2x: no file from %s with prefix %s matches ID \"%s\", SW%4.4d, HW%4.4d", address, path.c_str(), prefix.c_str(), ident.c_str(), sw, hw); return RESULT_ERR_NOTFOUND; } @@ -835,10 +818,10 @@ result_t loadScanConfigFile(MessageMap* messages, unsigned char address, SymbolS } result = messages->readFromFile(best, opt.checkConfig); if (result!=RESULT_OK) { - logError(lf_main, "error reading scan config file %s for ID \"%s\", SW%s, HW%s: %s", best.c_str(), ident.c_str(), sw.c_str(), hw.c_str(), getResultCode(result)); + logError(lf_main, "error reading scan config file %s for ID \"%s\", SW%4.4d, HW%4.4d: %s", best.c_str(), ident.c_str(), sw, hw, getResultCode(result)); return result; } - logNotice(lf_main, "read scan config file %s for ID \"%s\", SW%s, HW%s", best.c_str(), ident.c_str(), sw.c_str(), hw.c_str()); + logNotice(lf_main, "read scan config file %s for ID \"%s\", SW%4.4d, HW%4.4d", best.c_str(), ident.c_str(), sw, hw); result = messages->resolveConditions(false); if (result != RESULT_OK) @@ -849,7 +832,7 @@ result_t loadScanConfigFile(MessageMap* messages, unsigned char address, SymbolS if (result != RESULT_OK) logError(lf_main, "error executing instructions: %s, %s, %s", getResultCode(result), messages->getLastError().c_str(), log.str().c_str()); else if (log.tellp() > 0) - logNotice(lf_main, log.str().c_str()); + logInfo(lf_main, log.str().c_str()); logNotice(lf_main, "found messages: %d (%d conditional on %d conditions, %d poll, %d update)", messages->size(), messages->sizeConditional(), messages->sizeConditions(), messages->sizePoll(), messages->sizePassive()); relativeFile = best.substr(strlen(opt.configPath)+1); diff --git a/src/lib/ebus/filereader.h b/src/lib/ebus/filereader.h index 93edec9e..e24c6eb2 100644 --- a/src/lib/ebus/filereader.h +++ b/src/lib/ebus/filereader.h @@ -21,6 +21,7 @@ #include "symbol.h" #include "result.h" +#include #include #include #include @@ -93,25 +94,19 @@ public: return RESULT_ERR_NOTFOUND; } size_t lastSep = filename.find_last_of('/'); - size_t firstDot = filename.find_first_of('.', lastSep+1); - if (lastSep!=string::npos && firstDot==lastSep+1+2) { // potential destination address, matches "^ZZ." - result_t result; - string str = filename.substr(lastSep+1, 2); - unsigned char zz = (unsigned char)parseInt(str.c_str(), 16, 0, 0xff, result, NULL); - if (result==RESULT_OK && isValidAddress(zz)) { - defaultDest = str; - size_t endDot = filename.find_first_of('.', firstDot+1); - if (endDot>firstDot && endDot-firstDot<=6) { // potential ident, matches "^ZZ.IDENT." - str = filename.substr(firstDot+1, endDot-firstDot-1); // IDENT - if (str.find_first_of(' ')==string::npos) { - defaultCircuit = str; - size_t nextDot = filename.find_first_of('.', endDot+1); - if (nextDot!=string::npos && nextDot>endDot+1) { // potential index suffix, matches "^ZZ.IDENT.[0-9]*." - parseInt(filename.substr(endDot+1, nextDot-endDot-1).c_str(), 10, 1, 16, result, NULL); - if (result==RESULT_OK) - defaultSuffix = filename.substr(endDot, nextDot-endDot); // .[0-9]* - } - } + if (lastSep!=string::npos) { // potential destination address, matches "^ZZ." + // extract defaultDest, defaultCircuit, defaultSuffix from filename: + // ZZ.IDENT[.CIRCUIT][.SUFFIX].*csv + unsigned char checkDest; + string checkIdent, useCircuit, useSuffix; + unsigned int checkSw, checkHw; + if (extractDefaultsFromFilename(filename.substr(lastSep+1), checkDest, checkIdent, useCircuit, useSuffix, checkSw, checkHw)) { + defaultDest = filename.substr(lastSep+1, 2); + if (!useCircuit.empty()) { + defaultCircuit = useCircuit; + } + if (!useSuffix.empty()) { + defaultSuffix = useSuffix; } } } @@ -307,6 +302,75 @@ public: return true; } + /** + * Extract default values from the file name. + * @param name the file name (without path) in the form "ZZ[.IDENT][.CIRCUIT][.SUFFIX][.SWXXXX][.HWXXXX][.*].csv". + * @param dest the output destination address ZZ (hex digits). + * @param ident the identification part IDENT (up to 5 characters, set to empty if not present). + * @param circuit the circuit part CIRCUIT (set to IDENT if not present). + * @param suffix the suffix part SUFFIX including the leading dot (decimal digit, set to empty if not present). + * @param software the software version part SWXXXX (BCD digits, set to @a UINT_MAX if not present). + * @param hardware the hardware version part HWXXXX (BCD digits, set to @a UINT_MAX if not present). + * @return true if at least the address and the identification part were extracted, false otherwise. + */ + static bool extractDefaultsFromFilename(string name, unsigned char& dest, string& ident, string& circuit, + string& suffix, unsigned int& software, unsigned int& hardware) + { + ident = circuit = suffix = ""; + software = hardware = UINT_MAX; + if (name.length()>4 && name.substr(name.length()-4)==".csv") { + name = name.substr(0, name.length()-3); // including trailing "." + } + size_t pos = name.find('.'); + if (pos!=2) { + return false; // missing "ZZ." + } + result_t result = RESULT_OK; + dest = (unsigned char)parseInt(name.substr(0, pos).c_str(), 16, 0, 0xff, result, NULL); + if (result!=RESULT_OK || !isValidAddress(dest)) { + return false; // invalid "ZZ" + } + name.erase(0, pos); + if (name.length()>1) { + pos = name.rfind(".SW"); // check for ".SWxxxx." + if (pos!=string::npos && name.find(".", pos+1)==pos+7) { + software = parseInt(name.substr(pos+3, 4).c_str(), 10, 0, 9999, result, NULL); + if (result!=RESULT_OK) { + return false; // invalid "SWxxxx" + } + name.erase(pos, 7); + } + } + if (name.length()>1) { + pos = name.rfind(".HW"); // check for ".HWxxxx." + if (pos!=string::npos && name.find(".", pos+1)==pos+7) { + hardware = parseInt(name.substr(pos+3, 4).c_str(), 10, 0, 9999, result, NULL); + if (result!=RESULT_OK) { + return false; // invalid "HWxxxx" + } + name.erase(pos, 7); + } + } + if (name.length()>1) { + pos = name.find('.', 1); // check for ".IDENT." + if (pos!=string::npos && pos>2 && pos<=6) { // up to 5 chars between two "."s, immediately after "ZZ." + ident = circuit = name.substr(1, pos-1); + name.erase(0, pos); + pos = name.find('.', 1); // check for ".CIRCUIT." + if (pos!=string::npos && (pos>2 || name[1]<'0' || name[1]>'9')) { + circuit = name.substr(1, pos-1); + name.erase(0, pos); + pos = name.find('.', 1); // check for ".SUFFIX." + } + if (pos!=string::npos && pos==2 && name[1]>='0' && name[1]<='9') { + suffix = name.substr(0, 2); + name.erase(0, pos); + } + } + } + return true; + } + private: /** whether this instance supports rows with defaults (starting with a star). */