diff --git a/CMakeLists.txt b/CMakeLists.txt index cd70138..1e88297 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -117,13 +117,13 @@ endif() add_compile_options(-g) # optimizations -if(CMAKE_BUILD_TYPE STREQUAL "Debug") - add_compile_options(-Og) -else() - add_compile_options(-Os) -endif() - if(CMAKE_CROSSCOMPILING) + if(CMAKE_BUILD_TYPE STREQUAL "Debug") + add_compile_options(-Og) + else() + add_compile_options(-Os) + endif() + # mcu related settings set(MCU_FLAGS -mmcu=atmega32u4 -DF_CPU=16000000L) add_compile_options(${MCU_FLAGS}) @@ -136,6 +136,12 @@ if(CMAKE_CROSSCOMPILING) # disable exceptions and related metadata add_compile_options(-fno-exceptions -fno-unwind-tables) add_link_options(-Wl,--defsym,__exidx_start=0,--defsym,__exidx_end=0) +else() + if(CMAKE_BUILD_TYPE STREQUAL "Debug") + add_compile_options(-O0) + else() + add_compile_options(-O2) + endif() endif() # enable all warnings (well, not all, but some) @@ -165,10 +171,13 @@ add_executable(firmware) set_target_properties(firmware PROPERTIES CXX_STANDARD 14) -# generate firmware.bin file -objcopy(firmware "ihex" ".hex") - -add_custom_command(TARGET firmware POST_BUILD COMMAND avr-objdump ARGS -CSd firmware > firmware.txt) +if(CMAKE_CROSSCOMPILING) + # generate firmware.bin file + objcopy(firmware "ihex" ".hex") + add_custom_command( + TARGET firmware POST_BUILD COMMAND avr-objdump ARGS -CSd firmware > firmware.txt + ) +endif() add_custom_command(TARGET firmware POST_BUILD COMMAND avr-size ARGS -C --mcu=atmega32u4 firmware) diff --git a/src/modules/protocol.cpp b/src/modules/protocol.cpp index 048eb38..9378b1b 100644 --- a/src/modules/protocol.cpp +++ b/src/modules/protocol.cpp @@ -45,6 +45,7 @@ Protocol::DecodeStatus Protocol::DecodeRequest(uint8_t c) { rqState = RequestStates::Value; return DecodeStatus::NeedMoreData; default: + requestMsg.code = RequestMsgCodes::unknown; rqState = RequestStates::Error; return DecodeStatus::Error; } @@ -57,6 +58,7 @@ Protocol::DecodeStatus Protocol::DecodeRequest(uint8_t c) { rqState = RequestStates::Code; return DecodeStatus::MessageCompleted; } else { + requestMsg.code = RequestMsgCodes::unknown; rqState = RequestStates::Error; return DecodeStatus::Error; } @@ -65,6 +67,7 @@ Protocol::DecodeStatus Protocol::DecodeRequest(uint8_t c) { rqState = RequestStates::Code; return DecodeStatus::MessageCompleted; } else { + requestMsg.code = RequestMsgCodes::unknown; rqState = RequestStates::Error; return DecodeStatus::Error; } @@ -108,17 +111,10 @@ Protocol::DecodeStatus Protocol::DecodeResponse(uint8_t c) { responseMsg.request.value += c - '0'; return DecodeStatus::NeedMoreData; } else if (c == ' ') { - rspState = ResponseStates::Space; - return DecodeStatus::NeedMoreData; - } else { - rspState = ResponseStates::Error; - return DecodeStatus::Error; - } - case ResponseStates::Space: - if (c == ' ') { rspState = ResponseStates::ParamCode; return DecodeStatus::NeedMoreData; } else { + rspState = ResponseStates::Error; return DecodeStatus::Error; } case ResponseStates::ParamCode: @@ -129,6 +125,7 @@ Protocol::DecodeStatus Protocol::DecodeResponse(uint8_t c) { case 'A': case 'R': rspState = ResponseStates::ParamValue; + responseMsg.params.code = (RequestMsgCodes)c; // @@TODO this is not clean responseMsg.params.value = 0; return DecodeStatus::NeedMoreData; default: diff --git a/src/modules/protocol.h b/src/modules/protocol.h index d8ef63d..750535f 100644 --- a/src/modules/protocol.h +++ b/src/modules/protocol.h @@ -113,6 +113,12 @@ public: /// @returns number of bytes written into txbuff static uint8_t EncodeResponseQueryOperation(const RequestMsg &msg, ResponseMsgParamCodes code, uint8_t value, uint8_t *txbuff); + /// @returns the most recently lexed request message + inline const RequestMsg GetRequestMsg() const { return requestMsg; } + + /// @returns the most recently lexed response message + inline const ResponseMsg GetResponseMsg() const { return responseMsg; } + private: enum class RequestStates : uint8_t { Code, ///< starting state - expects message code @@ -126,7 +132,6 @@ private: enum class ResponseStates : uint8_t { RequestCode, ///< starting state - expects message code RequestValue, ///< expecting code value - Space, ///< expecting space ParamCode, ///< expecting param code ParamValue, ///< expecting param value Error ///< automaton in error state diff --git a/tests/unit/modules/protocol/test_protocol.cpp b/tests/unit/modules/protocol/test_protocol.cpp index 3da6aaa..db0a806 100644 --- a/tests/unit/modules/protocol/test_protocol.cpp +++ b/tests/unit/modules/protocol/test_protocol.cpp @@ -200,3 +200,157 @@ TEST_CASE("protocol::EncodeResponseQueryOperation", "[protocol]") { CHECK(txbuff[msglen - 1] == '\n'); } } + +TEST_CASE("protocol::DecodeRequest", "[protocol]") { + using namespace modules; + Protocol p; + const char *rxbuff = GENERATE( + "B0\n", "B1\n", "B2\n", + "E0\n", "E1\n", "E2\n", "E3\n", "E4\n", + "K0\n", + "L0\n", "L1\n", "L2\n", "L3\n", "L4\n", + "M0\n", "M1\n", + "P0\n", + "Q0\n", + "S0\n", "S1\n", "S2\n", "S3\n", + "T0\n", "T1\n", "T2\n", "T3\n", + "U0\n", + "W0\n", + "X0\n"); + + const char *pc = rxbuff; + for (;;) { + uint8_t c = *pc++; + if (c == 0) { + // end of input test data + break; + } else if (c == '\n') { + // regular end of message line + CHECK(p.DecodeRequest(c) == Protocol::DecodeStatus::MessageCompleted); + } else { + CHECK(p.DecodeRequest(c) == Protocol::DecodeStatus::NeedMoreData); + } + } + + // check the message type + const RequestMsg &rq = p.GetRequestMsg(); + CHECK((uint8_t)rq.code == rxbuff[0]); + CHECK(rq.value == rxbuff[1] - '0'); +} + +TEST_CASE("protocol::DecodeResponseReadFinda", "[protocol]") { + using namespace modules; + Protocol p; + const char *rxbuff = GENERATE( + "P0 A0\n", + "P0 A1\n"); + + const char *pc = rxbuff; + for (;;) { + uint8_t c = *pc++; + if (c == 0) { + // end of input test data + break; + } else if (c == '\n') { + // regular end of message line + CHECK(p.DecodeResponse(c) == Protocol::DecodeStatus::MessageCompleted); + } else { + CHECK(p.DecodeResponse(c) == Protocol::DecodeStatus::NeedMoreData); + } + } + + // check the message type + const ResponseMsg &rsp = p.GetResponseMsg(); + CHECK((uint8_t)rsp.request.code == rxbuff[0]); + CHECK(rsp.request.value == rxbuff[1] - '0'); + CHECK((uint8_t)rsp.params.code == rxbuff[3]); + CHECK((uint8_t)rsp.params.value == rxbuff[4] - '0'); +} + +TEST_CASE("protocol::DecodeResponseQueryOperation", "[protocol]") { + using namespace modules; + Protocol p; + const char *cmdReference = GENERATE( + "E0", "E1", "E2", "E3", "E4", + "K0", + "L0", "L1", "L2", "L3", "L4", + "T0", "T1", "T2", "T3", + "U0", + "W0"); + + const char *status = GENERATE( + "P0", "P1", "E0", "E1", "E9", "F"); + + std::string rxbuff(cmdReference); + rxbuff += ' '; + rxbuff += status; + rxbuff += '\n'; + + const char *pc = rxbuff.c_str(); + for (;;) { + uint8_t c = *pc++; + if (c == 0) { + // end of input test data + break; + } else if (c == '\n') { + // regular end of message line + CHECK(p.DecodeResponse(c) == Protocol::DecodeStatus::MessageCompleted); + } else { + CHECK(p.DecodeResponse(c) == Protocol::DecodeStatus::NeedMoreData); + } + } + + // check the message type + const ResponseMsg &rsp = p.GetResponseMsg(); + CHECK((uint8_t)rsp.request.code == rxbuff[0]); + CHECK(rsp.request.value == rxbuff[1] - '0'); + CHECK((uint8_t)rsp.params.code == rxbuff[3]); + if ((uint8_t)rsp.params.code != (uint8_t)ResponseMsgParamCodes::Finished) { //@@TODO again, not clean, need to define a separate msg struct for the params to make it type-safe and clean + CHECK((uint8_t)rsp.params.value == rxbuff[4] - '0'); + } +} + +TEST_CASE("protocol::DecodeRequestErrors", "[protocol]") { + using namespace modules; + Protocol p; + const char b0[] = "b0"; + CHECK(p.DecodeRequest(b0[0]) == Protocol::DecodeStatus::Error); + CHECK(p.GetRequestMsg().code == RequestMsgCodes::unknown); + CHECK(p.DecodeRequest(b0[1]) == Protocol::DecodeStatus::Error); + CHECK(p.GetRequestMsg().code == RequestMsgCodes::unknown); + + // reset protokol decoder + CHECK(p.DecodeRequest('\n') == Protocol::DecodeStatus::MessageCompleted); + CHECK(p.GetRequestMsg().code == RequestMsgCodes::unknown); + + const char B1_[] = "B1 \n"; + CHECK(p.DecodeRequest(B1_[0]) == Protocol::DecodeStatus::NeedMoreData); + CHECK(p.DecodeRequest(B1_[1]) == Protocol::DecodeStatus::NeedMoreData); + CHECK(p.DecodeRequest(B1_[2]) == Protocol::DecodeStatus::Error); + CHECK(p.GetRequestMsg().code == RequestMsgCodes::unknown); + CHECK(p.DecodeRequest(B1_[3]) == Protocol::DecodeStatus::MessageCompleted); + CHECK(p.GetRequestMsg().code == RequestMsgCodes::unknown); + + const char _B2[] = " B2\n"; + CHECK(p.DecodeRequest(_B2[0]) == Protocol::DecodeStatus::Error); + CHECK(p.GetRequestMsg().code == RequestMsgCodes::unknown); + CHECK(p.DecodeRequest(_B2[1]) == Protocol::DecodeStatus::Error); + CHECK(p.GetRequestMsg().code == RequestMsgCodes::unknown); + CHECK(p.GetRequestMsg().code == RequestMsgCodes::unknown); + CHECK(p.DecodeRequest(_B2[2]) == Protocol::DecodeStatus::Error); + CHECK(p.GetRequestMsg().code == RequestMsgCodes::unknown); + CHECK(p.DecodeRequest(_B2[3]) == Protocol::DecodeStatus::MessageCompleted); + CHECK(p.GetRequestMsg().code == RequestMsgCodes::unknown); + + const char _B0_[] = " B0 "; + CHECK(p.DecodeRequest(_B0_[0]) == Protocol::DecodeStatus::Error); + CHECK(p.GetRequestMsg().code == RequestMsgCodes::unknown); + CHECK(p.DecodeRequest(_B0_[1]) == Protocol::DecodeStatus::Error); + CHECK(p.GetRequestMsg().code == RequestMsgCodes::unknown); + CHECK(p.DecodeRequest(_B0_[2]) == Protocol::DecodeStatus::Error); + CHECK(p.GetRequestMsg().code == RequestMsgCodes::unknown); + CHECK(p.DecodeRequest(_B0_[3]) == Protocol::DecodeStatus::Error); + CHECK(p.GetRequestMsg().code == RequestMsgCodes::unknown); + CHECK(p.DecodeRequest('\n') == Protocol::DecodeStatus::MessageCompleted); + CHECK(p.GetRequestMsg().code == RequestMsgCodes::unknown); +}