From d3bb15c5b0db74d8010393ff94f9d7c57d18cb54 Mon Sep 17 00:00:00 2001 From: Philippe Leduc Date: Mon, 9 Dec 2024 16:49:12 +0100 Subject: [PATCH 1/3] Start unit test on mailbox response --- CMakeLists.txt | 4 +- lib/include/kickcat/Mailbox.h | 3 + lib/master/src/helpers.cc | 2 + lib/slave/include/kickcat/ESC/Lan9252.h | 4 +- lib/slave/include/kickcat/ESC/XMC4800.h | 5 +- lib/slave/src/ESC/Lan9252.cc | 2 + lib/slave/src/ESC/XMC4800.cc | 3 +- unit/{mailbox-t.cc => mailbox/request-t.cc} | 1 + unit/mailbox/response-t.cc | 170 ++++++++++++++++++++ unit/mocks/ESC.h | 25 +++ 10 files changed, 213 insertions(+), 6 deletions(-) rename unit/{mailbox-t.cc => mailbox/request-t.cc} (99%) create mode 100644 unit/mailbox/response-t.cc create mode 100644 unit/mocks/ESC.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 189ee16b..3d9c3e57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,16 +38,18 @@ if (GTest_FOUND) unit/gateway-t.cc unit/kickcat-t.cc unit/link-t.cc - unit/mailbox-t.cc unit/prints-t.cc unit/protocol-t.cc unit/slave-t.cc unit/socket-t.cc unit/Time.cc unit/CoE/protocol-t.cc + unit/mailbox/request-t.cc + unit/mailbox/response-t.cc ) target_link_libraries(kickcat_unit kickcat GTest::gmock_main) + target_include_directories(kickcat_unit PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/unit) set_kickcat_properties(kickcat_unit) add_test(NAME kickcat COMMAND kickcat_unit WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) diff --git a/lib/include/kickcat/Mailbox.h b/lib/include/kickcat/Mailbox.h index 75f6f25e..4198be1c 100644 --- a/lib/include/kickcat/Mailbox.h +++ b/lib/include/kickcat/Mailbox.h @@ -192,6 +192,9 @@ namespace kickcat::mailbox::response void process(); // Process a message in the to_process_ queue if any void send(); // Send a message in the to_send_ queue if any, keep it in the queue if the ESC is not ready yet + // Access on the next message to send: mainly for unit test + std::vector const& readyToSend() const { return to_send_.front(); } + private: void replyError(std::vector&& raw_message, uint16_t code); diff --git a/lib/master/src/helpers.cc b/lib/master/src/helpers.cc index 7ed35ea5..be1ae7fb 100644 --- a/lib/master/src/helpers.cc +++ b/lib/master/src/helpers.cc @@ -5,6 +5,7 @@ namespace kickcat { +// LCOV_EXCL_START void selectInterface(std::string& nominal_if, std::string& redundant_if) { std::vector> interfaces; @@ -40,4 +41,5 @@ namespace kickcat *ifname.second = netIntefaces[index].name; } } +// LCOV_EXCL_STOP } diff --git a/lib/slave/include/kickcat/ESC/Lan9252.h b/lib/slave/include/kickcat/ESC/Lan9252.h index 9003a8aa..57400e07 100644 --- a/lib/slave/include/kickcat/ESC/Lan9252.h +++ b/lib/slave/include/kickcat/ESC/Lan9252.h @@ -10,6 +10,7 @@ namespace kickcat { +// LCOV_EXCL_START // Host to Network byte order helper (Reminder: EtherCAT is LE, network is BE) // SPI INSTRUCTIONS @@ -103,8 +104,7 @@ namespace kickcat std::shared_ptr spi_interface_; // TODO shared ptr like link in bus.h }; - - +// LCOV_EXCL_STOP } diff --git a/lib/slave/include/kickcat/ESC/XMC4800.h b/lib/slave/include/kickcat/ESC/XMC4800.h index 72382bd4..4ea25edf 100644 --- a/lib/slave/include/kickcat/ESC/XMC4800.h +++ b/lib/slave/include/kickcat/ESC/XMC4800.h @@ -5,9 +5,9 @@ namespace kickcat { +// LCOV_EXCL_START constexpr uint32_t ECAT0_BASE_ADDRESS = 0x54010000; - constexpr uint32_t ECAT0_END_ADDRESS = 0x5401FFFF; - + constexpr uint32_t ECAT0_END_ADDRESS = 0x5401FFFF; class XMC4800 final : public AbstractESC { @@ -20,6 +20,7 @@ namespace kickcat int32_t read(uint16_t address, void* data, uint16_t size) override; int32_t write(uint16_t address, void const* data, uint16_t size) override; }; +// LCOV_EXCL_STOP } #endif diff --git a/lib/slave/src/ESC/Lan9252.cc b/lib/slave/src/ESC/Lan9252.cc index 1077cd45..1f110354 100644 --- a/lib/slave/src/ESC/Lan9252.cc +++ b/lib/slave/src/ESC/Lan9252.cc @@ -5,6 +5,7 @@ namespace kickcat { +// LCOV_EXCL_START Lan9252::Lan9252(std::shared_ptr spi_interface) : spi_interface_(spi_interface) { @@ -272,4 +273,5 @@ namespace kickcat return size; } +// LCOV_EXCL_STOP } diff --git a/lib/slave/src/ESC/XMC4800.cc b/lib/slave/src/ESC/XMC4800.cc index 67845991..836c2218 100644 --- a/lib/slave/src/ESC/XMC4800.cc +++ b/lib/slave/src/ESC/XMC4800.cc @@ -3,7 +3,7 @@ namespace kickcat { - +// LCOV_EXCL_START hresult XMC4800::init() { return hresult::OK; @@ -34,4 +34,5 @@ namespace kickcat std::memcpy(reinterpret_cast(ECAT0_BASE_ADDRESS + address), data, size); return size; } +// LCOV_EXCL_STOP } diff --git a/unit/mailbox-t.cc b/unit/mailbox/request-t.cc similarity index 99% rename from unit/mailbox-t.cc rename to unit/mailbox/request-t.cc index 86fddb49..b58f696c 100644 --- a/unit/mailbox-t.cc +++ b/unit/mailbox/request-t.cc @@ -1,4 +1,5 @@ #include + #include "kickcat/Mailbox.h" #include "kickcat/CoE/mailbox/request.h" diff --git a/unit/mailbox/response-t.cc b/unit/mailbox/response-t.cc new file mode 100644 index 00000000..0f6df1ad --- /dev/null +++ b/unit/mailbox/response-t.cc @@ -0,0 +1,170 @@ +#include + +#include "mocks/ESC.h" + +#include "kickcat/Mailbox.h" +#include "kickcat/CoE/mailbox/request.h" +#include "kickcat/CoE/mailbox/response.h" + +using namespace kickcat; +using namespace kickcat::mailbox::response; + +std::vector createTestSDO(uint16_t index, uint8_t subindex, bool CA=false) +{ + uint32_t data; + uint32_t data_size = sizeof(data); + mailbox::request::SDOMessage msg{256, index, subindex, CA, CoE::SDO::request::UPLOAD, &data, &data_size, 1ms}; + + std::vector raw_message; + raw_message.insert(raw_message.begin(), msg.data(), msg.data() + 256); + return raw_message; +} + +CoE::Dictionary createTestDictionary() +{ + CoE::Dictionary dictionary; + { + CoE::Object object + { + 0x1018, + CoE::ObjectCode::ARRAY, + "Identity Object", + {} + }; + CoE::addEntry(object,0,8, 7,static_cast(5),"Subindex 000", 0x4); + CoE::addEntry(object,1,32,7,static_cast(7),"Vendor ID", 0x6a5); + CoE::addEntry(object,2,32,7,static_cast(7),"Product code", 0xb0cad0); + CoE::addEntry(object,3,32,7,static_cast(7),"Revision number",0x0); + CoE::addEntry(object,4,32,7,static_cast(7),"Serial number", 0xcafedeca); + dictionary.push_back(std::move(object)); + } + + return dictionary; +} + +class Response : public ::testing::Test +{ +public: + void SetUp() override + { + mbx.enableCoE(std::move(createTestDictionary())); + } + + kickcat::MockESC esc; + Mailbox mbx{&esc, 256, 1}; +}; + +TEST_F(Response, SDO_read_expedited_OK) +{ + std::vector raw_message = createTestSDO(0x1018, 2); + auto response_msg = createSDOMessage(&mbx, std::move(raw_message)); + + ASSERT_EQ(mailbox::ProcessingResult::FINALIZE, response_msg->process()); + ASSERT_EQ(0, response_msg->size()); // message data was transfered in mbx send queue + auto const& msg = mbx.readyToSend(); + + { + auto header = pointData(msg.data()); + auto coe = pointData(header); + auto sdo = pointData(coe); + auto payload = pointData(sdo); + + ASSERT_EQ(CoE::Service::SDO_RESPONSE, coe->service); + ASSERT_EQ(10, header->len); + ASSERT_EQ(0xb0cad0, *payload); + } +} + +TEST_F(Response, SDO_read_object_ENOENT) +{ + std::vector raw_message = createTestSDO(0x2000, 2); + auto response_msg = createSDOMessage(&mbx, std::move(raw_message)); + + ASSERT_EQ(mailbox::ProcessingResult::FINALIZE, response_msg->process()); + ASSERT_EQ(0, response_msg->size()); // message data was transfered in mbx send queue + auto const& msg = mbx.readyToSend(); + + { + auto header = pointData(msg.data()); + auto coe = pointData(header); + auto sdo = pointData(coe); + auto payload = pointData(sdo); + + ASSERT_EQ(CoE::Service::SDO_REQUEST, coe->service); + ASSERT_EQ(10, header->len); + ASSERT_EQ(CoE::SDO::abort::OBJECT_DOES_NOT_EXIST, *payload); + } +} + +TEST_F(Response, SDO_read_entry_ENOENT) +{ + std::vector raw_message = createTestSDO(0x1018, 200); + auto response_msg = createSDOMessage(&mbx, std::move(raw_message)); + + ASSERT_EQ(mailbox::ProcessingResult::FINALIZE, response_msg->process()); + ASSERT_EQ(0, response_msg->size()); // message data was transfered in mbx send queue + auto const& msg = mbx.readyToSend(); + + { + auto header = pointData(msg.data()); + auto coe = pointData(header); + auto sdo = pointData(coe); + auto payload = pointData(sdo); + + ASSERT_EQ(CoE::Service::SDO_REQUEST, coe->service); + ASSERT_EQ(10, header->len); + ASSERT_EQ(CoE::SDO::abort::SUBINDEX_DOES_NOT_EXIST, *payload); + } +} + +TEST_F(Response, SDO_wrong_size) +{ + std::vector raw_message = createTestSDO(0x1018, 200); + { + auto header = pointData(raw_message.data()); + header->len = 1; + } + auto response_msg = createSDOMessage(&mbx, std::move(raw_message)); + + ASSERT_EQ(mailbox::ProcessingResult::FINALIZE, response_msg->process()); + ASSERT_EQ(0, response_msg->size()); // message data was transfered in mbx send queue + auto const& msg = mbx.readyToSend(); + + { + auto header = pointData(msg.data()); + auto err = pointData(header); + + ASSERT_EQ(mailbox::ERR, header->type); + ASSERT_EQ(4, header->len); + ASSERT_EQ(0x1, err->type); + ASSERT_EQ(mailbox::Error::SIZE_TOO_SHORT, err->detail); + } +} + +TEST_F(Response, SDO_read_CA) +{ + std::vector raw_message = createTestSDO(0x1018, 0, true); + auto response_msg = createSDOMessage(&mbx, std::move(raw_message)); + + ASSERT_EQ(mailbox::ProcessingResult::FINALIZE, response_msg->process()); + ASSERT_EQ(0, response_msg->size()); // message data was transfered in mbx send queue + auto const& msg = mbx.readyToSend(); + + { + auto header = pointData(msg.data()); + auto coe = pointData(header); + auto sdo = pointData(coe); + auto size = pointData(sdo); + auto subidx = pointData(size); + auto entry = pointData(subidx); + + ASSERT_EQ(CoE::Service::SDO_RESPONSE, coe->service); + ASSERT_EQ(27, header->len); // 10 + 17 + ASSERT_EQ(17, *size); + ASSERT_EQ(4, *subidx); + ASSERT_EQ(0x6a5, entry[0]); + ASSERT_EQ(0xb0cad0, entry[1]); + ASSERT_EQ(0, entry[2]); + ASSERT_EQ(0xcafedeca,entry[3]); + } +} diff --git a/unit/mocks/ESC.h b/unit/mocks/ESC.h new file mode 100644 index 00000000..c874f3c7 --- /dev/null +++ b/unit/mocks/ESC.h @@ -0,0 +1,25 @@ +#ifndef KICKCAT_UNIT_MOCKS_ESC_H +#define KICKCAT_UNIT_MOCKS_ESC_H + +#include + +#include "kickcat/AbstractESC.h" + +namespace kickcat +{ + class MockESC : public AbstractESC + { + public: + MockESC() : AbstractESC() + {} + + virtual ~MockESC() + {} + + MOCK_METHOD(hresult, init, (), (override)); + MOCK_METHOD(int32_t, read, (uint16_t, void*, uint16_t), (override)); + MOCK_METHOD(int32_t, write, (uint16_t, void const*, uint16_t), (override)); + }; +} + +#endif From 748477f3a25544ce9fbdb03ee9bf5e5e2e9a27c5 Mon Sep 17 00:00:00 2001 From: Philippe Leduc Date: Tue, 10 Dec 2024 15:09:33 +0100 Subject: [PATCH 2/3] Fix: SDO response - complete access Fix: ESI parser do not load bit offset --- examples/master/load_esi/load_esi.cc | 4 +- examples/slave/nuttx/xmc4800/od_populator.cc | 44 ++++++++-------- lib/include/kickcat/CoE/EsiParser.h | 4 +- lib/include/kickcat/CoE/OD.h | 11 ++-- lib/src/CoE/EsiParser.cc | 55 +++++++++++++------- lib/src/CoE/OD.cc | 7 ++- lib/src/CoE/mailbox/response.cc | 42 +++++++++------ unit/mailbox/response-t.cc | 10 ++-- 8 files changed, 102 insertions(+), 75 deletions(-) diff --git a/examples/master/load_esi/load_esi.cc b/examples/master/load_esi/load_esi.cc index 83840b73..4b8b7905 100644 --- a/examples/master/load_esi/load_esi.cc +++ b/examples/master/load_esi/load_esi.cc @@ -3,12 +3,12 @@ using namespace kickcat; -int main() +int main(int, char const* argv[]) { CoE::EsiParser parser; nanoseconds t1 = since_epoch(); - CoE::Dictionary coe_dict = parser.load("ingenia_esi.xml"); + CoE::Dictionary coe_dict = parser.load(argv[1]); nanoseconds t2 = since_epoch(); // dangerous lack of error checking. diff --git a/examples/slave/nuttx/xmc4800/od_populator.cc b/examples/slave/nuttx/xmc4800/od_populator.cc index 57cca18d..c6dff0d7 100644 --- a/examples/slave/nuttx/xmc4800/od_populator.cc +++ b/examples/slave/nuttx/xmc4800/od_populator.cc @@ -16,11 +16,11 @@ namespace kickcat::CoE "Identity Object", {} }; - CoE::addEntry(object,0,8, 7,static_cast(5),"Subindex 000",0x4); - CoE::addEntry(object,1,32,7,static_cast(7),"Vendor ID",0x6a5); - CoE::addEntry(object,2,32,7,static_cast(7),"Product code",0xb0cad0); - CoE::addEntry(object,3,32,7,static_cast(7),"Revision number",0x0); - CoE::addEntry(object,4,32,7,static_cast(7),"Serial number",0xcafedeca); + CoE::addEntry(object,0,8,0,7,static_cast(5),"Subindex 000",0x4); + CoE::addEntry(object,1,32,16,7,static_cast(7),"Vendor ID",0x6a5); + CoE::addEntry(object,2,32,48,7,static_cast(7),"Product code",0xb0cad0); + CoE::addEntry(object,3,32,80,7,static_cast(7),"Revision number",0x0); + CoE::addEntry(object,4,32,112,7,static_cast(7),"Serial number",0xcafedeca); dictionary.push_back(std::move(object)); } @@ -32,8 +32,8 @@ namespace kickcat::CoE "RxPDO Map 1", {} }; - CoE::addEntry(object,0,8,15,static_cast(5),"Subindex 000",0x1); - CoE::addEntry(object,1,32,15,static_cast(7),"RxPDO Map 1 Element 1",0x10); + CoE::addEntry(object,0,8,0,15,static_cast(5),"Subindex 000",0x1); + CoE::addEntry(object,1,32,16,15,static_cast(7),"RxPDO Map 1 Element 1",0x10); dictionary.push_back(std::move(object)); } @@ -45,8 +45,8 @@ namespace kickcat::CoE "TxPDO Map 1", {} }; - CoE::addEntry(object,0,8,15,static_cast(5),"Subindex 000",0x1); - CoE::addEntry(object,1,32,15,static_cast(7),"TxPDO Map 1 Element 1",0xe0); + CoE::addEntry(object,0,8,0,15,static_cast(5),"Subindex 000",0x1); + CoE::addEntry(object,1,32,16,15,static_cast(7),"TxPDO Map 1 Element 1",0xe0); dictionary.push_back(std::move(object)); } @@ -58,11 +58,11 @@ namespace kickcat::CoE "Sync manager type", {} }; - CoE::addEntry(object,0,8,7,static_cast(5),"Subindex 0",0x4); - CoE::addEntry(object,1,8,7,static_cast(5),"Subindex 1",0x1); - CoE::addEntry(object,2,8,7,static_cast(5),"Subindex 2",0x2); - CoE::addEntry(object,3,8,7,static_cast(5),"Subindex 3",0x3); - CoE::addEntry(object,4,8,7,static_cast(5),"Subindex 4",0x4); + CoE::addEntry(object,0,8,0,7,static_cast(5),"Subindex 0",0x4); + CoE::addEntry(object,1,8,16,7,static_cast(5),"Subindex 1",0x1); + CoE::addEntry(object,2,8,24,7,static_cast(5),"Subindex 2",0x2); + CoE::addEntry(object,3,8,32,7,static_cast(5),"Subindex 3",0x3); + CoE::addEntry(object,4,8,40,7,static_cast(5),"Subindex 4",0x4); dictionary.push_back(std::move(object)); } @@ -74,8 +74,8 @@ namespace kickcat::CoE "RxPDO assign", {} }; - CoE::addEntry(object,0,8,15,static_cast(5),"Subindex 000",0x1); - CoE::addEntry(object,1,32,15,static_cast(7),"RxPDO assign Element 1",0x1600); + CoE::addEntry(object,0,8,0,15,static_cast(5),"Subindex 000",0x1); + CoE::addEntry(object,1,32,16,15,static_cast(7),"RxPDO assign Element 1",0x1600); dictionary.push_back(std::move(object)); } @@ -87,8 +87,8 @@ namespace kickcat::CoE "TxPDO assign", {} }; - CoE::addEntry(object,0,8,15,static_cast(5),"Subindex 000",0x1); - CoE::addEntry(object,1,32,15,static_cast(7),"TxPDO assign Element 1",0x1a00); + CoE::addEntry(object,0,8,0,15,static_cast(5),"Subindex 000",0x1); + CoE::addEntry(object,1,32,16,15,static_cast(7),"TxPDO assign Element 1",0x1a00); dictionary.push_back(std::move(object)); } @@ -100,12 +100,12 @@ namespace kickcat::CoE "FreezeValue", {} }; - CoE::addEntry(object,0,8,7,static_cast(5),"Subindex 000",0x2); - CoE::addEntry(object,1,32,63,static_cast(7),"ForceSensor0",0x2); - CoE::addEntry(object,2,32,63,static_cast(7),"IMU",0x2); + CoE::addEntry(object,0,8,0,7,static_cast(5),"Subindex 000",0x2); + CoE::addEntry(object,1,32,16,63,static_cast(7),"ForceSensor0",0x2); + CoE::addEntry(object,2,32,48,63,static_cast(7),"IMU",0x2); dictionary.push_back(std::move(object)); } - return dictionary; + return dictionary; } } diff --git a/lib/include/kickcat/CoE/EsiParser.h b/lib/include/kickcat/CoE/EsiParser.h index 2be0361c..d3eecc33 100644 --- a/lib/include/kickcat/CoE/EsiParser.h +++ b/lib/include/kickcat/CoE/EsiParser.h @@ -38,7 +38,7 @@ namespace kickcat::CoE uint16_t loadAccess(tinyxml2::XMLNode* node); - std::tuple toType(tinyxml2::XMLNode* node); + std::tuple parseType(tinyxml2::XMLNode* node); tinyxml2::XMLNode* findNodeType(tinyxml2::XMLNode* node); @@ -61,7 +61,7 @@ namespace kickcat::CoE tinyxml2::XMLElement* dtypes_; tinyxml2::XMLElement* objects_; - static const std::unordered_map> BASIC_TYPES; + static const std::unordered_map BASIC_TYPES; static const std::unordered_map SM_CONF; }; } diff --git a/lib/include/kickcat/CoE/OD.h b/lib/include/kickcat/CoE/OD.h index d1e6dd5c..d01bbd1f 100644 --- a/lib/include/kickcat/CoE/OD.h +++ b/lib/include/kickcat/CoE/OD.h @@ -175,8 +175,8 @@ namespace kickcat::CoE struct Entry // ETG1000.5 6.1.4.2.1 Formal model { Entry() = default; - Entry(uint8_t subindex, uint16_t bitlen, uint16_t access, - DataType type, std::string const& description); + Entry(uint8_t subindex, uint16_t bitlen, uint16_t bitoff, + uint16_t access, DataType type, std::string const& description); ~Entry(); Entry(Entry const&) = delete; @@ -187,6 +187,7 @@ namespace kickcat::CoE uint8_t subindex; uint16_t bitlen; // For PDO, shall be < 11888 + uint16_t bitoff; // offset in bit uint16_t access{0}; DataType type; // default value @@ -218,10 +219,10 @@ namespace kickcat::CoE std::tuple findObject(Dictionary& dict, uint16_t index, uint8_t subindex); template - void addEntry(Object &object, uint8_t subindex, uint16_t bitlen, uint16_t access, - DataType type, std::string const& description, T data) + void addEntry(Object &object, uint8_t subindex, uint16_t bitlen, uint16_t bitoff, + uint16_t access, DataType type, std::string const& description, T data) { - object.entries.emplace_back(subindex, bitlen, access, type, description); + object.entries.emplace_back(subindex, bitlen, bitoff, access, type, description); if constexpr(std::is_same_v) { object.entries.back().data = malloc(bitlen/8); diff --git a/lib/src/CoE/EsiParser.cc b/lib/src/CoE/EsiParser.cc index c51fe099..0652311d 100644 --- a/lib/src/CoE/EsiParser.cc +++ b/lib/src/CoE/EsiParser.cc @@ -9,19 +9,19 @@ using namespace tinyxml2; namespace kickcat::CoE { - const std::unordered_map> EsiParser::BASIC_TYPES + const std::unordered_map EsiParser::BASIC_TYPES { - {"BOOL", {DataType::BOOLEAN, 1 }}, - {"BYTE", {DataType::BYTE, 8 }}, - {"SINT", {DataType::INTEGER8, 8 }}, - {"USINT", {DataType::UNSIGNED8, 8 }}, - {"INT", {DataType::INTEGER16, 16 }}, - {"UINT", {DataType::UNSIGNED16, 16 }}, - {"DINT", {DataType::INTEGER32, 32 }}, - {"UDINT", {DataType::UNSIGNED32, 32 }}, - {"LINT", {DataType::INTEGER64, 64 }}, - {"ULINT", {DataType::UNSIGNED64, 64 }}, - {"REAL", {DataType::REAL32, 32 }}, + {"BOOL", DataType::BOOLEAN }, + {"BYTE", DataType::BYTE }, + {"SINT", DataType::INTEGER8 }, + {"USINT", DataType::UNSIGNED8 }, + {"INT", DataType::INTEGER16 }, + {"UINT", DataType::UNSIGNED16 }, + {"DINT", DataType::INTEGER32 }, + {"UDINT", DataType::UNSIGNED32 }, + {"LINT", DataType::INTEGER64 }, + {"ULINT", DataType::UNSIGNED64 }, + {"REAL", DataType::REAL32 }, }; const std::unordered_map EsiParser::SM_CONF @@ -89,7 +89,7 @@ namespace kickcat::CoE sms_type.name = "Sync manager type"; // create first entry (array size) - sms_type.entries.push_back(CoE::Entry{0, 8, Access::READ, DataType::UNSIGNED8, "Subindex 0"}); + sms_type.entries.push_back(CoE::Entry{0, 8, 0, Access::READ, DataType::UNSIGNED8, "Subindex 0"}); auto sm = firstChildElement(device_, "Sm"); while (sm) @@ -98,6 +98,7 @@ namespace kickcat::CoE entry.subindex = sms_type.entries.size(); entry.access = Access::READ; entry.bitlen = 8; + entry.bitoff = sms_type.entries.size() * 8 + 8; // + 8 for padding of the first entry entry.description = "Subindex " + std::to_string(sms_type.entries.size()); entry.type = DataType::UNSIGNED8; entry.data = malloc(1); @@ -257,7 +258,7 @@ namespace kickcat::CoE return flags; } - std::tuple EsiParser::toType(XMLNode* node) + std::tuple EsiParser::parseType(XMLNode* node) { auto node_type = node->FirstChildElement("Type"); if (not node_type) @@ -265,19 +266,30 @@ namespace kickcat::CoE node_type = node->FirstChildElement("BaseType"); } + + auto it = BASIC_TYPES.find(node_type->GetText()); if (it != BASIC_TYPES.end()) { - return it->second; + uint16_t bitlen = atoi(node->FirstChildElement("BitSize")->GetText()); + + uint16_t bitoff = 0; + auto node_bitoff = node->FirstChildElement("BitOffs"); + if (node_bitoff) + { + bitoff = atoi(node->FirstChildElement("BitOffs")->GetText()); + } + + return {it->second, bitlen, bitoff}; } if(strstr(node_type->GetText(), "STRING")) { uint32_t bitlen = toNumber(node->FirstChildElement("BitSize")); - return {DataType::VISIBLE_STRING, bitlen}; + return {DataType::VISIBLE_STRING, bitlen, 0}; } - return {DataType::UNKNOWN, 0}; + return {DataType::UNKNOWN, 0, 0}; } @@ -304,7 +316,7 @@ namespace kickcat::CoE Object object; object.index = toNumber(node->FirstChildElement("Index")); object.name = node->FirstChildElement("Name")->GetText(); - auto [type, bitlen] = toType(node); + auto [type, bitlen, bitoff] = parseType(node); if (isBasic(type)) { // Basic type: no subindex in the ESI file because it is defined directly in the object node. @@ -313,6 +325,7 @@ namespace kickcat::CoE auto& entry = object.entries.at(0); entry.subindex = 0; entry.bitlen = bitlen; + entry.bitoff = bitoff; entry.type = type; entry.access = loadAccess(node); @@ -332,13 +345,14 @@ namespace kickcat::CoE entry.description = node_name->GetText(); } - auto [subitem_type, subitem_bitlen] = toType(node_subitem); + auto [subitem_type, subitem_bitlen, subitem_bitoff] = parseType(node_subitem); if (isBasic(subitem_type)) { object.code = ObjectCode::RECORD; entry.type = subitem_type; entry.bitlen = subitem_bitlen; + entry.bitoff = subitem_bitoff; entry.subindex = toNumber(node_subitem->FirstChildElement("SubIdx")); entry.access = loadAccess(node_subitem); @@ -349,9 +363,10 @@ namespace kickcat::CoE object.code = ObjectCode::ARRAY; auto node_array_type = findNodeType(node_subitem); - auto [array_type, array_bitlen] = toType(node_array_type); + auto [array_type, array_bitlen, array_bitoff] = parseType(node_array_type); entry.type = array_type; entry.bitlen = array_bitlen; + entry.bitoff = array_bitoff; entry.access = loadAccess(node_subitem); auto node_array_info = node_array_type->FirstChildElement("ArrayInfo"); diff --git a/lib/src/CoE/OD.cc b/lib/src/CoE/OD.cc index 107ff2f9..0cea2bf1 100644 --- a/lib/src/CoE/OD.cc +++ b/lib/src/CoE/OD.cc @@ -171,7 +171,8 @@ namespace kickcat::CoE result << " * Subindex " << (int)entry.subindex << '\n'; result << " Desc: " << entry.description << '\n'; result << " Type: " << toString(entry.type) << '\n'; - result << " Bitlen: " << entry.bitlen << '\n'; + result << " BitLen: " << entry.bitlen << '\n'; + result << " BitOff: " << entry.bitoff << '\n'; result << " Access: " << Access::toString(entry.access) << '\n'; result << " Data: "; @@ -244,9 +245,10 @@ namespace kickcat::CoE return result; } - Entry::Entry(uint8_t subindex_in, uint16_t bitlen_in, uint16_t access_in, DataType type_in, std::string const& description_in) + Entry::Entry(uint8_t subindex_in, uint16_t bitlen_in, uint16_t bitoff_in, uint16_t access_in, DataType type_in, std::string const& description_in) : subindex{subindex_in} , bitlen{bitlen_in} + , bitoff{bitoff_in} , access{access_in} , type{type_in} , description{description_in} @@ -274,6 +276,7 @@ namespace kickcat::CoE { subindex = std::move(other.subindex); bitlen = std::move(other.bitlen); + bitoff = std::move(other.bitoff); access = std::move(other.access); type = std::move(other.type); description = std::move(other.description); diff --git a/lib/src/CoE/mailbox/response.cc b/lib/src/CoE/mailbox/response.cc index 8bbeef67..aa443b1a 100644 --- a/lib/src/CoE/mailbox/response.cc +++ b/lib/src/CoE/mailbox/response.cc @@ -139,12 +139,12 @@ namespace kickcat::mailbox::response uint32_t size = entry->bitlen / 8; header_->len = sizeof(mailbox::Header) + sizeof(CoE::ServiceData); + sdo_->size_indicator = 1; // always 1 on upload response if (size <= 4) { // expedited sdo_->transfer_type = 1; sdo_->block_size = 4 - size; - sdo_->size_indicator = 1; } else { @@ -152,11 +152,9 @@ namespace kickcat::mailbox::response std::memcpy(payload_, &size, 4); payload_ += 4; header_->len += size; - sdo_->size_indicator = 0; } std::memcpy(payload_, entry->data, size); - coe_->service = CoE::Service::SDO_RESPONSE; sdo_->command = CoE::SDO::response::UPLOAD; reply(std::move(data_)); @@ -168,10 +166,13 @@ namespace kickcat::mailbox::response ProcessingResult SDOMessage::uploadComplete(CoE::Object* object) { - sdo_->transfer_type = 0; // complete access -> not expedited + sdo_->size_indicator = 1; // always 1 on upload response + sdo_->transfer_type = 0; // complete access -> not expedited uint32_t size = 0; uint8_t number_of_entries = *(uint8_t*)object->entries.at(0).data; + uint16_t skip_offset = object->entries.at(sdo_->subindex).bitoff / 8; + for (uint32_t i = sdo_->subindex; i <= number_of_entries; ++i) { auto* entry = &object->entries.at(i); @@ -183,9 +184,10 @@ namespace kickcat::mailbox::response beforeHooks(CoE::Access::READ, entry); - uint32_t entry_size = entry->bitlen / 8; - std::memcpy(payload_ + 4 + size, entry->data, entry_size); - size += entry_size; + uint16_t entry_size = entry->bitlen / 8; + uint16_t entry_off = entry->bitoff / 8 - skip_offset; + std::memcpy(payload_ + 4 + entry_off, entry->data, entry_size); + size = entry_size + entry_off; // only record the last position + last entry size afterHooks(CoE::Access::READ, entry); } @@ -194,7 +196,7 @@ namespace kickcat::mailbox::response header_->len = sizeof(mailbox::Header) + sizeof(CoE::ServiceData) + size; coe_->service = CoE::Service::SDO_RESPONSE; - sdo_->command = CoE::SDO::response::UPLOAD_SEGMENTED; + sdo_->command = CoE::SDO::response::UPLOAD; reply(std::move(data_)); return ProcessingResult::FINALIZE; } @@ -242,25 +244,27 @@ namespace kickcat::mailbox::response uint32_t msg_size; std::memcpy(&msg_size, payload_, 4); - uint32_t size = 0; + uint8_t* start_offset = payload_ + 4; + uint8_t* current_offset = start_offset; + uint16_t skip_offset = object->entries.at(sdo_->subindex).bitoff / 8; + if (sdo_->subindex == 0) { auto* entry = &object->entries.at(0); beforeHooks(CoE::Access::WRITE, entry); std::memcpy(entry->data, payload_ + 4, 1); - size += 2; + current_offset += 1; afterHooks(CoE::Access::WRITE, entry); } uint16_t subindex = 1; - while (size < msg_size) + while ((current_offset - start_offset) < msg_size) { - if (subindex > object->entries.size()) + if (subindex >= object->entries.size()) { - abort(CoE::SDO::abort::SUBINDEX_DOES_NOT_EXIST); - return ProcessingResult::FINALIZE; + break; } auto* entry = &object->entries.at(subindex); @@ -273,15 +277,19 @@ namespace kickcat::mailbox::response beforeHooks(CoE::Access::WRITE, entry); uint32_t entry_size = entry->bitlen / 8; - std::memcpy(entry->data, payload_ + 4 + size, entry_size); - size += entry_size; + uint32_t entry_off = entry->bitoff / 8; + current_offset = start_offset + entry_off - skip_offset; + + std::memcpy(entry->data, current_offset, entry_size); + current_offset += entry_size; subindex++; afterHooks(CoE::Access::WRITE, entry); } + header_->len = 10; coe_->service = CoE::Service::SDO_RESPONSE; - sdo_->command = CoE::SDO::response::DOWNLOAD_SEGMENTED; + sdo_->command = CoE::SDO::response::DOWNLOAD; reply(std::move(data_)); return ProcessingResult::FINALIZE; } diff --git a/unit/mailbox/response-t.cc b/unit/mailbox/response-t.cc index 0f6df1ad..d1d17d8a 100644 --- a/unit/mailbox/response-t.cc +++ b/unit/mailbox/response-t.cc @@ -31,11 +31,11 @@ CoE::Dictionary createTestDictionary() "Identity Object", {} }; - CoE::addEntry(object,0,8, 7,static_cast(5),"Subindex 000", 0x4); - CoE::addEntry(object,1,32,7,static_cast(7),"Vendor ID", 0x6a5); - CoE::addEntry(object,2,32,7,static_cast(7),"Product code", 0xb0cad0); - CoE::addEntry(object,3,32,7,static_cast(7),"Revision number",0x0); - CoE::addEntry(object,4,32,7,static_cast(7),"Serial number", 0xcafedeca); + CoE::addEntry(object,0,8,0, 7,static_cast(5),"Subindex 000", 0x4); + CoE::addEntry(object,1,32,8,7,static_cast(7),"Vendor ID", 0x6a5); + CoE::addEntry(object,2,32,40,7,static_cast(7),"Product code", 0xb0cad0); + CoE::addEntry(object,3,32,72,7,static_cast(7),"Revision number",0x0); + CoE::addEntry(object,4,32,104,7,static_cast(7),"Serial number", 0xcafedeca); dictionary.push_back(std::move(object)); } From 68f67539a0be29e2f0aafdcc30082da5db358ad4 Mon Sep 17 00:00:00 2001 From: Philippe Leduc Date: Tue, 10 Dec 2024 15:25:33 +0100 Subject: [PATCH 3/3] Fix: OD generator: addEntry type size is not enforced, leading to size mismatch. --- .../nuttx/xmc4800/boards/wdc_foot/foot.bin | Bin 564 -> 564 bytes .../nuttx/xmc4800/boards/wdc_foot/foot.xml | 135 +++++++++++++++++- examples/slave/nuttx/xmc4800/od_populator.cc | 58 +++++--- lib/include/kickcat/CoE/OD.h | 12 +- lib/src/CoE/EsiParser.cc | 14 +- lib/src/CoE/OD.cc | 70 +++++---- lib/src/CoE/mailbox/response.cc | 4 +- tools/od_generator.cc | 25 +++- 8 files changed, 249 insertions(+), 69 deletions(-) diff --git a/examples/slave/nuttx/xmc4800/boards/wdc_foot/foot.bin b/examples/slave/nuttx/xmc4800/boards/wdc_foot/foot.bin index 04137c74844824169d72debd92ebd864a8a2b066..d133b49ed61388ddee472c85638fcab307b09a0d 100644 GIT binary patch delta 176 zcmWm2%MHRX6hzT^wqrZa2M44Gjj}+{0f-%~z=}R7f<{=Rzo1#2tGk9Z{GMi)@5}C= zqr)5Ku`D>j!W9pIqenBL(qgQD(Ix5Vv-AwD3~Y*Q7&{r6dYPC9nc0rAW!cHXuF8)6 ZBzq3~vtr XMC4800 Wandercraft - foot + Foot @@ -36,7 +36,8 @@ XMC4800 Wandercraft - 402 + + 0 @@ -157,6 +158,79 @@ + + DT1C12 + 32 + + 0 + Subindex 000 + USINT + 8 + 0 + + rw + o + 0 + 0 + + + + Elements + DT1C12ARR + 16 + 16 + + rw + o + + + + + DT1C12ARR + UINT + 16 + + 1 + 1 + + + + DT1C13 + 32 + + 0 + Subindex 000 + USINT + 8 + 0 + + rw + o + 0 + 0 + + + + Elements + DT1C13ARR + 16 + 16 + + rw + o + + + + + DT1C13ARR + UINT + 16 + + 1 + 1 + + + DT2000 80 @@ -204,7 +278,21 @@ - + + #x1000 + Device Type + UDINT + 32 + + 00000000 + + + ro + m + 0 + 0 + + #x1018 Identity Object @@ -244,6 +332,47 @@ + + #x1c12 + RxPDO assign + DT1C12 + 32 + + + Subindex 000 + + 01 + + + + RxPDO assign Element 1 + + 0016 + + + + + + #x1c13 + TxPDO assign + DT1C13 + 32 + + + Subindex 000 + + 01 + + + + TxPDO assign Element 1 + + 001a + + + + + #x2000 FreezeValue diff --git a/examples/slave/nuttx/xmc4800/od_populator.cc b/examples/slave/nuttx/xmc4800/od_populator.cc index c6dff0d7..019d5153 100644 --- a/examples/slave/nuttx/xmc4800/od_populator.cc +++ b/examples/slave/nuttx/xmc4800/od_populator.cc @@ -8,19 +8,31 @@ namespace kickcat::CoE { CoE::Dictionary dictionary; + { + CoE::Object object + { + 0x1000, + CoE::ObjectCode::VAR, + "Device Type", + {} + }; + CoE::addEntry(object,0,32,0,7,static_cast(7),"",0x0); + dictionary.push_back(std::move(object)); + } + { CoE::Object object { 0x1018, - CoE::ObjectCode::ARRAY, + CoE::ObjectCode::RECORD, "Identity Object", {} }; - CoE::addEntry(object,0,8,0,7,static_cast(5),"Subindex 000",0x4); - CoE::addEntry(object,1,32,16,7,static_cast(7),"Vendor ID",0x6a5); - CoE::addEntry(object,2,32,48,7,static_cast(7),"Product code",0xb0cad0); - CoE::addEntry(object,3,32,80,7,static_cast(7),"Revision number",0x0); - CoE::addEntry(object,4,32,112,7,static_cast(7),"Serial number",0xcafedeca); + CoE::addEntry(object,0,8,0,7,static_cast(5),"Subindex 000",0x4); + CoE::addEntry(object,1,32,16,7,static_cast(7),"Vendor ID",0x6a5); + CoE::addEntry(object,2,32,48,7,static_cast(7),"Product code",0xb0cad0); + CoE::addEntry(object,3,32,80,7,static_cast(7),"Revision number",0x0); + CoE::addEntry(object,4,32,112,7,static_cast(7),"Serial number",0xcafedeca); dictionary.push_back(std::move(object)); } @@ -32,8 +44,8 @@ namespace kickcat::CoE "RxPDO Map 1", {} }; - CoE::addEntry(object,0,8,0,15,static_cast(5),"Subindex 000",0x1); - CoE::addEntry(object,1,32,16,15,static_cast(7),"RxPDO Map 1 Element 1",0x10); + CoE::addEntry(object,0,8,0,15,static_cast(5),"Subindex 000",0x1); + CoE::addEntry(object,1,32,16,15,static_cast(7),"RxPDO Map 1 Element 1",0x10); dictionary.push_back(std::move(object)); } @@ -45,8 +57,8 @@ namespace kickcat::CoE "TxPDO Map 1", {} }; - CoE::addEntry(object,0,8,0,15,static_cast(5),"Subindex 000",0x1); - CoE::addEntry(object,1,32,16,15,static_cast(7),"TxPDO Map 1 Element 1",0xe0); + CoE::addEntry(object,0,8,0,15,static_cast(5),"Subindex 000",0x1); + CoE::addEntry(object,1,32,16,15,static_cast(7),"TxPDO Map 1 Element 1",0xe0); dictionary.push_back(std::move(object)); } @@ -58,11 +70,11 @@ namespace kickcat::CoE "Sync manager type", {} }; - CoE::addEntry(object,0,8,0,7,static_cast(5),"Subindex 0",0x4); - CoE::addEntry(object,1,8,16,7,static_cast(5),"Subindex 1",0x1); - CoE::addEntry(object,2,8,24,7,static_cast(5),"Subindex 2",0x2); - CoE::addEntry(object,3,8,32,7,static_cast(5),"Subindex 3",0x3); - CoE::addEntry(object,4,8,40,7,static_cast(5),"Subindex 4",0x4); + CoE::addEntry(object,0,8,0,7,static_cast(5),"Subindex 0",0x4); + CoE::addEntry(object,1,8,16,7,static_cast(5),"Subindex 1",0x1); + CoE::addEntry(object,2,8,24,7,static_cast(5),"Subindex 2",0x2); + CoE::addEntry(object,3,8,32,7,static_cast(5),"Subindex 3",0x3); + CoE::addEntry(object,4,8,40,7,static_cast(5),"Subindex 4",0x4); dictionary.push_back(std::move(object)); } @@ -74,8 +86,8 @@ namespace kickcat::CoE "RxPDO assign", {} }; - CoE::addEntry(object,0,8,0,15,static_cast(5),"Subindex 000",0x1); - CoE::addEntry(object,1,32,16,15,static_cast(7),"RxPDO assign Element 1",0x1600); + CoE::addEntry(object,0,8,0,15,static_cast(5),"Subindex 000",0x1); + CoE::addEntry(object,1,16,16,15,static_cast(6),"RxPDO assign Element 1",0x1600); dictionary.push_back(std::move(object)); } @@ -87,8 +99,8 @@ namespace kickcat::CoE "TxPDO assign", {} }; - CoE::addEntry(object,0,8,0,15,static_cast(5),"Subindex 000",0x1); - CoE::addEntry(object,1,32,16,15,static_cast(7),"TxPDO assign Element 1",0x1a00); + CoE::addEntry(object,0,8,0,15,static_cast(5),"Subindex 000",0x1); + CoE::addEntry(object,1,16,16,15,static_cast(6),"TxPDO assign Element 1",0x1a00); dictionary.push_back(std::move(object)); } @@ -96,13 +108,13 @@ namespace kickcat::CoE CoE::Object object { 0x2000, - CoE::ObjectCode::ARRAY, + CoE::ObjectCode::RECORD, "FreezeValue", {} }; - CoE::addEntry(object,0,8,0,7,static_cast(5),"Subindex 000",0x2); - CoE::addEntry(object,1,32,16,63,static_cast(7),"ForceSensor0",0x2); - CoE::addEntry(object,2,32,48,63,static_cast(7),"IMU",0x2); + CoE::addEntry(object,0,8,0,7,static_cast(5),"Subindex 000",0x2); + CoE::addEntry(object,1,32,16,63,static_cast(7),"ForceSensor0",0x2); + CoE::addEntry(object,2,32,48,63,static_cast(7),"IMU",0x2); dictionary.push_back(std::move(object)); } diff --git a/lib/include/kickcat/CoE/OD.h b/lib/include/kickcat/CoE/OD.h index d01bbd1f..57a61e01 100644 --- a/lib/include/kickcat/CoE/OD.h +++ b/lib/include/kickcat/CoE/OD.h @@ -205,6 +205,7 @@ namespace kickcat::CoE std::string dataToString() const; }; + std::string toString(Entry const& entry); struct Object // ETG1000.5 6.1.4.2.1 Formal model { @@ -223,16 +224,19 @@ namespace kickcat::CoE uint16_t access, DataType type, std::string const& description, T data) { object.entries.emplace_back(subindex, bitlen, bitoff, access, type, description); + auto& alloc = object.entries.back().data; + std::size_t size = bitlen / 8; + alloc = std::malloc(size); + if constexpr(std::is_same_v) { - object.entries.back().data = malloc(bitlen/8); - memcpy(object.entries.back().data, data, bitlen/8); + std::memcpy(alloc, data, size); } else { - T* dataAlloc = new T(data); - object.entries.back().data = dataAlloc; + std::memcpy(alloc, &data, size); } + } Dictionary createOD(); diff --git a/lib/src/CoE/EsiParser.cc b/lib/src/CoE/EsiParser.cc index 0652311d..4ecdc154 100644 --- a/lib/src/CoE/EsiParser.cc +++ b/lib/src/CoE/EsiParser.cc @@ -271,13 +271,12 @@ namespace kickcat::CoE auto it = BASIC_TYPES.find(node_type->GetText()); if (it != BASIC_TYPES.end()) { - uint16_t bitlen = atoi(node->FirstChildElement("BitSize")->GetText()); - + uint16_t bitlen = toNumber(node->FirstChildElement("BitSize")); uint16_t bitoff = 0; auto node_bitoff = node->FirstChildElement("BitOffs"); if (node_bitoff) { - bitoff = atoi(node->FirstChildElement("BitOffs")->GetText()); + bitoff = toNumber(node_bitoff); } return {it->second, bitlen, bitoff}; @@ -385,9 +384,14 @@ namespace kickcat::CoE { // array entries are the subindex starting from 1, 0 is the array size uint8_t elements = toNumber(node_array_info->FirstChildElement("Elements")); - for (uint8_t i = 0; i < elements; ++i) + uint16_t element_bitlen = toNumber(node_subitem->FirstChildElement("BitSize")); + uint16_t element_bitoff = toNumber(node_subitem->FirstChildElement("BitOffs")); + + for (uint8_t i = 1; i <= elements; ++i) { - entry.subindex = i + 1; + entry.bitlen = element_bitlen; + entry.bitoff = element_bitoff * i; + entry.subindex = i; object.entries.push_back(std::move(entry)); } } diff --git a/lib/src/CoE/OD.cc b/lib/src/CoE/OD.cc index 0cea2bf1..6f0c7015 100644 --- a/lib/src/CoE/OD.cc +++ b/lib/src/CoE/OD.cc @@ -156,6 +156,44 @@ namespace kickcat::CoE return result.str(); } + std::string toString(Entry const& entry) + { + std::stringstream result; + + result << " * Subindex " << (int)entry.subindex << '\n'; + result << " Desc: " << entry.description << '\n'; + result << " Type: " << toString(entry.type) << '\n'; + result << " BitLen: " << entry.bitlen << '\n'; + result << " BitOff: " << entry.bitoff << '\n'; + result << " Access: " << Access::toString(entry.access) << '\n'; + + result << " Data: "; + if (entry.data == nullptr) + { + result << "nullptr\n"; + } + else + { + switch (entry.type) + { + case DataType::BYTE: { result << (int)*static_cast (entry.data); break; } + case DataType::INTEGER8: { result << (int)*static_cast (entry.data); break; } + case DataType::UNSIGNED8: { result << (int)*static_cast (entry.data); break; } + case DataType::INTEGER16: { result << *static_cast (entry.data); break; } + case DataType::UNSIGNED16: { result << *static_cast(entry.data); break; } + case DataType::INTEGER32: { result << *static_cast (entry.data); break; } + case DataType::UNSIGNED32: { result << *static_cast(entry.data); break; } + case DataType::INTEGER64: { result << *static_cast (entry.data); break; } + case DataType::UNSIGNED64: { result << *static_cast(entry.data); break; } + case DataType::REAL64: { result << *static_cast (entry.data); break; } + case DataType::REAL32: { result << *static_cast (entry.data); break; } + default: { result << "no rendered"; } + } + } + + return result.str(); + } + std::string toString(Object const& object) { @@ -168,37 +206,7 @@ namespace kickcat::CoE for (auto const& entry : object.entries) { - result << " * Subindex " << (int)entry.subindex << '\n'; - result << " Desc: " << entry.description << '\n'; - result << " Type: " << toString(entry.type) << '\n'; - result << " BitLen: " << entry.bitlen << '\n'; - result << " BitOff: " << entry.bitoff << '\n'; - result << " Access: " << Access::toString(entry.access) << '\n'; - - result << " Data: "; - if (entry.data == nullptr) - { - result << "nullptr\n"; - } - else - { - switch (entry.type) - { - case DataType::BYTE: { result << (int)*static_cast (entry.data); break; } - case DataType::INTEGER8: { result << (int)*static_cast (entry.data); break; } - case DataType::UNSIGNED8: { result << (int)*static_cast (entry.data); break; } - case DataType::INTEGER16: { result << *static_cast (entry.data); break; } - case DataType::UNSIGNED16: { result << *static_cast(entry.data); break; } - case DataType::INTEGER32: { result << *static_cast (entry.data); break; } - case DataType::UNSIGNED32: { result << *static_cast(entry.data); break; } - case DataType::INTEGER64: { result << *static_cast (entry.data); break; } - case DataType::UNSIGNED64: { result << *static_cast(entry.data); break; } - case DataType::REAL64: { result << *static_cast (entry.data); break; } - case DataType::REAL32: { result << *static_cast (entry.data); break; } - default: { result << "no rendered"; } - } - } - result << '\n'; + result << toString(entry) << "\n"; } return result.str(); diff --git a/lib/src/CoE/mailbox/response.cc b/lib/src/CoE/mailbox/response.cc index aa443b1a..a6457dbf 100644 --- a/lib/src/CoE/mailbox/response.cc +++ b/lib/src/CoE/mailbox/response.cc @@ -502,9 +502,9 @@ namespace kickcat::mailbox::response desc->data_type = entry->type; auto name = pointData(desc); - std::memcpy(name, object->name.data(), object->name.size()); + std::memcpy(name, entry->description.data(), entry->description.size()); - header_->len += sizeof(CoE::SDO::information::EntryDescription) + object->name.size(); + header_->len += sizeof(CoE::SDO::information::EntryDescription) + entry->description.size(); reply(std::move(data_)); return ProcessingResult::FINALIZE; diff --git a/tools/od_generator.cc b/tools/od_generator.cc index d3bc822a..7de22e88 100644 --- a/tools/od_generator.cc +++ b/tools/od_generator.cc @@ -10,6 +10,28 @@ namespace kickcat { constexpr std::string_view OD_POPULATOR_FILE{"od_populator.cc"}; + char const* toStdType(CoE::DataType dataType) + { + switch (dataType) + { + case CoE::DataType::UNSIGNED8: { return "uint8_t"; } + case CoE::DataType::UNSIGNED16: { return "uint16_t"; } + case CoE::DataType::UNSIGNED32: { return "uint32_t"; } + case CoE::DataType::UNSIGNED64: { return "uint64_t"; } + case CoE::DataType::INTEGER8: { return "int8_t"; } + case CoE::DataType::INTEGER16: { return "int16_t"; } + case CoE::DataType::INTEGER32: { return "int32_t"; } + case CoE::DataType::INTEGER64: { return "int64_t"; } + case CoE::DataType::VISIBLE_STRING: { return "char const*"; } + default: + { + std::string what = "Unsupported type "; + what += toString(dataType); + throw std::invalid_argument{what}; + } + } + } + CoE::Dictionary loadOD(std::string esiFileName) { CoE::EsiParser parser; @@ -49,9 +71,10 @@ namespace kickcat } std::stringstream result; - result << " CoE::addEntry(object,"; + result << " CoE::addEntry<" << toStdType(entryToAdd.type) << ">(object,"; result << std::to_string(entryToAdd.subindex) << ","; result << std::to_string(entryToAdd.bitlen) << ","; + result << std::to_string(entryToAdd.bitoff) << ","; result << std::to_string(entryToAdd.access) << ","; result << "static_cast(" << std::to_string(static_cast(entryToAdd.type)) << "),"; result << "\"" << entryToAdd.description << "\",";