From 11757d145bdbe91fb6f0a8c5aeb84e005698e0f1 Mon Sep 17 00:00:00 2001 From: john30 Date: Sun, 27 Dec 2015 22:45:04 +0100 Subject: [PATCH] added chained messages, removed unused methods --- src/lib/ebus/message.h | 197 ++++++++++++++++++++++------- src/lib/ebus/test/test_message.cpp | 121 +++++++++++++----- 2 files changed, 240 insertions(+), 78 deletions(-) diff --git a/src/lib/ebus/message.h b/src/lib/ebus/message.h index 7fedeb73..d1915483 100644 --- a/src/lib/ebus/message.h +++ b/src/lib/ebus/message.h @@ -144,7 +144,7 @@ public: * @param dstAddress the new destination address. * @return the derived @a Message instance. */ - Message* derive(const unsigned char dstAddress); + virtual Message* derive(const unsigned char dstAddress); /** * Get the optional circuit name. @@ -201,38 +201,33 @@ public: */ unsigned char getSecondaryCommand() const { return m_id[1]; } - /** - * Get the full command ID bytes. - * @return the primary, secondary, and optionally further command ID bytes. - */ - vector getFullId() const { return m_id; } - /** * Get the length of the ID bytes (without primary and secondary command bytes). * @return the length of the ID bytes (without primary and secondary command bytes). */ - unsigned char getIdLength() const { return (unsigned char)(m_id.size() - 2); } + virtual unsigned char getIdLength() const { return (unsigned char)(m_id.size() - 2); } /** * Check if the full command ID starts with the given value. * @param id the ID bytes to check against. * @return true if the full command ID starts with the given value. */ - bool checkIdMatch(vector& id); + bool checkIdPrefix(vector& id); /** - * Check the ID extension (bytes exceeding @a MAX_ID_KEYLEN) against the master @a SymbolString data. + * Check the ID against the master @a SymbolString data. * @param master the master @a SymbolString to check against. - * @return true if the ID extension matches, false otherwise. + * @param index the variable in which to store the message part index, or NULL to ignore. + * @return true if the ID matches, false otherwise. */ - bool checkIdExtension(SymbolString* master); + virtual bool checkId(SymbolString& master, unsigned char* index=NULL); /** - * Check the ID extension (bytes exceeding @a MAX_ID_KEYLEN) against the other @a Message. + * Check the ID against the other @a Message. * @param other the other @a Message to check against. - * @return true if the ID extension matches, false otherwise. + * @return true if the ID matches, false otherwise. */ - bool checkIdExtension(Message* other); + virtual bool checkId(Message& other); /** * Return the key for storing in @a MessageMap. @@ -285,6 +280,11 @@ public: */ bool hasField(const char* fieldName, bool numeric=true); + /** + * @return the number of parts this message is composed of. + */ + virtual unsigned char getCount() { return 1; } + /** * Prepare the master @a SymbolString for sending a query or command to the bus. * @param srcAddress the source address to set. @@ -292,23 +292,54 @@ public: * @param input the @a istringstream to parse the formatted value(s) from. * @param separator the separator character between multiple fields. * @param dstAddress the destination address to set, or @a SYN to keep the address defined during construction. + * @param index the index of the part to prepare. * @return @a RESULT_OK on success, or an error code. */ result_t prepareMaster(const unsigned char srcAddress, SymbolString& masterData, istringstream& input, char separator=UI_FIELD_SEPARATOR, - const unsigned char dstAddress=SYN); + const unsigned char dstAddress=SYN, unsigned char index=0); + +protected: + + /** + * Prepare a part of the master data @a SymbolString for sending (everything including NN). + * @param master the master data @a SymbolString for writing symbols to. + * @param input the @a istringstream to parse the formatted value(s) from. + * @param separator the separator character between multiple fields. + * @param index the index of the part to prepare. + * @return @a RESULT_OK on success, or an error code. + */ + virtual result_t prepareMasterPart(SymbolString& master, istringstream& input, char separator, unsigned char index); + +public: /** * Prepare the slave @a SymbolString for sending an answer to the bus. * @param slaveData the slave data @a SymbolString for writing symbols to. * @return @a RESULT_OK on success, or an error code. */ - result_t prepareSlave(SymbolString& slaveData); + virtual result_t prepareSlave(SymbolString& slaveData); /** - * Decode a singular part of a received message. + * Store the last seen master and slave data. + * @param master the last seen master data. + * @param slave the last seen slave data. + * @return @a RESULT_OK on success, or an error code. + */ + virtual result_t storeLastData(SymbolString& master, SymbolString& slave); + + /** + * Store last seen master or slave data. + * @param partType the @a PartType of the data. + * @param data the last seen data. + * @param index the index of the part to store. + * @return @a RESULT_OK on success, or an error code. + */ + virtual result_t storeLastData(const PartType partType, SymbolString& data, unsigned char index); + + /** + * Decode the value from the last stored data. * @param partType the @a PartType of the data. - * @param data the unescaped data @a SymbolString for reading binary data. * @param output the @a ostringstream to append the formatted value to. * @param outputFormat the @a OutputFormat options to use. * @param leadingSeparator whether to prepend a separator before the formatted value. @@ -316,23 +347,10 @@ public: * @param fieldIndex the optional index of the named field to limit the output to, or -1. * @return @a RESULT_OK on success, or an error code. */ - result_t decode(const PartType partType, SymbolString& data, + virtual result_t decodeLastData(const PartType partType, ostringstream& output, OutputFormat outputFormat=0, bool leadingSeparator=false, const char* fieldName=NULL, signed char fieldIndex=-1); - /** - * Decode all parts of a received message. - * @param masterData the unescaped master data @a SymbolString to decode. - * @param slaveData the unescaped slave data @a SymbolString to decode. - * @param output the @a ostringstream to append the formatted value to. - * @param outputFormat the @a OutputFormat options to use. - * @param leadingSeparator whether to prepend a separator before the formatted value. - * @return @a RESULT_OK on success, or an error code. - */ - result_t decode(SymbolString& masterData, SymbolString& slaveData, - ostringstream& output, OutputFormat outputFormat=0, - bool leadingSeparator=false); - /** * Decode the value from the last stored data. * @param output the @a ostringstream to append the formatted value to. @@ -342,17 +360,17 @@ public: * @param fieldIndex the optional index of the named field to limit the output to, or -1. * @return @a RESULT_OK on success, or an error code. */ - result_t decodeLastData(ostringstream& output, OutputFormat outputFormat=0, + virtual result_t decodeLastData(ostringstream& output, OutputFormat outputFormat=0, bool leadingSeparator=false, const char* fieldName=NULL, signed char fieldIndex=-1); /** - * Decode a particular field value from the last stored data. + * Decode a particular numeric field value from the last stored data. * @param output the variable in which to store the value. * @param fieldName the name of the field to decode, or NULL for the first field. * @param fieldIndex the optional index of the named field, or -1. * @return @a RESULT_OK on success, or an error code. */ - result_t decodeLastDataField(unsigned int& output, const char* fieldName, signed char fieldIndex=-1); + virtual result_t decodeLastDataNumField(unsigned int& output, const char* fieldName, signed char fieldIndex=-1); /** * Get the last seen master data. @@ -398,7 +416,14 @@ public: */ void dump(ostream& output, vector* columns=NULL); -private: +protected: + + /** + * Write the specified column to the @a ostream. + * @param output the @a ostream to append the formatted value to. + * @param column the column indexes to write. + */ + virtual void dumpColumn(ostream& output, size_t column); /** the optional circuit name. */ const string m_circuit; @@ -433,7 +458,8 @@ private: *
  • bits 5-7: length of ID bytes (without PB/SB)
  • *
  • bits 0-4: *
      - *
    • master number (1..25) of QQ for passive message
    • + *
    • master number (1..25) of sender for passive message
    • + *
    • 0x00 for passive message with any sender
    • *
    • 0x1f for active write
    • *
    • 0x1e for active read
    • *
    @@ -483,6 +509,93 @@ private: }; +/** + * A chained @a Message that needs more than one read/write on the bus to collect/send the data. + */ +class ChainedMessage : public Message +{ +public: + + /** + * Construct a new instance. + * @param circuit the optional circuit name. + * @param name the message name (unique within the same circuit and type). + * @param isWrite whether this is a write message. + * @param comment the comment. + * @param srcAddress the source address, or @a SYN for any (only relevant if passive). + * @param dstAddress the destination address, or @a SYN for any (set later). + * @param id the primary, secondary, and optional further ID bytes common to each part of the chain. + * @param ids the primary, secondary, and optional further ID bytes for each part of the chain. + * @param lengths the data length for each part of the chain. + * @param data the @a DataField for encoding/decoding the chained message. + * @param deleteData whether to delete the @a DataField during destruction. + * @param pollPriority the priority for polling, or 0 for no polling at all. + * @param condition the @a Condition for this message, or NULL. + */ + ChainedMessage(const string circuit, const string name, + const bool isWrite, const string comment, + const unsigned char srcAddress, const unsigned char dstAddress, + const vector id, + vector< vector > ids, vector lengths, + DataField* data, const bool deleteData, + const unsigned char pollPriority, + Condition* condition=NULL); + + virtual ~ChainedMessage(); + + // @copydoc + virtual Message* derive(const unsigned char dstAddress); + + // @copydoc + virtual unsigned char getIdLength() const { return (unsigned char)(m_ids[0].size() - 2); } + + // @copydoc + virtual bool checkId(SymbolString& master, unsigned char* index=NULL); + + // @copydoc + virtual unsigned char getCount() { return (unsigned char)m_ids.size(); } + +protected: + + // @copydoc + virtual result_t prepareMasterPart(SymbolString& master, istringstream& input, char separator, unsigned char index); + +public: + + // @copydoc + virtual result_t storeLastData(SymbolString& master, SymbolString& slave); + + // @copydoc + virtual result_t storeLastData(const PartType partType, SymbolString& data, unsigned char index); + +protected: + + // @copydoc + virtual void dumpColumn(ostream& output, size_t column); + +private: + + /** the primary, secondary, and optional further ID bytes for each part of the chain. */ + vector< vector > m_ids; + + /** the data length for each part of the chain. */ + vector m_lengths; + + /** array of the last seen master datas. */ + SymbolString** m_lastMasterDatas; + + /** array of the last seen slave datas. */ + SymbolString** m_lastSlaveDatas; + + /** array of the system times when the corresponding master data was last updated, 0 for never. */ + time_t* m_lastMasterUpdateTimes; + + /** array of the system times when the corresponding slave data was last updated, 0 for never. */ + time_t* m_lastSlaveUpdateTimes; + +}; + + /** * A function that compares the weighted poll priority of two @a Message instances. */ @@ -807,14 +920,6 @@ public: */ Message* find(SymbolString& master); - /** - * Find all @a Message instances for the specified master data. - * @param master the master @a SymbolString for identifying the @a Message. - * @return the @a Message instance, or NULL. - * Note: the caller may not free the returned instances. - */ - deque findAll(SymbolString& master); - /** * Invalidate cached data of the @a Message and all other instances with a matching name key. * @param message the @a Message to invalidate. diff --git a/src/lib/ebus/test/test_message.cpp b/src/lib/ebus/test/test_message.cpp index 33592041..074c0e56 100644 --- a/src/lib/ebus/test/test_message.cpp +++ b/src/lib/ebus/test/test_message.cpp @@ -65,11 +65,16 @@ int main() {"tempsensor,temp;sensor,,Temperatursensor", "", "", "", "template"}, {"r,message circuit,message name,message comment,,25,B509,0d2800,,,tempsensor", "temp=-14.00 Temperatursensor [Temperatur];sensor=ok [Fühlerstatus]", "ff25b509030d2800", "0320ff00", "mD"}, {"r,message circuit,message name,message comment,,25,B509,0d2800,,,tempsensor,,field unit,field comment", "temp=-14.00 field unit [field comment];sensor=ok [Fühlerstatus]", "ff25b509030d2800", "0320ff00", "mD"}, + {"r,message circuit,message name,message comment,,25,B509,0d2800,,,tempsensor,,field unit,field comment", "\n \"temp\": {\"value\": -14.00},\n \"sensor\": {\"value\": \"ok\"}", "ff25b509030d2800", "0320ff00", "mj"}, + {"r,message circuit,message name,message comment,,25,B509,0d2800,,,tempsensor,,field unit,field comment", "\n \"temp\": {\"value\": -14.00, \"unit\": \"field unit\", \"comment\": \"field comment\"},\n \"sensor\": {\"value\": \"ok\", \"comment\": \"Fühlerstatus\"}", "ff25b509030d2800", "0320ff00", "mJ"}, {"r,message circuit,message name,message comment,,25,B509,0d2800,,,temp,,field unit,field comment,,,sensor", "temp=-14.00 field unit [field comment];sensor=ok [Fühlerstatus]", "ff25b509030d2800", "0320ff00", "mD"}, + {"r,message circuit,message name,message comment,,25,B509,0d2800,,,D2C,,°C,Temperatur,,,sensor", "\n \"0\": {\"name\": \"\", \"value\": -14.00},\n \"1\": {\"name\": \"sensor\", \"value\": \"ok\"}", "ff25b509030d2800", "0320ff00", "mj"}, {"u,,first,,,fe,0700,,x,,bda", "26.10.2014", "fffe07000426100614", "00", "p"}, {"u,broadcast,hwStatus,,,fe,b505,27,,,UCH,,,,,,UCH,,,,,,UCH,,,", "0;19;0", "10feb505042700130097", "00", ""}, {"w,,first,,,15,b509,0400,date,,bda", "26.10.2014", "ff15b50906040026100614", "00", "m"}, {"w,,first,,,15,b509", "", "ff15b50900", "00", "m"}, + {"w,,,,,,b505,2d", "", "", "", "defaults"}, + {"w,,offset,,,50,,,,,temp", "0.50", "ff50b505042d080000", "00", "md"}, {"r,ehp,time,,,08,b509,0d2800,,,time", "15:00:17", "ff08b509030d2800", "0311000f", "md"}, {"r,ehp,time,,,08;10,b509,0d2800,,,time", "", "", "", "c"}, {"r,ehp,time,,,08;09,b509,0d2800,,,time", "15:00:17", "ff08b509030d2800", "0311000f", "md*"}, @@ -90,11 +95,17 @@ int main() {"r,ehp,bad,invalid pos,,50,B5ff,,,s,HEX:8;tempsensor;tempsensor;tempsensor;tempsensor;tempsensor;power;power,,,", "", "", "", "c" }, {"r,ehp,ApplianceCode,,,08,b509,0d4301,,,UCH,", "9", "ff08b509030d4301", "0109", "d" }, {"r,ehp,,,,08,b509,0d", "", "", "", "defaults" }, + {"w,ehp,,,,08,b509,0e", "", "", "", "defaults" }, {"[brinetowater],ehp,ApplianceCode,,,,4;6;8;9;10", "", "", "", "condition" }, {"[airtowater]r,ehp,notavailable,,,,,0100,,,uch", "1", "", "", "c" }, {"[brinetowater]r,ehp,available,,,,,0100,,,uch", "1", "ff08b509030d0100", "0101", "d" }, - {"r,,x,,,,,\"6800\",,,UCH,,,bit0=\"comment, continued comment", "", "", "", "c"}, - {"r,,x,,,,,\"6800\",,,UCH,,\"\",\"bit0=\"comment, continued comment\"", "=1 [bit0=\"comment, continued comment]", "ff08b509030d6800", "0101", "mD"}, + {"r,,x,,,,,\"6800\",,,UCH,,,bit0=\"comment, continued comment", "", "", "", "c" }, + {"r,,x,,,,,\"6800\",,,UCH,,\"\",\"bit0=\"comment, continued comment\"", "=1 [bit0=\"comment, continued comment]", "ff08b509030d6800", "0101", "mD" }, + {"r,ehp,multi,,,,,0001:5;0002;0003,longname,,STR:15", "ABCDEFGHIJKLMNO", "ff08b509030d0001;ff08b509030d0003;ff08b509030d0002", "054142434445;054b4c4d4e4f;05464748494a", "mdC" }, + {"r,ehp,multi,,,,,01;02;03,longname,,STR:15", "ABCDEFGHIJKLMNO", "ff08b509020d01;ff08b509020d03;ff08b509020d02", "084142434445464748;054b4c4d4e4f;02494a", "mdC" }, + {"w,ehp,multi,,,,,01:8;02:2;03,longname,,STR:15", "ABCDEFGHIJKLMNO", "ff08b5090a0e014142434445464748;ff08b509040e02494a;ff08b509070e034b4c4d4e4f", "00;00;00", "mdC" }, + {"w,ehp,multi,,,,,01:8;02:2;0304,longname,,STR:15", "ABCDEFGHIJKLMNO", "ff08b5090a0e014142434445464748;ff08b509040e02494a;ff08b509070e034b4c4d4e4f", "00;00;00", "cC" }, + {"r,ehp,scan,chained scan,,08,B509,24:9;25;26;27,,,IGN,,,,id4,,STR:28", "21074500100027790000000000N8", "ff08b5090124;ff08b5090125;ff08b5090126;ff08b5090127", "09003231303734353030;09313030303237373930;09303030303030303030;024E38", "mdC" }, }; templates = new DataFieldTemplates(); MessageMap* messages = new MessageMap(); @@ -102,21 +113,13 @@ int main() map &conditions = messages->getConditions(); Message* message = NULL; vector deleteMessages; + vector mstrs; + vector sstrs; + mstrs.resize(1); + sstrs.resize(1); for (size_t i = 0; i < sizeof(checks) / sizeof(checks[0]); i++) { string check[5] = checks[i]; string inputStr = check[1]; - SymbolString mstr(true); - result_t result = mstr.parseHex(check[2]); - if (result != RESULT_OK) { - cout << "\"" << check[0] << "\": parse \"" << check[2] << "\" error: " << getResultCode(result) << endl; - continue; - } - SymbolString sstr(true); - result = sstr.parseHex(check[3]); - if (result != RESULT_OK) { - cout << "\"" << check[0] << "\": parse \"" << check[3] << "\" error: " << getResultCode(result) << endl; - continue; - } string flags = check[4]; bool isTemplate = flags == "template"; bool isCondition = flags == "condition"; @@ -124,12 +127,68 @@ int main() bool dontMap = flags.find('m') != string::npos; bool onlyMap = flags.find('M') != string::npos; bool failedCreate = flags.find('c') != string::npos; - bool decodeVerbose = flags.find('D') != string::npos; - bool decode = decodeVerbose || (flags.find('d') != string::npos); + bool isChain = flags.find('C') != string::npos; + bool decodeJson = flags.find('j') != string::npos || flags.find('J') != string::npos; + bool decodeVerbose = flags.find('D') != string::npos || flags.find('J') != string::npos; + bool decode = decodeJson || decodeVerbose || (flags.find('d') != string::npos); bool failedPrepare = flags.find('p') != string::npos; bool failedPrepareMatch = flags.find('P') != string::npos; bool multi = flags.find('*') != string::npos; bool withInput = flags.find('i') != string::npos; + result_t result; + if (isChain) { + size_t pos = 0; + string token; + istringstream stream(check[2]); + while (getline(stream, token, VALUE_SEPARATOR) != 0) { + if (pos>=mstrs.size()) + mstrs.resize(pos+1); + else if (mstrs[pos]!=NULL) + delete mstrs[pos]; + mstrs[pos] = new SymbolString(false); + result = mstrs[pos]->parseHex(token); + if (result != RESULT_OK) { + cout << "\"" << check[0] << "\": parse \"" << token << "\" error: " << getResultCode(result) << endl; + break; + } + pos++; + } + pos = 0; + stream.str(check[3]); + stream.clear(); + while (getline(stream, token, VALUE_SEPARATOR) != 0) { + if (pos>=sstrs.size()) + sstrs.resize(pos+1); + else if (sstrs[pos]!=NULL) + delete sstrs[pos]; + sstrs[pos] = new SymbolString(false); + result = sstrs[pos]->parseHex(token); + if (result != RESULT_OK) { + cout << "\"" << check[0] << "\": parse \"" << token << "\" error: " << getResultCode(result) << endl; + break; + } + pos++; + } + if (result != RESULT_OK) + continue; + } else { + if (mstrs[0]!=NULL) + delete mstrs[0]; + mstrs[0] = new SymbolString(true); + result = mstrs[0]->parseHex(check[2]); + if (result != RESULT_OK) { + cout << "\"" << check[0] << "\": parse \"" << check[2] << "\" error: " << getResultCode(result) << endl; + continue; + } + if (sstrs[0]!=NULL) + delete sstrs[0]; + sstrs[0] = new SymbolString(true); + result = sstrs[0]->parseHex(check[3]); + if (result != RESULT_OK) { + cout << "\"" << check[0] << "\": parse \"" << check[3] << "\" error: " << getResultCode(result) << endl; + continue; + } + } string item; vector entries; @@ -190,7 +249,7 @@ int main() continue; } if (entries.size() == 0) { - message = messages->find(mstr); + message = messages->find(*mstrs[0]); if (message == NULL) { cout << "\"" << check[2] << "\": find error: NULL" << endl; continue; @@ -254,7 +313,7 @@ int main() deleteMessages.clear(); if (onlyMap) continue; - Message* foundMessage = messages->find(mstr); + Message* foundMessage = messages->find(*mstrs[0]); if (foundMessage == message) cout << " find OK" << endl; else if (foundMessage == NULL) @@ -268,7 +327,10 @@ int main() if (message->isPassive() || decode) { ostringstream output; - result = message->decode(mstr, sstr, output, decodeVerbose?OF_VERBOSE:0); + for (unsigned char index=0; indexgetCount(); index++) { + message->storeLastData(*mstrs[index], *sstrs[index]); + } + result = message->decodeLastData(output, (decodeVerbose?OF_VERBOSE:0)|(decodeJson?OF_JSON:0), false); if (result != RESULT_OK) { cout << " \"" << check[2] << "\" / \"" << check[3] << "\": decode error: " << getResultCode(result) << endl; @@ -277,16 +339,6 @@ int main() cout << " \"" << check[2] << "\" / \"" << check[3] << "\": decode OK" << endl; bool match = inputStr == output.str(); verify(false, "decode", check[2] + "/" + check[3], match, inputStr, output.str()); - ostringstream output2; - result = message->decodeLastData(output2, decodeVerbose?OF_VERBOSE:0); - if (result != RESULT_OK) { - cout << " \"" << check[2] << "\" / \"" << check[3] << "\": decodeLast error: " - << getResultCode(result) << endl; - continue; - } - cout << " \"" << check[2] << "\" / \"" << check[3] << "\": decodeLast OK" << endl; - match = output.str() == output2.str(); - verify(false, "decodeLast", check[2] + "/" + check[3], match, output.str(), output2.str()); } if (!message->isPassive() && (withInput || !decode)) { istringstream input(inputStr); @@ -307,8 +359,8 @@ int main() } cout << " \"" << inputStr << "\": prepare OK" << endl; - bool match = writeMstr==mstr; - verify(failedPrepareMatch, "prepare", inputStr, match, mstr.getDataStr(), writeMstr.getDataStr()); + bool match = writeMstr==*mstrs[0]; + verify(failedPrepareMatch, "prepare", inputStr, match, mstrs[0]->getDataStr(), writeMstr.getDataStr()); } } @@ -322,7 +374,12 @@ int main() delete templates; delete messages; - + for (vector::iterator it = mstrs.begin(); it!=mstrs.end(); it++) { + delete *it; + } + for (vector::iterator it = sstrs.begin(); it!=sstrs.end(); it++) { + delete *it; + } return 0; }