deque<Message*> MessageMap::findAll(SymbolString& master) { deque<Message*> ret; if (master.size() < 5) return ret; unsigned char maxIdLength = master[4]; if (maxIdLength > m_maxIdLength) maxIdLength = m_maxIdLength; if (master.size() < 5+maxIdLength) return ret; for (int idLength = maxIdLength; ret.size()==0 && idLength >= 0; idLength--) { int exp = 7; unsigned long long key = (unsigned long long)idLength << (8 * exp + 5); key |= (unsigned long long)getMasterNumber(master[0]) << (8 * exp--); key |= (unsigned long long)master[1] << (8 * exp--); key |= (unsigned long long)master[2] << (8 * exp--); key |= (unsigned long long)master[3] << (8 * exp--); for (unsigned char i = 0; i < idLength; i++) key |= (unsigned long long)master[5 + i] << (8 * exp--); map<unsigned long long , vector<Message*> >::iterator it = m_messagesByKey.find(key); if (it != m_messagesByKey.end()) { Message* message = getFirstAvailable(it->second); if (message) ret.push_back(message); } if ((key & ID_SOURCE_MASK) != 0) { key &= ~ID_SOURCE_MASK; it = m_messagesByKey.find(key & ~ID_SOURCE_MASK); // try again without specific source master if (it != m_messagesByKey.end()) { Message* message = getFirstAvailable(it->second); if (message) ret.push_back(message); } } it = m_messagesByKey.find(key | ID_SOURCE_ACTIVE_READ); // try again with special value for active read if (it != m_messagesByKey.end()) { Message* message = getFirstAvailable(it->second); if (message) ret.push_back(message); } it = m_messagesByKey.find(key | ID_SOURCE_ACTIVE_WRITE); // try again with special value for active write if (it != m_messagesByKey.end()) { Message* message = getFirstAvailable(it->second); if (message) ret.push_back(message); } } return ret; }
result_t SingleDataField::read(const PartType partType, SymbolString& data, unsigned char offset, unsigned int& output, const char* fieldName, signed char fieldIndex) { if (partType != m_partType) return RESULT_EMPTY; switch (m_partType) { case pt_masterData: offset = (unsigned char)(offset + 5); // skip QQ ZZ PB SB NN break; case pt_slaveData: offset++; // skip NN break; default: return RESULT_ERR_INVALID_PART; } bool remainder = m_length==REMAIN_LEN && m_dataType->isAdjustableLength(); if (offset + (remainder?1:m_length) > data.size()) { return RESULT_ERR_INVALID_POS; } if (isIgnored() || (fieldName != NULL && (m_name != fieldName || fieldIndex > 0))) { return RESULT_EMPTY; } return m_dataType->readRawValue(data, offset, m_length, output); }
result_t BusHandler::scanAndWait(unsigned char dstAddress, SymbolString& slave) { if (!isValidAddress(dstAddress) || isMaster(dstAddress)) return RESULT_ERR_INVALID_ADDR; m_seenAddresses[dstAddress] |= SCAN_INIT; Message* scanMessage = m_messages->getScanMessage(); if (scanMessage==NULL) { return RESULT_ERR_NOTFOUND; } istringstream input; SymbolString master; result_t result = scanMessage->prepareMaster(m_ownMasterAddress, master, input, UI_FIELD_SEPARATOR, dstAddress); if (result==RESULT_OK) { result = sendAndWait(master, slave); if (result==RESULT_OK) { Message* message = m_messages->getScanMessage(dstAddress); if (message!=NULL && message!=scanMessage) { scanMessage = message; scanMessage->storeLastData(pt_masterData, master, 0); // update the cache, expected to work since this is a clone } } if (result!=RESULT_ERR_NO_SIGNAL) m_seenAddresses[dstAddress] |= SCAN_DONE; } if (result != RESULT_OK || slave.size() == 0) // avoid "invalid position" during decode return result; return scanMessage->storeLastData(pt_slaveData, slave, 0); // update the cache }
result_t DataFieldSet::read(const PartType partType, SymbolString& data, unsigned char offset, ostringstream& output, OutputFormat outputFormat, signed char outputIndex, bool leadingSeparator, const char* fieldName, signed char fieldIndex) { bool previousFullByteOffset = true, found = false, findFieldIndex = fieldName != NULL && fieldIndex >= 0; if (!m_uniqueNames && outputIndex<0) outputIndex = 0; for (vector<SingleDataField*>::iterator it = m_fields.begin(); it < m_fields.end(); it++) { SingleDataField* field = *it; if (partType != pt_any && field->getPartType() != partType) { if (outputIndex>=0 && !field->isIgnored()) outputIndex++; continue; } if (!previousFullByteOffset && !field->hasFullByteOffset(false)) offset--; result_t result = field->read(partType, data, offset, output, outputFormat, outputIndex, leadingSeparator, fieldName, fieldIndex); if (result < RESULT_OK) return result; offset = (unsigned char)(offset + field->getLength(partType, (unsigned char)(data.size()-offset))); previousFullByteOffset = field->hasFullByteOffset(true); if (result != RESULT_EMPTY) { found = true; leadingSeparator = true; } if (findFieldIndex && fieldName == field->getName()) { if (fieldIndex == 0) { if (!found) return RESULT_ERR_NOTFOUND; break; } fieldIndex--; } if (outputIndex>=0 && !field->isIgnored()) outputIndex++; } if (!found) { return RESULT_EMPTY; } if ((outputFormat & OF_COMMENTS) && m_comment.length() > 0) { if (outputFormat & OF_JSON) { output << ",\"comment\": \"" << m_comment << '"'; } else { output << " [" << m_comment << "]"; } } return RESULT_OK; }
result_t DataFieldSet::read(const PartType partType, SymbolString& data, unsigned char offset, unsigned int& output, const char* fieldName, signed char fieldIndex) { bool previousFullByteOffset = true, found = false, findFieldIndex = fieldName != NULL && fieldIndex >= 0; for (vector<SingleDataField*>::iterator it = m_fields.begin(); it < m_fields.end(); it++) { SingleDataField* field = *it; if (partType != pt_any && field->getPartType() != partType) continue; if (!previousFullByteOffset && !field->hasFullByteOffset(false)) offset--; result_t result = field->read(partType, data, offset, output, fieldName, fieldIndex); if (result < RESULT_OK) return result; offset = (unsigned char)(offset + field->getLength(partType, (unsigned char)(data.size()-offset))); previousFullByteOffset = field->hasFullByteOffset(true); if (result != RESULT_EMPTY) { found = true; } if (findFieldIndex && fieldName == field->getName()) { if (fieldIndex == 0) { if (!found) return RESULT_ERR_NOTFOUND; break; } fieldIndex--; } } if (!found) { return RESULT_EMPTY; } return RESULT_OK; }
result_t SingleDataField::read(const PartType partType, SymbolString& data, unsigned char offset, ostringstream& output, OutputFormat outputFormat, signed char outputIndex, bool leadingSeparator, const char* fieldName, signed char fieldIndex) { if (partType != m_partType) { return RESULT_OK; } switch (m_partType) { case pt_masterData: offset = (unsigned char)(offset + 5); // skip QQ ZZ PB SB NN break; case pt_slaveData: offset++; // skip NN break; default: return RESULT_ERR_INVALID_PART; } bool remainder = m_length==REMAIN_LEN && m_dataType->isAdjustableLength(); if (offset + (remainder?1:m_length) > data.size()) { return RESULT_ERR_INVALID_POS; } if (isIgnored() || (fieldName != NULL && (m_name != fieldName || fieldIndex > 0))) { return RESULT_EMPTY; } if (outputFormat & OF_JSON) { if (leadingSeparator) { output << ","; } if (outputIndex>=0 || m_name.empty() || !(outputFormat & OF_NAMES)) { output << "\n \"" << static_cast<signed int>(outputIndex<0?0:outputIndex) << "\": {\"name\": \"" << m_name << "\"" << ", \"value\": "; } else { output << "\n \"" << m_name << "\": {\"value\": "; } } else { if (leadingSeparator) { output << UI_FIELD_SEPARATOR; } if (outputFormat & OF_NAMES) { output << m_name << "="; } } result_t result = readSymbols(data, m_partType==pt_masterData, offset, output, outputFormat); if (result != RESULT_OK) { return result; } if ((outputFormat & OF_UNITS) && m_unit.length() > 0) { if (outputFormat & OF_JSON) { output << ", \"unit\": \"" << m_unit << '"'; } else { output << " " << m_unit; } } if ((outputFormat & OF_COMMENTS) && m_comment.length() > 0) { if (outputFormat & OF_JSON) { output << ", \"comment\": \"" << m_comment << '"'; } else { output << " [" << m_comment << "]"; } } if (outputFormat & OF_JSON) { output << "}"; } return RESULT_OK; }
result_t loadScanConfigFile(MessageMap* messages, unsigned char address, SymbolString& data, string& relativeFile) { PartType partType; if (isMaster(address)) { address = (unsigned char)(data[0]+5); // slave address of sending master partType = pt_masterData; if (data.size()<5+1+5+2+2) // skip QQ ZZ PB SB NN return RESULT_EMPTY; } else { partType = pt_slaveData; if (data.size()<1+1+5+2+2) // skip NN return RESULT_EMPTY; } DataFieldSet* identFields = DataFieldSet::getIdentFields(); // MANUFACTURER/ZZ. ( C.S.H., C.H., C.S., S.H., C., S., H., "" ) .*csv string path, prefix, ident, sw, hw; // path: cfgpath/MANUFACTURER, prefix: ZZ., ident: C[C[C[C[C]]]], sw: xxxx, hw: xxxx ostringstream out; unsigned char offset = 0; size_t field = 0; result_t result = (*identFields)[field]->read(partType, data, offset, out, 0); // manufacturer name if (result==RESULT_ERR_NOTFOUND) result = (*identFields)[field]->read(partType, data, offset, out, OF_NUMERIC); // manufacturer name if (result==RESULT_OK) { path = out.str(); transform(path.begin(), path.end(), path.begin(), ::tolower); path = string(opt.configPath) + "/" + path; out.str(""); out << setw(2) << hex << setfill('0') << nouppercase << static_cast<unsigned>(address) << "."; prefix = out.str(); out.str(""); out.clear(); offset = (unsigned char)(offset+(*identFields)[field++]->getLength(partType)); result = (*identFields)[field]->read(partType, data, offset, out, 0); // identification string } if (result==RESULT_OK) { 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 } 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 } if (result!=RESULT_OK) { logDebug(lf_main, "load scan config files: %s", getResultCode(result)); return result; } vector<string> files; bool hasTemplates = false; if (result==RESULT_OK) { hw = out.str(); 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)); if (result!=RESULT_OK) return result; if (files.empty()) return RESULT_ERR_NOTFOUND; // complete name: cfgpath/MANUFACTURER/ZZ[.C[C[C[C[C]]]]][.SWxxxx][.HWxxxx][.*].csv for (string::iterator it = ident.begin(); it!=ident.end(); it++) { if (::isspace(*it)) { ident.erase(it--); } else { *it = (char)::tolower(*it); } } size_t prefixLen = path.length()+1+prefix.length()-1; size_t bestMatch = 0; string best; for (vector<string>::iterator it = files.begin(); it!=files.end(); it++) { string name = *it; name = name.substr(prefixLen, name.length()-prefixLen+1-strlen(".csv")); // .*. 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 } } 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') break; remain.erase(remain.length()-1); } if (matches) match += remain.length(); else { continue; // IDENT mismatch } } } if (match>=bestMatch) { bestMatch = match; best = *it; } } if (best.length()==0) return RESULT_ERR_NOTFOUND; // found the right file. load the templates if necessary, then load the file itself bool readCommon = false; DataFieldTemplates* templates = getTemplates(path, ".csv", hasTemplates, false, &readCommon); if (readCommon) { result = collectConfigFiles(path, "", ".csv", files); if (result==RESULT_OK && !files.empty()) { for (vector<string>::iterator it = files.begin(); it!=files.end(); it++) { string name = *it; name = name.substr(path.length()+1, name.length()-path.length()-strlen(".csv")); // *. if (name=="_templates.") // skip templates continue; if (name.length()<3 || name.find_first_of('.')!=2) { // different from the scheme "ZZ." name = *it; result = messages->readFromFile(name, templates); if (result==RESULT_OK) logNotice(lf_main, "read common config file %s for scan %s", name.c_str(), ident.c_str()); else logError(lf_main, "error reading common config file %s for scan %s: %s", name.c_str(), ident.c_str(), getResultCode(result)); } } } } result = messages->readFromFile(best, templates); if (result!=RESULT_OK) { logError(lf_main, "error reading config file %s for scan %s: %s", best.c_str(), ident.c_str(), getResultCode(result)); return result; } logNotice(lf_main, "read config file %s for scan %s", best.c_str(), ident.c_str()); result = messages->resolveConditions(false); if (result != RESULT_OK) logError(lf_main, "error resolving conditions: %s, %s", getResultCode(result), messages->getLastError().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); return RESULT_OK; }