From 96e69cbb8c57927b74b0de47458f5cc30afde9d0 Mon Sep 17 00:00:00 2001 From: Philippe Leduc Date: Wed, 20 May 2026 16:16:09 +0200 Subject: [PATCH] ESI parser compliance - step 2 (#387) Parse // with full attributes and drive 0x1C00 synthesis from the parsed list. Move enums and packed register structs into kickcat::SyncManager:: and kickcat::fmmu:: sub-namespaces with throwing fromString. --- lib/include/kickcat/ESI/Device.h | 59 +++++++-- lib/include/kickcat/ESI/Parser.h | 8 +- lib/include/kickcat/Mailbox.h | 2 +- lib/include/kickcat/protocol.h | 109 ++++++++++------- lib/master/src/Bus.cc | 30 ++--- lib/master/src/Prints.cc | 2 +- lib/slave/include/kickcat/AbstractESC.h | 12 +- lib/slave/include/kickcat/ESC/EmulatedESC.h | 6 +- lib/slave/src/AbstractESC.cc | 18 +-- lib/slave/src/PDO.cc | 16 +-- lib/src/ESI/Parser.cc | 118 ++++++++++++++---- lib/src/Mailbox.cc | 14 +-- lib/src/protocol.cc | 60 +++++++-- unit/CMakeLists.txt | 1 + unit/kickcat_esi_test_sm_fmmu.xml | 52 ++++++++ unit/mocks/ESMStateTest.h | 20 +-- unit/src/ESI/Parser-t.cc | 128 ++++++++++++++++++++ unit/src/EmulatedESC-t.cc | 8 +- unit/src/bus-t.cc | 2 +- unit/src/mailbox/request-t.cc | 2 +- unit/src/mailbox/response-t.cc | 64 +++++----- unit/src/protocol-t.cc | 2 +- unit/src/slave/PDO-t.cc | 50 ++++---- unit/src/slave/slave-t.cc | 30 ++--- 24 files changed, 584 insertions(+), 229 deletions(-) create mode 100644 unit/kickcat_esi_test_sm_fmmu.xml diff --git a/lib/include/kickcat/ESI/Device.h b/lib/include/kickcat/ESI/Device.h index 07d9a676..c3333183 100644 --- a/lib/include/kickcat/ESI/Device.h +++ b/lib/include/kickcat/ESI/Device.h @@ -8,9 +8,40 @@ #include #include "kickcat/CoE/OD.h" +#include "kickcat/protocol.h" namespace kickcat::ESI { + struct SyncManager + { + kickcat::SyncManager::Type type = kickcat::SyncManager::Unused; + uint16_t min_size = 0; + uint16_t max_size = 0; + uint16_t default_size = 0; + uint16_t start_address = 0; + uint8_t control_byte = 0; + uint8_t enable = 0; + bool is_virtual = false; + bool op_only = false; + }; + + struct SyncUnit + { + bool separate_su = false; + bool separate_frame = false; + bool frame_repeat_support = false; + }; + + struct Fmmu + { + fmmu::Type type = fmmu::Unused; + // -1 when the attribute is absent. ESI Sm/Su attributes index into + // the device's / declaration order. + int sm = -1; + int su = -1; + bool op_only = false; + }; + struct DeviceSummary { std::string type; @@ -30,18 +61,22 @@ namespace kickcat::ESI struct Device { - std::string type; - uint32_t product_code = 0; - uint32_t revision_no = 0; - uint32_t serial_no = 0; - std::string name; - std::string group_type; - uint16_t profile_no = 0; - - std::string vendor_name; - uint32_t vendor_id = 0; - - CoE::Dictionary dictionary; + std::string type; + uint32_t product_code = 0; + uint32_t revision_no = 0; + uint32_t serial_no = 0; + std::string name; + std::string group_type; + uint16_t profile_no = 0; + + std::string vendor_name; + uint32_t vendor_id = 0; + + std::vector sync_managers; + std::vector sync_units; + std::vector fmmus; + + CoE::Dictionary dictionary; }; } diff --git a/lib/include/kickcat/ESI/Parser.h b/lib/include/kickcat/ESI/Parser.h index d67024a6..f3edb025 100644 --- a/lib/include/kickcat/ESI/Parser.h +++ b/lib/include/kickcat/ESI/Parser.h @@ -43,7 +43,12 @@ namespace kickcat::ESI tinyxml2::XMLElement* selectDevice(DeviceFilter const& filter); DeviceSummary summarize (tinyxml2::XMLElement* device); - CoE::Dictionary buildDictionary(tinyxml2::XMLElement* device, tinyxml2::XMLElement* profile); + void parseSyncManagers(tinyxml2::XMLElement* device, std::vector& out); + void parseSyncUnits (tinyxml2::XMLElement* device, std::vector& out); + void parseFmmus (tinyxml2::XMLElement* device, std::vector& out); + + CoE::Dictionary buildDictionary(tinyxml2::XMLElement* profile, + std::vector const& sms); std::vector loadHexBinary(tinyxml2::XMLElement* node); std::vector loadStringData(tinyxml2::XMLElement* node); @@ -70,7 +75,6 @@ namespace kickcat::ESI std::string profile_no_; static const std::unordered_map BASIC_TYPES; - static const std::unordered_map SM_CONF; }; } diff --git a/lib/include/kickcat/Mailbox.h b/lib/include/kickcat/Mailbox.h index 0ada5b28..335cc381 100644 --- a/lib/include/kickcat/Mailbox.h +++ b/lib/include/kickcat/Mailbox.h @@ -121,7 +121,7 @@ namespace kickcat::mailbox::request bool toggle; // for SDO segmented transfer // - void generateSMConfig(SyncManager SM[2]); + void generateSMConfig(SyncManager::Register SM[2]); // messages factory std::shared_ptr createSDO(uint16_t index, uint8_t subindex, bool CA, uint8_t request, void* data, uint32_t* data_size, nanoseconds timeout = 20ms); diff --git a/lib/include/kickcat/protocol.h b/lib/include/kickcat/protocol.h index ae20c477..eca35abd 100644 --- a/lib/include/kickcat/protocol.h +++ b/lib/include/kickcat/protocol.h @@ -384,34 +384,35 @@ namespace kickcat std::string toString(ErrorCounters const& counters); - struct SyncManager + namespace SyncManager { - uint16_t start_address; - uint16_t length; - uint8_t control; - uint8_t status; - uint8_t activate; - uint8_t pdi_control; - } __attribute__((__packed__)); + struct Register + { + uint16_t start_address; + uint16_t length; + uint8_t control; + uint8_t status; + uint8_t activate; + uint8_t pdi_control; + } __attribute__((__packed__)); + } - constexpr uint8_t SM_CONTROL_MODE_MASK = 0x03; - constexpr uint8_t SM_CONTROL_MODE_BUFFERED = 0x00; - constexpr uint8_t SM_CONTROL_MODE_MAILBOX = 0x02; - constexpr uint8_t SM_CONTROL_DIRECTION_MASK = 0x0C; - constexpr uint8_t SM_CONTROL_DIRECTION_READ = 0x00; // ECAT read access, PDI write access - constexpr uint8_t SM_CONTROL_DIRECTION_WRITE = 0x04; // ECAT write access, PDI read access - constexpr uint8_t SM_CONTROL_INTERRUPT_ECAT_MASK = 0x10; - constexpr uint8_t SM_CONTROL_INTERRUPT_ECAT_DISABLED= 0x00; - constexpr uint8_t SM_CONTROL_INTERRUPT_ECAT_ENABLED = 0x10; - constexpr uint8_t SM_CONTROL_INTERRUPT_AL_MASK = 0x20; - constexpr uint8_t SM_CONTROL_INTERRUPT_AL_DISABLED = 0x00; - constexpr uint8_t SM_CONTROL_INTERRUPT_AL_ENABLED = 0x20; - constexpr uint8_t SM_CONTROL_WATCHDOG_MASK = 0x40; - constexpr uint8_t SM_CONTROL_WATCHDOG_DISABLED = 0x00; - constexpr uint8_t SM_CONTROL_WATCHDOG_ENABLED = 0x40; - constexpr uint8_t SYNC_MANAGER_CONTROL_OPERATION_MODE_MASK = 0x03; // bits [1:0] - constexpr uint8_t SYNC_MANAGER_CONTROL_DIRECTION_MASK = 0x0C; // bits [3:2] + constexpr uint8_t SM_CONTROL_OPERATION_MODE_MASK = 0x03; // bits [1:0] + constexpr uint8_t SM_CONTROL_MODE_BUFFERED = 0x00; + constexpr uint8_t SM_CONTROL_MODE_MAILBOX = 0x02; + constexpr uint8_t SM_CONTROL_DIRECTION_MASK = 0x0C; // bits [3:2] + constexpr uint8_t SM_CONTROL_DIRECTION_READ = 0x00; // ECAT read access, PDI write access + constexpr uint8_t SM_CONTROL_DIRECTION_WRITE = 0x04; // ECAT write access, PDI read access + constexpr uint8_t SM_CONTROL_INTERRUPT_ECAT_MASK = 0x10; + constexpr uint8_t SM_CONTROL_INTERRUPT_ECAT_DISABLED = 0x00; + constexpr uint8_t SM_CONTROL_INTERRUPT_ECAT_ENABLED = 0x10; + constexpr uint8_t SM_CONTROL_INTERRUPT_AL_MASK = 0x20; + constexpr uint8_t SM_CONTROL_INTERRUPT_AL_DISABLED = 0x00; + constexpr uint8_t SM_CONTROL_INTERRUPT_AL_ENABLED = 0x20; + constexpr uint8_t SM_CONTROL_WATCHDOG_MASK = 0x40; + constexpr uint8_t SM_CONTROL_WATCHDOG_DISABLED = 0x00; + constexpr uint8_t SM_CONTROL_WATCHDOG_ENABLED = 0x40; constexpr uint8_t SM_ACTIVATE_ENABLE = (1 << 0); constexpr uint8_t SM_ACTIVATE_REPEAT_REQ = (1 << 1); @@ -422,29 +423,47 @@ namespace kickcat constexpr uint8_t SM_STATUS_IRQ_READ = (1 << 1); constexpr uint8_t SM_STATUS_MAILBOX = (1 << 3); - enum SyncManagerType + namespace SyncManager { - Unused = 0, - MailboxOut = 1, - MailboxIn = 2, - Output = 3, - Input = 4 // slave to master - }; - char const* toString(SyncManagerType const& type); - constexpr uint16_t addressSM(uint8_t index) { return static_cast(reg::SYNC_MANAGER + index * sizeof(SyncManager)); }; + enum Type : uint8_t + { + Unused = 0, + MailboxOut = 1, + MailboxIn = 2, + Output = 3, + Input = 4 // slave to master + }; + char const* toString(Type const& type); + void fromString(std::string_view text, Type& out); + } - struct FMMU + namespace fmmu { - uint32_t logical_address; - uint16_t length; - uint8_t logical_start_bit; - uint8_t logical_stop_bit; - uint16_t physical_address; - uint8_t physical_start_bit; - uint8_t type; - uint8_t activate; - uint8_t reserved[3]; - } __attribute__((__packed__)); + enum Type : uint8_t + { + Unused = 0, + Outputs = 1, + Inputs = 2, + MBoxState = 3, + }; + char const* toString(Type const& type); + void fromString(std::string_view text, Type& out); + + struct Register + { + uint32_t logical_address; + uint16_t length; + uint8_t logical_start_bit; + uint8_t logical_stop_bit; + uint16_t physical_address; + uint8_t physical_start_bit; + uint8_t type; + uint8_t activate; + uint8_t reserved[3]; + } __attribute__((__packed__)); + } + + constexpr uint16_t addressSM(uint8_t index) { return static_cast(reg::SYNC_MANAGER + index * sizeof(SyncManager::Register)); }; namespace eeprom // addresses are in words! { diff --git a/lib/master/src/Bus.cc b/lib/master/src/Bus.cc index 2a6bccc5..301aa3ed 100644 --- a/lib/master/src/Bus.cc +++ b/lib/master/src/Bus.cc @@ -363,7 +363,7 @@ namespace kickcat { if (slave.sii.info.mailbox_protocol) { - SyncManager SM[2]; + SyncManager::Register SM[2]; slave.mailbox.generateSMConfig(SM); link_->addDatagram(Command::FPWR, createAddress(slave.address, reg::SYNC_MANAGER), SM, process, error); } @@ -412,7 +412,7 @@ namespace kickcat } Slave::PIMapping* mapping = &slave.input; - if (sm[i] == SyncManagerType::Output) + if (sm[i] == SyncManager::Output) { mapping = &slave.output; } @@ -441,7 +441,7 @@ namespace kickcat else { // unsupported mailbox: use SII to get the mapping size - auto siiMapping = [&](Slave::PIMapping* mapping, std::vector const& PDOs, SyncManagerType type) + auto siiMapping = [&](Slave::PIMapping* mapping, std::vector const& PDOs, SyncManager::Type type) { mapping->sync_manager = -1; mapping->size = 0; @@ -467,8 +467,8 @@ namespace kickcat } }; - siiMapping(&slave.output, slave.sii.RxPDO, SyncManagerType::Output); - siiMapping(&slave.input, slave.sii.TxPDO, SyncManagerType::Input); + siiMapping(&slave.output, slave.sii.RxPDO, SyncManager::Output); + siiMapping(&slave.input, slave.sii.TxPDO, SyncManager::Input); } } } @@ -804,7 +804,7 @@ namespace kickcat void Bus::configureFMMUs() { - auto prepareDatagrams = [this](Slave& slave, Slave::PIMapping& mapping, SyncManagerType type) + auto prepareDatagrams = [this](Slave& slave, Slave::PIMapping& mapping, SyncManager::Type type) { if (mapping.bsize == 0) @@ -830,15 +830,15 @@ namespace kickcat // Get SyncManager configuration from SII auto& sii_sm = slave.sii.syncManagers[mapping.sync_manager]; - SyncManager sm; - FMMU fmmu; - std::memset(&sm, 0, sizeof(SyncManager)); - std::memset(&fmmu, 0, sizeof(FMMU)); + SyncManager::Register sm; + fmmu::Register fmmu; + std::memset(&sm, 0, sizeof(SyncManager::Register)); + std::memset(&fmmu, 0, sizeof(fmmu::Register)); uint16_t targeted_fmmu = reg::FMMU; // FMMU0 - outputs sm.control = 0x64; // 3 buffers - write acces - PDI IRQ ON - Watchdog trigger fmmu.type = 2; // write access - if (type == SyncManagerType::Input) + if (type == SyncManager::Input) { sm.control = 0x20; // 3 buffers - read acces - PDI IRQ ON fmmu.type = 1; // read access @@ -868,8 +868,8 @@ namespace kickcat for (auto& slave : slaves_) { - prepareDatagrams(slave, slave.input, SyncManagerType::Input); - prepareDatagrams(slave, slave.output, SyncManagerType::Output); + prepareDatagrams(slave, slave.input, SyncManager::Input); + prepareDatagrams(slave, slave.output, SyncManager::Output); } link_->processDatagrams(); @@ -898,8 +898,8 @@ namespace kickcat { for (auto const& entry : entries) { - FMMU fmmu; - std::memset(&fmmu, 0, sizeof(FMMU)); + fmmu::Register fmmu; + std::memset(&fmmu, 0, sizeof(fmmu::Register)); fmmu.logical_address = frame.address + entry.byte_offset; fmmu.length = 1; fmmu.logical_start_bit = entry.bit_position; diff --git a/lib/master/src/Prints.cc b/lib/master/src/Prints.cc index 16cee1d4..8a06d175 100644 --- a/lib/master/src/Prints.cc +++ b/lib/master/src/Prints.cc @@ -69,7 +69,7 @@ namespace kickcat os << "SM[" << std::dec << i << "] config\n"; os << " physical address: " << "0x" << std::hex << sm.start_address << "\n"; os << " length: " << std::dec << sm.length << "\n"; - os << " type: " << std::dec << toString(static_cast(sm.type)) << "\n"; + os << " type: " << std::dec << toString(static_cast(sm.type)) << "\n"; os << " control: " << std::hex << (int)sm.control_register << "\n"; } diff --git a/lib/slave/include/kickcat/AbstractESC.h b/lib/slave/include/kickcat/AbstractESC.h index 399a98aa..a99197f1 100644 --- a/lib/slave/include/kickcat/AbstractESC.h +++ b/lib/slave/include/kickcat/AbstractESC.h @@ -16,20 +16,20 @@ namespace kickcat uint16_t start_address; uint16_t length; uint8_t control; - SyncManagerType type; + SyncManager::Type type; }; constexpr SyncManagerConfig SYNC_MANAGER_PI_IN(uint8_t index, uint16_t address, uint16_t length) - { return {index, address, length, 0x20, SyncManagerType::Input}; } + { return {index, address, length, 0x20, SyncManager::Input}; } constexpr SyncManagerConfig SYNC_MANAGER_PI_OUT(uint8_t index, uint16_t address, uint16_t length) - { return {index, address, length, 0x64, SyncManagerType::Output}; } + { return {index, address, length, 0x64, SyncManager::Output}; } constexpr SyncManagerConfig SYNC_MANAGER_MBX_IN(uint8_t index, uint16_t address, uint16_t length) - { return {index, address, length, 0x02, SyncManagerType::MailboxIn}; } + { return {index, address, length, 0x02, SyncManager::MailboxIn}; } constexpr SyncManagerConfig SYNC_MANAGER_MBX_OUT(uint8_t index, uint16_t address, uint16_t length) - { return {index, address, length, 0x06, SyncManagerType::MailboxOut}; } + { return {index, address, length, 0x06, SyncManager::MailboxOut}; } namespace mailbox::response @@ -59,7 +59,7 @@ namespace kickcat /// \return Number of bytes written or a negative errno code otherwise virtual int32_t write(uint16_t address, void const* data, uint16_t size) = 0; - std::tuple findSm(uint16_t controlMode); + std::tuple findSm(uint16_t controlMode); void activateSm(SyncManagerConfig const& sm); void deactivateSm(SyncManagerConfig const& sm); diff --git a/lib/slave/include/kickcat/ESC/EmulatedESC.h b/lib/slave/include/kickcat/ESC/EmulatedESC.h index d3afe6d1..ff352856 100644 --- a/lib/slave/include/kickcat/ESC/EmulatedESC.h +++ b/lib/slave/include/kickcat/ESC/EmulatedESC.h @@ -133,10 +133,10 @@ namespace kickcat uint32_t phy_port_status; uint8_t padding23[228]; - FMMU fmmu[16]; + fmmu::Register fmmu[16]; uint8_t padding24[0x100]; - SyncManager sync_manager[16]; + SyncManager::Register sync_manager[16]; uint8_t padding25[128]; // DC @@ -165,7 +165,7 @@ namespace kickcat uint8_t access; uint16_t address; uint16_t size; - SyncManager* registers; + SyncManager::Register* registers; }; std::vector syncs_; diff --git a/lib/slave/src/AbstractESC.cc b/lib/slave/src/AbstractESC.cc index 6771d457..4287dbe0 100644 --- a/lib/slave/src/AbstractESC.cc +++ b/lib/slave/src/AbstractESC.cc @@ -5,12 +5,12 @@ namespace kickcat { - std::tuple AbstractESC::findSm(uint16_t controlMode) + std::tuple AbstractESC::findSm(uint16_t controlMode) { for (uint8_t i = 0; i < reg::SM_STATS; i++) { - SyncManager sync{}; - read(reg::SYNC_MANAGER + sizeof(SyncManager) * i, &sync, sizeof(SyncManager)); + SyncManager::Register sync{}; + read(reg::SYNC_MANAGER + sizeof(SyncManager::Register) * i, &sync, sizeof(SyncManager::Register)); if ((sync.control & 0x0F) == (controlMode & 0x0F)) { return std::tuple(i, sync); @@ -22,19 +22,19 @@ namespace kickcat bool AbstractESC::isSmValid(SyncManagerConfig const& sm_ref) { - SyncManager sm_read; + SyncManager::Register sm_read; read(addressSM(sm_ref.index), &sm_read, sizeof(sm_read)); bool is_valid = (sm_read.start_address == sm_ref.start_address) and (sm_read.length == sm_ref.length) - and ((sm_read.control & SYNC_MANAGER_CONTROL_OPERATION_MODE_MASK) - == (sm_ref.control & SYNC_MANAGER_CONTROL_OPERATION_MODE_MASK)) - and ((sm_read.control & SYNC_MANAGER_CONTROL_DIRECTION_MASK) - == (sm_ref.control & SYNC_MANAGER_CONTROL_DIRECTION_MASK)) + and ((sm_read.control & SM_CONTROL_OPERATION_MODE_MASK) + == (sm_ref.control & SM_CONTROL_OPERATION_MODE_MASK)) + and ((sm_read.control & SM_CONTROL_DIRECTION_MASK) + == (sm_ref.control & SM_CONTROL_DIRECTION_MASK)) and (sm_read.activate & SM_ACTIVATE_ENABLE); slave_info("SM read %i: start address %x, length %u, control %x, status %x, activate %x \n", sm_ref.index, sm_read.start_address, sm_read.length, sm_read.control, sm_read.status, sm_read.activate); slave_info("SM config %i: start address %x, length %u, control %x \n", sm_ref.index, sm_ref.start_address, sm_ref.length, sm_ref.control); - + return is_valid; } diff --git a/lib/slave/src/PDO.cc b/lib/slave/src/PDO.cc index f288654e..2186ed4b 100644 --- a/lib/slave/src/PDO.cc +++ b/lib/slave/src/PDO.cc @@ -19,7 +19,7 @@ namespace kickcat } else { - sm_input_ = SyncManagerConfig{indexIn, 0, 0, 0, SyncManagerType::Unused}; + sm_input_ = SyncManagerConfig{indexIn, 0, 0, 0, SyncManager::Unused}; } if(pdoOut.length > 0) @@ -28,7 +28,7 @@ namespace kickcat } else { - sm_output_ = SyncManagerConfig{indexOut, 0, 0, 0, SyncManagerType::Unused}; + sm_output_ = SyncManagerConfig{indexOut, 0, 0, 0, SyncManager::Unused}; } } catch (std::exception const& e) @@ -41,11 +41,11 @@ namespace kickcat StatusCode PDO::isConfigOk() { - if (sm_input_.type != SyncManagerType::Unused and not esc_->isSmValid(sm_input_)) + if (sm_input_.type != SyncManager::Unused and not esc_->isSmValid(sm_input_)) { return StatusCode::INVALID_INPUT_CONFIGURATION; } - if (sm_output_.type != SyncManagerType::Unused and not esc_->isSmValid(sm_output_)) + if (sm_output_.type != SyncManager::Unused and not esc_->isSmValid(sm_output_)) { return StatusCode::INVALID_OUTPUT_CONFIGURATION; } @@ -55,7 +55,7 @@ namespace kickcat void PDO::activateOutput(bool is_activated) { - if (sm_output_.type != SyncManagerType::Unused) + if (sm_output_.type != SyncManager::Unused) { esc_->setSmActivate({sm_output_}, is_activated); } @@ -63,7 +63,7 @@ namespace kickcat void PDO::activateInput(bool is_activated) { - if (sm_input_.type != SyncManagerType::Unused) + if (sm_input_.type != SyncManager::Unused) { esc_->setSmActivate({sm_input_}, is_activated); } @@ -83,7 +83,7 @@ namespace kickcat void PDO::updateInput() { - if (input_ == nullptr or sm_input_.type == SyncManagerType::Unused) + if (input_ == nullptr or sm_input_.type == SyncManager::Unused) { return; } @@ -98,7 +98,7 @@ namespace kickcat void PDO::updateOutput() { - if (output_ == nullptr or sm_output_.type == SyncManagerType::Unused) + if (output_ == nullptr or sm_output_.type == SyncManager::Unused) { return; } diff --git a/lib/src/ESI/Parser.cc b/lib/src/ESI/Parser.cc index 9d277920..bd8d10b4 100644 --- a/lib/src/ESI/Parser.cc +++ b/lib/src/ESI/Parser.cc @@ -44,14 +44,6 @@ const std::unordered_map Parser::BASIC_TYPES {"BIT8", CoE::DataType::BIT8 }, }; -const std::unordered_map Parser::SM_CONF -{ - {"MBoxOut", 1}, - {"MBoxIn", 2}, - {"Outputs", 3}, - {"Inputs", 4}, -}; - namespace { XMLElement* requireChild(XMLNode* node, char const* name) @@ -161,6 +153,21 @@ namespace std::snprintf(buf, sizeof(buf), "Object 0x%04x", index); return buf; } + + // xs:boolean per the ESI schema: accepts "true"/"false" and "1"/"0". + bool readBoolAttr(XMLElement* node, char const* name) + { + if (node == nullptr) + { + return false; + } + char const* raw = node->Attribute(name); + if (raw == nullptr) + { + return false; + } + return std::strcmp(raw, "true") == 0 or std::strcmp(raw, "1") == 0; + } } std::optional Parser::readHexDecAttr(XMLElement* node, char const* name) @@ -336,11 +343,78 @@ Device Parser::loadDeviceImpl(DeviceFilter const& filter) device.profile_no = static_cast(parseHexDec(profile_no_)); } - device.dictionary = buildDictionary(device_node, profile_node); + parseSyncManagers(device_node, device.sync_managers); + parseSyncUnits (device_node, device.sync_units); + parseFmmus (device_node, device.fmmus); + + device.dictionary = buildDictionary(profile_node, device.sync_managers); return device; } -CoE::Dictionary Parser::buildDictionary(XMLElement* device, XMLElement* profile) +void Parser::parseSyncManagers(XMLElement* device, std::vector& out) +{ + for (auto* sm = device->FirstChildElement("Sm"); sm != nullptr; sm = sm->NextSiblingElement("Sm")) + { + SyncManager entry; + + char const* text = sm->GetText(); + if (text != nullptr) + { + fromString(text, entry.type); + } + + entry.min_size = static_cast(readHexDecAttr(sm, "MinSize" ).value_or(0)); + entry.max_size = static_cast(readHexDecAttr(sm, "MaxSize" ).value_or(0)); + entry.default_size = static_cast(readHexDecAttr(sm, "DefaultSize" ).value_or(0)); + entry.start_address = static_cast(readHexDecAttr(sm, "StartAddress").value_or(0)); + entry.control_byte = static_cast (readHexDecAttr(sm, "ControlByte" ).value_or(0)); + entry.enable = static_cast (readHexDecAttr(sm, "Enable" ).value_or(0)); + entry.is_virtual = readBoolAttr(sm, "Virtual"); + entry.op_only = readBoolAttr(sm, "OpOnly"); + + out.push_back(entry); + } +} + +void Parser::parseSyncUnits(XMLElement* device, std::vector& out) +{ + for (auto* su = device->FirstChildElement("Su"); su != nullptr; su = su->NextSiblingElement("Su")) + { + SyncUnit entry; + entry.separate_su = readBoolAttr(su, "SeparateSu"); + entry.separate_frame = readBoolAttr(su, "SeparateFrame"); + entry.frame_repeat_support = readBoolAttr(su, "FrameRepeatSupport"); + out.push_back(entry); + } +} + +void Parser::parseFmmus(XMLElement* device, std::vector& out) +{ + for (auto* fmmu = device->FirstChildElement("Fmmu"); fmmu != nullptr; fmmu = fmmu->NextSiblingElement("Fmmu")) + { + Fmmu entry; + + char const* text = fmmu->GetText(); + if (text != nullptr) + { + fromString(text, entry.type); + } + + if (auto sm_attr = readHexDecAttr(fmmu, "Sm")) + { + entry.sm = static_cast(*sm_attr); + } + if (auto su_attr = readHexDecAttr(fmmu, "Su")) + { + entry.su = static_cast(*su_attr); + } + entry.op_only = readBoolAttr(fmmu, "OpOnly"); + + out.push_back(entry); + } +} + +CoE::Dictionary Parser::buildDictionary(XMLElement* profile, std::vector const& sms) { auto* dictionary = requireChild(profile, "Dictionary"); dtypes_ = requireChild(dictionary, "DataTypes"); @@ -353,37 +427,35 @@ CoE::Dictionary Parser::buildDictionary(XMLElement* device, XMLElement* profile) out.push_back(createObject(node_object)); } + // Synthesize CoE object 0x1C00 (Sync Manager Communication Type) from + // the device's declarations so legacy callers of loadFile/loadString + // still get an SM-type array in their CoE::Dictionary. CoE::Object sms_type; sms_type.index = 0x1c00; sms_type.code = CoE::ObjectCode::ARRAY; sms_type.name = "Sync manager type"; sms_type.entries.push_back(CoE::Entry{0, 8, 0, CoE::Access::READ, CoE::DataType::UNSIGNED8, "Subindex 0"}); - for (auto* sm = device->FirstChildElement("Sm"); sm != nullptr; sm = sm->NextSiblingElement("Sm")) + for (std::size_t i = 0; i < sms.size(); ++i) { CoE::Entry entry; - entry.subindex = static_cast(sms_type.entries.size()); + entry.subindex = static_cast(i + 1); entry.access = CoE::Access::READ; entry.bitlen = 8; - entry.bitoff = static_cast(sms_type.entries.size() * 8 + 8); - entry.description = "Subindex " + std::to_string(sms_type.entries.size()); + entry.bitoff = static_cast((i + 1) * 8); + entry.description = "Subindex " + std::to_string(i + 1); entry.type = CoE::DataType::UNSIGNED8; entry.data = std::malloc(1); - char const* sm_text = textOrEmpty(sm); - auto it = SM_CONF.find(sm_text); - uint8_t sm_type = 0; - if (it != SM_CONF.end()) - { - sm_type = it->second; - } - std::memcpy(entry.data, &sm_type, 1); + uint8_t type = static_cast(sms[i].type); + std::memcpy(entry.data, &type, 1); sms_type.entries.push_back(std::move(entry)); } + auto& subindex0 = sms_type.entries.at(0); subindex0.data = std::malloc(1); - uint8_t array_size = static_cast(sms_type.entries.size() - 1); + uint8_t array_size = static_cast(sms.size()); std::memcpy(subindex0.data, &array_size, 1); out.push_back(std::move(sms_type)); diff --git a/lib/src/Mailbox.cc b/lib/src/Mailbox.cc index 81d8d4e8..07229428 100644 --- a/lib/src/Mailbox.cc +++ b/lib/src/Mailbox.cc @@ -31,7 +31,7 @@ namespace kickcat::mailbox::request } - void Mailbox::generateSMConfig(SyncManager SM[2]) + void Mailbox::generateSMConfig(SyncManager::Register SM[2]) { // 0 is mailbox out, 1 is mailbox in - cf. default EtherCAT configuration if slave support a mailbox // NOTE: mailbox out -> master to slave - mailbox in -> slave to master @@ -314,7 +314,7 @@ namespace kickcat::mailbox::response bool Mailbox::isConfigOk() { - if (mbx_in_.type == SyncManagerType::Unused or mbx_out_.type == SyncManagerType::Unused) + if (mbx_in_.type == SyncManager::Unused or mbx_out_.type == SyncManager::Unused) { return false; } @@ -329,7 +329,7 @@ namespace kickcat::mailbox::response void Mailbox::activate(bool is_activated) { - if (mbx_in_.type != SyncManagerType::Unused and mbx_out_.type != SyncManagerType::Unused ) + if (mbx_in_.type != SyncManager::Unused and mbx_out_.type != SyncManager::Unused ) { esc_->setSmActivate({mbx_in_, mbx_out_}, is_activated); } @@ -338,8 +338,8 @@ namespace kickcat::mailbox::response void Mailbox::receive() { - SyncManager sync; - esc_->read(addressSM(mbx_out_.index), &sync, sizeof(SyncManager)); + SyncManager::Register sync; + esc_->read(addressSM(mbx_out_.index), &sync, sizeof(SyncManager::Register)); if (not (sync.status & SM_STATUS_MAILBOX)) { return; @@ -481,8 +481,8 @@ namespace kickcat::mailbox::response void Mailbox::send() { - SyncManager sync; - esc_->read(addressSM(mbx_in_.index), &sync, sizeof(SyncManager)); + SyncManager::Register sync; + esc_->read(addressSM(mbx_in_.index), &sync, sizeof(SyncManager::Register)); // Save last fetched message for repeat procedure if (sync.status & SM_STATUS_IRQ_READ) diff --git a/lib/src/protocol.cc b/lib/src/protocol.cc index b0d91c5f..d1db5400 100644 --- a/lib/src/protocol.cc +++ b/lib/src/protocol.cc @@ -1,6 +1,7 @@ #include "protocol.h" #include "Error.h" #include +#include namespace kickcat { @@ -218,16 +219,59 @@ namespace kickcat } - char const* toString(SyncManagerType const& type) + namespace SyncManager { - switch (type) + char const* toString(Type const& type) { - case SyncManagerType::Unused: { return "Unused"; } - case SyncManagerType::MailboxOut: { return "MailboxOut"; } - case SyncManagerType::MailboxIn: { return "MailboxIn" ; } - case SyncManagerType::Output: { return "Output (Master to Slave)"; } - case SyncManagerType::Input: { return "Input (Slave to Master)"; } - default: { return "unknown"; } + switch (type) + { + case Unused: { return "Unused"; } + case MailboxOut: { return "MailboxOut"; } + case MailboxIn: { return "MailboxIn" ; } + case Output: { return "Output (Master to Slave)"; } + case Input: { return "Input (Slave to Master)"; } + default: { return "unknown"; } + } + } + + void fromString(std::string_view text, Type& out) + { + if (text == "MBoxOut") { out = MailboxOut; return; } + if (text == "MBoxIn") { out = MailboxIn; return; } + if (text == "Outputs") { out = Output; return; } + if (text == "Inputs") { out = Input; return; } + + std::string what = "SyncManager::Type: unknown text '"; + what.append(text); + what += "'"; + throw std::invalid_argument(what); + } + } + + namespace fmmu + { + char const* toString(Type const& type) + { + switch (type) + { + case Unused: { return "Unused"; } + case Outputs: { return "Outputs"; } + case Inputs: { return "Inputs"; } + case MBoxState: { return "MBoxState"; } + default: { return "unknown"; } + } + } + + void fromString(std::string_view text, Type& out) + { + if (text == "Outputs") { out = Outputs; return; } + if (text == "Inputs") { out = Inputs; return; } + if (text == "MBoxState") { out = MBoxState; return; } + + std::string what = "fmmu::Type: unknown text '"; + what.append(text); + what += "'"; + throw std::invalid_argument(what); } } diff --git a/unit/CMakeLists.txt b/unit/CMakeLists.txt index 111409db..4dcbcc4d 100644 --- a/unit/CMakeLists.txt +++ b/unit/CMakeLists.txt @@ -42,6 +42,7 @@ file(COPY ${CMAKE_SOURCE_DIR}/examples/slave/nuttx/xmc4800/boards/wdc_foot/foot. file(COPY ${CMAKE_SOURCE_DIR}/unit/kickcat_esi_test_basic.xml DESTINATION ${CMAKE_BINARY_DIR}) file(COPY ${CMAKE_SOURCE_DIR}/unit/kickcat_esi_test_complex.xml DESTINATION ${CMAKE_BINARY_DIR}) file(COPY ${CMAKE_SOURCE_DIR}/unit/kickcat_esi_test_multi_device.xml DESTINATION ${CMAKE_BINARY_DIR}) +file(COPY ${CMAKE_SOURCE_DIR}/unit/kickcat_esi_test_sm_fmmu.xml DESTINATION ${CMAKE_BINARY_DIR}) target_link_libraries(kickcat_unit kickcat GTest::gmock_main) target_include_directories(kickcat_unit PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/unit/kickcat_esi_test_sm_fmmu.xml b/unit/kickcat_esi_test_sm_fmmu.xml new file mode 100644 index 00000000..779260e0 --- /dev/null +++ b/unit/kickcat_esi_test_sm_fmmu.xml @@ -0,0 +1,52 @@ + + + + #x0CAFE + KickCAT + + + + + KickCAT Test Devices + KickCAT Test Devices + + + + + kickcat_esi_test_sm_fmmu + KickCAT ESI Test Sm/Fmmu/Su + KickCAT Test Devices + + 5005 + + + + UDINT + 32 + + + + + #x1000 + Device Type + UDINT + 32 + + ro + + + + + + Outputs + Inputs + MBoxState + + MBoxOut + MBoxIn + Outputs + Inputs + + + + diff --git a/unit/mocks/ESMStateTest.h b/unit/mocks/ESMStateTest.h index 2cddbe1d..c67955bc 100644 --- a/unit/mocks/ESMStateTest.h +++ b/unit/mocks/ESMStateTest.h @@ -20,10 +20,10 @@ class ESMStateTest : public testing::Test uint8_t buffer_in_[1024]; uint8_t buffer_out_[1024]; - SyncManager mbx_in{0x00, 100, SM_CONTROL_MODE_MAILBOX | SM_CONTROL_DIRECTION_READ, 0x00, SM_ACTIVATE_ENABLE, 0x00}; - SyncManager mbx_out{0x01, 100, SM_CONTROL_MODE_MAILBOX | SM_CONTROL_DIRECTION_WRITE, 0x00, SM_ACTIVATE_ENABLE, 0x00}; - SyncManager pdo_in{0x02, 200, SM_CONTROL_MODE_BUFFERED | SM_CONTROL_DIRECTION_READ, 0x00, SM_ACTIVATE_ENABLE, 0x00}; - SyncManager pdo_out{0x03, 200, SM_CONTROL_MODE_BUFFERED | SM_CONTROL_DIRECTION_WRITE, 0x00, SM_ACTIVATE_ENABLE, 0x00}; + SyncManager::Register mbx_in{0x00, 100, SM_CONTROL_MODE_MAILBOX | SM_CONTROL_DIRECTION_READ, 0x00, SM_ACTIVATE_ENABLE, 0x00}; + SyncManager::Register mbx_out{0x01, 100, SM_CONTROL_MODE_MAILBOX | SM_CONTROL_DIRECTION_WRITE, 0x00, SM_ACTIVATE_ENABLE, 0x00}; + SyncManager::Register pdo_in{0x02, 200, SM_CONTROL_MODE_BUFFERED | SM_CONTROL_DIRECTION_READ, 0x00, SM_ACTIVATE_ENABLE, 0x00}; + SyncManager::Register pdo_out{0x03, 200, SM_CONTROL_MODE_BUFFERED | SM_CONTROL_DIRECTION_WRITE, 0x00, SM_ACTIVATE_ENABLE, 0x00}; Init init{esc_, pdo_}; PreOP preop{esc_, pdo_}; @@ -75,11 +75,11 @@ class ESMStateTest : public testing::Test EXPECT_EQ(context.al_status_code, statusCode); } - void expectSyncManagerRead(uint8_t index, SyncManager& syncManager) + void expectSyncManagerRead(uint8_t index, SyncManager::Register& syncManager) { - EXPECT_CALL(esc_, read(reg::SYNC_MANAGER + sizeof(SyncManager) * index, _, sizeof(SyncManager))) + EXPECT_CALL(esc_, read(reg::SYNC_MANAGER + sizeof(SyncManager::Register) * index, _, sizeof(SyncManager::Register))) .WillRepeatedly(DoAll( - testing::Invoke([&](uint16_t, void* ptr, uint16_t) { memcpy(ptr, &syncManager, sizeof(SyncManager)); }), + testing::Invoke([&](uint16_t, void* ptr, uint16_t) { memcpy(ptr, &syncManager, sizeof(SyncManager::Register)); }), Return(0))); } @@ -95,10 +95,10 @@ class ESMStateTest : public testing::Test void expectSyncManagerActivate(uint8_t index, bool enable = true) { - EXPECT_CALL(esc_, read(reg::SYNC_MANAGER + sizeof(SyncManager) * index + 7, _, sizeof(uint8_t))) + EXPECT_CALL(esc_, read(reg::SYNC_MANAGER + sizeof(SyncManager::Register) * index + 7, _, sizeof(uint8_t))) .WillRepeatedly(Return(0)); - EXPECT_CALL(esc_, write(reg::SYNC_MANAGER + sizeof(SyncManager) * index + 7, _, sizeof(uint8_t))) + EXPECT_CALL(esc_, write(reg::SYNC_MANAGER + sizeof(SyncManager::Register) * index + 7, _, sizeof(uint8_t))) .WillOnce(Return(0)); uint8_t pdi_control = 0; @@ -107,7 +107,7 @@ class ESMStateTest : public testing::Test pdi_control = 1; } - EXPECT_CALL(esc_, read(reg::SYNC_MANAGER + sizeof(SyncManager) * index + 7, _, 1)) + EXPECT_CALL(esc_, read(reg::SYNC_MANAGER + sizeof(SyncManager::Register) * index + 7, _, 1)) .WillRepeatedly(DoAll( testing::Invoke([=](uint16_t, void* ptr, uint16_t) { memcpy(ptr, &pdi_control, sizeof(uint8_t)); }), Return(0))); diff --git a/unit/src/ESI/Parser-t.cc b/unit/src/ESI/Parser-t.cc index 4d06cc96..1e8f5a81 100644 --- a/unit/src/ESI/Parser-t.cc +++ b/unit/src/ESI/Parser-t.cc @@ -581,6 +581,134 @@ TEST(ESIParser, throws_when_subitem_type_unresolved) } } +TEST(ESIParser, loadDevice_parses_sync_managers_with_attributes) +{ + ESI::Parser parser; + ESI::Device device = parser.loadDevice("kickcat_esi_test_sm_fmmu.xml"); + + ASSERT_EQ(device.sync_managers.size(), 4u); + + auto const& mbox_out = device.sync_managers[0]; + ASSERT_EQ(mbox_out.type, SyncManager::MailboxOut); + ASSERT_EQ(mbox_out.min_size, 40); + ASSERT_EQ(mbox_out.max_size, 1486); + ASSERT_EQ(mbox_out.default_size, 128); + ASSERT_EQ(mbox_out.start_address, 0x1000); + ASSERT_EQ(mbox_out.control_byte, 0x26); + ASSERT_EQ(mbox_out.enable, 1); + ASSERT_FALSE(mbox_out.is_virtual); + ASSERT_FALSE(mbox_out.op_only); + + auto const& mbox_in = device.sync_managers[1]; + ASSERT_EQ(mbox_in.type, SyncManager::MailboxIn); + ASSERT_EQ(mbox_in.start_address, 0x1400); + ASSERT_EQ(mbox_in.control_byte, 0x22); + ASSERT_TRUE(mbox_in.is_virtual); // Virtual="1" + ASSERT_FALSE(mbox_in.op_only); + + auto const& outputs = device.sync_managers[2]; + ASSERT_EQ(outputs.type, SyncManager::Output); + ASSERT_EQ(outputs.min_size, 0); // attribute absent + ASSERT_EQ(outputs.default_size, 40); + ASSERT_EQ(outputs.start_address, 0x1800); + ASSERT_EQ(outputs.control_byte, 0x64); + ASSERT_TRUE(outputs.op_only); // OpOnly="true" + + auto const& inputs = device.sync_managers[3]; + ASSERT_EQ(inputs.type, SyncManager::Input); + ASSERT_EQ(inputs.default_size, 22); + ASSERT_EQ(inputs.start_address, 0x1c00); +} + +TEST(ESIParser, loadDevice_parses_fmmus) +{ + ESI::Parser parser; + ESI::Device device = parser.loadDevice("kickcat_esi_test_sm_fmmu.xml"); + + ASSERT_EQ(device.fmmus.size(), 3u); + + ASSERT_EQ(device.fmmus[0].type, fmmu::Outputs); + ASSERT_EQ(device.fmmus[0].sm, 2); + ASSERT_TRUE(device.fmmus[0].op_only); + + ASSERT_EQ(device.fmmus[1].type, fmmu::Inputs); + ASSERT_EQ(device.fmmus[1].sm, 3); + ASSERT_FALSE(device.fmmus[1].op_only); + + ASSERT_EQ(device.fmmus[2].type, fmmu::MBoxState); + ASSERT_EQ(device.fmmus[2].sm, -1); // absent + ASSERT_EQ(device.fmmus[2].su, -1); +} + +TEST(ESIParser, loadDevice_parses_sync_units) +{ + ESI::Parser parser; + ESI::Device device = parser.loadDevice("kickcat_esi_test_sm_fmmu.xml"); + + ASSERT_EQ(device.sync_units.size(), 1u); + ASSERT_TRUE (device.sync_units[0].separate_su); // "true" + ASSERT_FALSE(device.sync_units[0].separate_frame); // "0" + ASSERT_TRUE (device.sync_units[0].frame_repeat_support); // "1" +} + +TEST(ESIParser, sync_managers_drive_legacy_0x1C00_synthesis) +{ + // The synthesized 0x1C00 array must reflect the parsed SM list. + ESI::Parser parser; + auto dictionary = parser.loadFile("kickcat_esi_test_sm_fmmu.xml"); + + auto [object, entry] = findObject(dictionary, 0x1C00, 0); + ASSERT_NE(object, nullptr); + ASSERT_EQ(object->code, CoE::ObjectCode::ARRAY); + ASSERT_EQ(object->entries.size(), 5u); // 1 size byte + 4 SMs + + uint8_t size = 0; + std::memcpy(&size, object->entries[0].data, 1); + ASSERT_EQ(size, 4u); + + uint8_t sm_kinds[4]; + for (int i = 0; i < 4; ++i) + { + std::memcpy(&sm_kinds[i], object->entries[i + 1].data, 1); + } + ASSERT_EQ(sm_kinds[0], 1u); // MBoxOut + ASSERT_EQ(sm_kinds[1], 2u); // MBoxIn + ASSERT_EQ(sm_kinds[2], 3u); // Outputs + ASSERT_EQ(sm_kinds[3], 4u); // Inputs +} + +TEST(ESIParser, throws_on_unknown_fmmu_text) +{ + char const* xml = R"( + + #x1V + + T + 0 + + UDINT32 + + #x1000XUDINT32 + + + + SomeUnknownThing + + )"; + + ESI::Parser parser; + try + { + (void) parser.loadString(xml); + FAIL() << "expected invalid_argument"; + } + catch (std::invalid_argument const& e) + { + std::string msg = e.what(); + ASSERT_NE(msg.find("SomeUnknownThing"), std::string::npos) << msg; + } +} + TEST(ESIParser, CoE_alias_is_backwards_compatible) { // The CoE::EsiParser alias must still resolve to ESI::Parser. diff --git a/unit/src/EmulatedESC-t.cc b/unit/src/EmulatedESC-t.cc index 2227ef74..bf470415 100644 --- a/unit/src/EmulatedESC-t.cc +++ b/unit/src/EmulatedESC-t.cc @@ -231,8 +231,8 @@ TEST(EmulatedESC, ecat_PDOs) uint8_t next = State::SAFE_OP; esc.write(reg::AL_CONTROL, &next, 1); - FMMU fmmu; - memset(&fmmu, 0, sizeof(FMMU)); + fmmu::Register fmmu; + memset(&fmmu, 0, sizeof(fmmu::Register)); fmmu.type = 2; // write access fmmu.logical_address = 0x2000; @@ -242,7 +242,7 @@ TEST(EmulatedESC, ecat_PDOs) fmmu.physical_address = 0x3000; fmmu.physical_start_bit = 0; fmmu.activate = 1; - esc.write(reg::FMMU + 0x00, &fmmu, sizeof(FMMU)); + esc.write(reg::FMMU + 0x00, &fmmu, sizeof(fmmu::Register)); fmmu.type = 1; // read access fmmu.logical_address = 0x200A; @@ -252,7 +252,7 @@ TEST(EmulatedESC, ecat_PDOs) fmmu.physical_address = 0x300A; fmmu.physical_start_bit = 0; fmmu.activate = 1; - esc.write(reg::FMMU + 0x10, &fmmu, sizeof(FMMU)); + esc.write(reg::FMMU + 0x10, &fmmu, sizeof(fmmu::Register)); // run internal logic DatagramHeader header{Command::BRD, 0, 0, sizeof(uint64_t), 0, 0, 0, 0}; diff --git a/unit/src/bus-t.cc b/unit/src/bus-t.cc index b55f8122..05d01ccd 100644 --- a/unit/src/bus-t.cc +++ b/unit/src/bus-t.cc @@ -494,7 +494,7 @@ TEST_F(BusTest, read_SDO_buffer_too_small) TEST_F(BusTest, detect_mapping_CoE) { - addReadEmulatedSDO(CoE::SM_COM_TYPE, { 2, SyncManagerType::Output, SyncManagerType::Input}); + addReadEmulatedSDO(CoE::SM_COM_TYPE, { 2, SyncManager::Output, SyncManager::Input}); addReadEmulatedSDO(CoE::SM_CHANNEL + 0, { 2, 0x1A0A, 0x1A0B }); addReadEmulatedSDO(0x1A0A, { 2, 8, 8 }); diff --git a/unit/src/mailbox/request-t.cc b/unit/src/mailbox/request-t.cc index e130dae9..cc9c6a9e 100644 --- a/unit/src/mailbox/request-t.cc +++ b/unit/src/mailbox/request-t.cc @@ -26,7 +26,7 @@ TEST_F(Mailbox_Request, SyncManager_configuration) mailbox.recv_size = 42; mailbox.recv_offset = 0x300; - SyncManager SM[2]; + SyncManager::Register SM[2]; mailbox.generateSMConfig(SM); ASSERT_EQ(42, SM[0].length); diff --git a/unit/src/mailbox/response-t.cc b/unit/src/mailbox/response-t.cc index e79ebf9a..ed75f8c7 100644 --- a/unit/src/mailbox/response-t.cc +++ b/unit/src/mailbox/response-t.cc @@ -37,14 +37,14 @@ class Mailbox_Response : public ::testing::Test { mbx.enableCoE(createResponseTestDictionary()); - EXPECT_CALL(esc, read(reg::SYNC_MANAGER + sizeof(SyncManager) * 0, _, sizeof(SyncManager))) + EXPECT_CALL(esc, read(reg::SYNC_MANAGER + sizeof(SyncManager::Register) * 0, _, sizeof(SyncManager::Register))) .WillRepeatedly(DoAll( - Invoke([this](uint16_t, void* ptr, uint16_t) { memcpy(ptr, &sm_in, sizeof(SyncManager)); }), + Invoke([this](uint16_t, void* ptr, uint16_t) { memcpy(ptr, &sm_in, sizeof(SyncManager::Register)); }), Return(0))); - EXPECT_CALL(esc, read(reg::SYNC_MANAGER + sizeof(SyncManager) * 1, _, sizeof(SyncManager))) + EXPECT_CALL(esc, read(reg::SYNC_MANAGER + sizeof(SyncManager::Register) * 1, _, sizeof(SyncManager::Register))) .WillRepeatedly(DoAll( - Invoke([this](uint16_t, void* ptr, uint16_t) { memcpy(ptr, &sm_out, sizeof(SyncManager)); }), + Invoke([this](uint16_t, void* ptr, uint16_t) { memcpy(ptr, &sm_out, sizeof(SyncManager::Register)); }), Return(0))); ASSERT_EQ(0, mbx.configure()); @@ -61,12 +61,12 @@ class Mailbox_Response : public ::testing::Test return raw; } - void expectSmStatusRead(uint8_t sm_index, SyncManager const& sync) + void expectSmStatusRead(uint8_t sm_index, SyncManager::Register const& sync) { - EXPECT_CALL(esc, read(addressSM(sm_index), _, sizeof(SyncManager))) + EXPECT_CALL(esc, read(addressSM(sm_index), _, sizeof(SyncManager::Register))) .WillOnce(DoAll( - Invoke([sync](uint16_t, void* ptr, uint16_t) { memcpy(ptr, &sync, sizeof(SyncManager)); }), - Return(sizeof(SyncManager)))) + Invoke([sync](uint16_t, void* ptr, uint16_t) { memcpy(ptr, &sync, sizeof(SyncManager::Register)); }), + Return(sizeof(SyncManager::Register)))) .RetiresOnSaturation(); } @@ -80,8 +80,8 @@ class Mailbox_Response : public ::testing::Test } MockESC esc; - SyncManager sm_in {RESP_MBX_IN_ADDR, RESP_MBX_SIZE, SM_CONTROL_MODE_MAILBOX | SM_CONTROL_DIRECTION_READ, 0, SM_ACTIVATE_ENABLE, 0}; - SyncManager sm_out{RESP_MBX_OUT_ADDR, RESP_MBX_SIZE, SM_CONTROL_MODE_MAILBOX | SM_CONTROL_DIRECTION_WRITE, 0, SM_ACTIVATE_ENABLE, 0}; + SyncManager::Register sm_in {RESP_MBX_IN_ADDR, RESP_MBX_SIZE, SM_CONTROL_MODE_MAILBOX | SM_CONTROL_DIRECTION_READ, 0, SM_ACTIVATE_ENABLE, 0}; + SyncManager::Register sm_out{RESP_MBX_OUT_ADDR, RESP_MBX_SIZE, SM_CONTROL_MODE_MAILBOX | SM_CONTROL_DIRECTION_WRITE, 0, SM_ACTIVATE_ENABLE, 0}; Mailbox mbx{&esc, RESP_MBX_SIZE, 2}; }; @@ -93,7 +93,7 @@ TEST(Mailbox_Reponse_configure, not_configured) for (uint8_t i = 0; i < reg::SM_STATS; ++i) { - EXPECT_CALL(esc, read(reg::SYNC_MANAGER + sizeof(SyncManager) * i, _, sizeof(SyncManager))).WillOnce(Return(0)); + EXPECT_CALL(esc, read(reg::SYNC_MANAGER + sizeof(SyncManager::Register) * i, _, sizeof(SyncManager::Register))).WillOnce(Return(0)); } ASSERT_EQ(-EAGAIN, mbx.configure()); } @@ -106,13 +106,13 @@ TEST(Mailbox_Reponse_configure, badly_configured) for (int i = 0; i < reg::SM_STATS; ++i) { - EXPECT_CALL(esc, read(reg::SYNC_MANAGER + sizeof(SyncManager) * i, _, sizeof(SyncManager))) + EXPECT_CALL(esc, read(reg::SYNC_MANAGER + sizeof(SyncManager::Register) * i, _, sizeof(SyncManager::Register))) .WillOnce(Invoke([&](uint16_t, void* data, uint16_t) { - SyncManager sm{}; - std::memset(&sm, 0, sizeof(SyncManager)); - std::memcpy(data, &sm, sizeof(SyncManager)); - return sizeof(SyncManager); + SyncManager::Register sm{}; + std::memset(&sm, 0, sizeof(SyncManager::Register)); + std::memcpy(data, &sm, sizeof(SyncManager::Register)); + return sizeof(SyncManager::Register); })); } @@ -122,7 +122,7 @@ TEST(Mailbox_Reponse_configure, badly_configured) TEST_F(Mailbox_Response, receive_nothing_when_sm_empty) { - SyncManager sync{}; + SyncManager::Register sync{}; sync.status = 0; expectSmStatusRead(1, sync); @@ -134,7 +134,7 @@ TEST_F(Mailbox_Response, receive_new_CoE_message) { auto raw = buildRawSDORead(0x1018, 1); - SyncManager sync{}; + SyncManager::Register sync{}; sync.status = SM_STATUS_MAILBOX; expectSmStatusRead(1, sync); expectMailboxDataRead(raw); @@ -145,7 +145,7 @@ TEST_F(Mailbox_Response, receive_new_CoE_message) TEST_F(Mailbox_Response, receive_read_failure) { - SyncManager sync{}; + SyncManager::Register sync{}; sync.status = SM_STATUS_MAILBOX; expectSmStatusRead(1, sync); @@ -162,7 +162,7 @@ TEST_F(Mailbox_Response, receive_unsupported_protocol) auto header = pointData(raw.data()); header->type = mailbox::Type::VoE; - SyncManager sync{}; + SyncManager::Register sync{}; sync.status = SM_STATUS_MAILBOX; expectSmStatusRead(1, sync); expectMailboxDataRead(raw); @@ -179,7 +179,7 @@ TEST_F(Mailbox_Response, receive_unsupported_protocol) TEST_F(Mailbox_Response, receive_queue_full) { - SyncManager sync{}; + SyncManager::Register sync{}; sync.status = SM_STATUS_MAILBOX; auto raw1 = buildRawSDORead(0x1018, 1); @@ -210,7 +210,7 @@ TEST_F(Mailbox_Response, process_finalize_message) { auto raw = buildRawSDORead(0x1018, 1); - SyncManager sync{}; + SyncManager::Register sync{}; sync.status = SM_STATUS_MAILBOX; expectSmStatusRead(1, sync); expectMailboxDataRead(raw); @@ -237,7 +237,7 @@ TEST_F(Mailbox_Response, process_empty_queue) TEST_F(Mailbox_Response, send_nothing_when_queue_empty) { - SyncManager sync{}; + SyncManager::Register sync{}; sync.status = 0; sync.activate = 0; sync.pdi_control = 0; @@ -251,7 +251,7 @@ TEST_F(Mailbox_Response, send_message) { auto raw = buildRawSDORead(0x1018, 2); - SyncManager sync_out{}; + SyncManager::Register sync_out{}; sync_out.status = SM_STATUS_MAILBOX; expectSmStatusRead(1, sync_out); expectMailboxDataRead(raw); @@ -259,7 +259,7 @@ TEST_F(Mailbox_Response, send_message) mbx.receive(); mbx.process(); - SyncManager sync_in{}; + SyncManager::Register sync_in{}; sync_in.status = 0; sync_in.activate = 0; sync_in.pdi_control = 0; @@ -276,7 +276,7 @@ TEST_F(Mailbox_Response, send_blocked_when_mailbox_full) { auto raw = buildRawSDORead(0x1018, 2); - SyncManager sync_out{}; + SyncManager::Register sync_out{}; sync_out.status = SM_STATUS_MAILBOX; expectSmStatusRead(1, sync_out); expectMailboxDataRead(raw); @@ -284,7 +284,7 @@ TEST_F(Mailbox_Response, send_blocked_when_mailbox_full) mbx.receive(); mbx.process(); - SyncManager sync_in{}; + SyncManager::Register sync_in{}; sync_in.status = SM_STATUS_MAILBOX; sync_in.activate = 0; sync_in.pdi_control = 0; @@ -300,7 +300,7 @@ TEST_F(Mailbox_Response, send_repeat_procedure) { auto raw = buildRawSDORead(0x1018, 2); - SyncManager sync_out{}; + SyncManager::Register sync_out{}; sync_out.status = SM_STATUS_MAILBOX; expectSmStatusRead(1, sync_out); expectMailboxDataRead(raw); @@ -309,7 +309,7 @@ TEST_F(Mailbox_Response, send_repeat_procedure) mbx.process(); // First send: normal - SyncManager sync_first{}; + SyncManager::Register sync_first{}; sync_first.status = 0; sync_first.activate = 0; sync_first.pdi_control = 0; @@ -322,7 +322,7 @@ TEST_F(Mailbox_Response, send_repeat_procedure) mbx.send(); // Second send: IRQ_READ + repeat requested - SyncManager sync_repeat{}; + SyncManager::Register sync_repeat{}; sync_repeat.status = SM_STATUS_IRQ_READ; sync_repeat.activate = SM_ACTIVATE_REPEAT_REQ; sync_repeat.pdi_control = 0; @@ -350,7 +350,7 @@ TEST_F(Mailbox_Response, full_receive_process_send_cycle) { auto raw = buildRawSDORead(0x1018, 2); - SyncManager sync_out{}; + SyncManager::Register sync_out{}; sync_out.status = SM_STATUS_MAILBOX; expectSmStatusRead(1, sync_out); expectMailboxDataRead(raw); @@ -358,7 +358,7 @@ TEST_F(Mailbox_Response, full_receive_process_send_cycle) mbx.process(); - SyncManager sync_in{}; + SyncManager::Register sync_in{}; sync_in.status = 0; sync_in.activate = 0; sync_in.pdi_control = 0; diff --git a/unit/src/protocol-t.cc b/unit/src/protocol-t.cc index 058edcf7..83614e2f 100644 --- a/unit/src/protocol-t.cc +++ b/unit/src/protocol-t.cc @@ -39,7 +39,7 @@ TEST(Protocol, SyncManageType_to_string) { for (uint8_t i = 0; i < UINT8_MAX; ++i) { - char const* text = toString(static_cast(i)); + char const* text = toString(static_cast(i)); ASSERT_EQ(4, strnlen(text, 4)); } } diff --git a/unit/src/slave/PDO-t.cc b/unit/src/slave/PDO-t.cc index a0d9f5a1..c2738ef5 100644 --- a/unit/src/slave/PDO-t.cc +++ b/unit/src/slave/PDO-t.cc @@ -15,9 +15,9 @@ constexpr uint16_t PDO_IN_ADDR = 0x1400; constexpr uint16_t PDO_OUT_ADDR = 0x1600; constexpr uint16_t PDO_SIZE = 16; -static SyncManager makeSM(uint16_t start, uint16_t length, uint8_t control) +static SyncManager::Register makeSM(uint16_t start, uint16_t length, uint8_t control) { - SyncManager sm{}; + SyncManager::Register sm{}; sm.start_address = start; sm.length = length; sm.control = control; @@ -27,7 +27,7 @@ static SyncManager makeSM(uint16_t start, uint16_t length, uint8_t control) static constexpr uint16_t smPdiAddr(uint8_t index) { - return static_cast(reg::SYNC_MANAGER + index * sizeof(SyncManager) + 7); + return static_cast(reg::SYNC_MANAGER + index * sizeof(SyncManager::Register) + 7); } static uint32_t makeMappingEntry(uint16_t index, uint8_t sub, uint8_t bits) @@ -44,11 +44,11 @@ class PDOTest : public ::testing::Test uint8_t input_[PDO_SIZE]{}; uint8_t output_[PDO_SIZE]{}; - SyncManager sm_pdo_in_ = makeSM(PDO_IN_ADDR, PDO_SIZE, SM_CONTROL_MODE_BUFFERED | SM_CONTROL_DIRECTION_READ); - SyncManager sm_pdo_out_ = makeSM(PDO_OUT_ADDR, PDO_SIZE, SM_CONTROL_MODE_BUFFERED | SM_CONTROL_DIRECTION_WRITE); - SyncManager sm_mbx_in_ = makeSM(0x1000, 256, SM_CONTROL_MODE_MAILBOX | SM_CONTROL_DIRECTION_READ); - SyncManager sm_mbx_out_ = makeSM(0x1200, 256, SM_CONTROL_MODE_MAILBOX | SM_CONTROL_DIRECTION_WRITE); - SyncManager sm_empty_{}; + SyncManager::Register sm_pdo_in_ = makeSM(PDO_IN_ADDR, PDO_SIZE, SM_CONTROL_MODE_BUFFERED | SM_CONTROL_DIRECTION_READ); + SyncManager::Register sm_pdo_out_ = makeSM(PDO_OUT_ADDR, PDO_SIZE, SM_CONTROL_MODE_BUFFERED | SM_CONTROL_DIRECTION_WRITE); + SyncManager::Register sm_mbx_in_ = makeSM(0x1000, 256, SM_CONTROL_MODE_MAILBOX | SM_CONTROL_DIRECTION_READ); + SyncManager::Register sm_mbx_out_ = makeSM(0x1200, 256, SM_CONTROL_MODE_MAILBOX | SM_CONTROL_DIRECTION_WRITE); + SyncManager::Register sm_empty_{}; void SetUp() override { @@ -59,13 +59,13 @@ class PDOTest : public ::testing::Test void setupSmReads() { - auto setupSm = [this](int idx, SyncManager const &sm) + auto setupSm = [this](int idx, SyncManager::Register const &sm) { - ON_CALL(esc_, read(static_cast(reg::SYNC_MANAGER + sizeof(SyncManager) * idx), _, sizeof(SyncManager))) + ON_CALL(esc_, read(static_cast(reg::SYNC_MANAGER + sizeof(SyncManager::Register) * idx), _, sizeof(SyncManager::Register))) .WillByDefault(DoAll( Invoke([sm](uint16_t, void *ptr, uint16_t) - { std::memcpy(ptr, &sm, sizeof(SyncManager)); }), - Return(sizeof(SyncManager)))); + { std::memcpy(ptr, &sm, sizeof(SyncManager::Register)); }), + Return(sizeof(SyncManager::Register)))); }; setupSm(0, sm_mbx_in_); @@ -112,15 +112,15 @@ TEST_F(PDOTest, configure_success) TEST_F(PDOTest, configure_failure_no_buffered_sm) { // Replace all SMs with mailbox-only (control=0x02), so findSm(BUFFERED) throws - SyncManager mbx{}; + SyncManager::Register mbx{}; mbx.control = SM_CONTROL_MODE_MAILBOX | SM_CONTROL_DIRECTION_READ; for (int i = 0; i < 5; ++i) { - ON_CALL(esc_, read(static_cast(reg::SYNC_MANAGER + sizeof(SyncManager) * i), _, sizeof(SyncManager))) + ON_CALL(esc_, read(static_cast(reg::SYNC_MANAGER + sizeof(SyncManager::Register) * i), _, sizeof(SyncManager::Register))) .WillByDefault(DoAll( Invoke([mbx](uint16_t, void *ptr, uint16_t) - { std::memcpy(ptr, &mbx, sizeof(SyncManager)); }), - Return(sizeof(SyncManager)))); + { std::memcpy(ptr, &mbx, sizeof(SyncManager::Register)); }), + Return(sizeof(SyncManager::Register)))); } ASSERT_EQ(-EINVAL, pdo_.configure()); } @@ -136,26 +136,26 @@ TEST_F(PDOTest, isConfigOk_no_error) TEST_F(PDOTest, isConfigOk_invalid_input_length_mismatch) { configurePdo(); - SyncManager bad = sm_pdo_in_; + SyncManager::Register bad = sm_pdo_in_; bad.length = PDO_SIZE + 1; - ON_CALL(esc_, read(static_cast(reg::SYNC_MANAGER + sizeof(SyncManager) * 2), _, sizeof(SyncManager))) + ON_CALL(esc_, read(static_cast(reg::SYNC_MANAGER + sizeof(SyncManager::Register) * 2), _, sizeof(SyncManager::Register))) .WillByDefault(DoAll( Invoke([bad](uint16_t, void *ptr, uint16_t) - { std::memcpy(ptr, &bad, sizeof(SyncManager)); }), - Return(sizeof(SyncManager)))); + { std::memcpy(ptr, &bad, sizeof(SyncManager::Register)); }), + Return(sizeof(SyncManager::Register)))); ASSERT_EQ(StatusCode::INVALID_INPUT_CONFIGURATION, pdo_.isConfigOk()); } TEST_F(PDOTest, isConfigOk_invalid_output_length_mismatch) { configurePdo(); - SyncManager bad = sm_pdo_out_; + SyncManager::Register bad = sm_pdo_out_; bad.length = PDO_SIZE + 1; - ON_CALL(esc_, read(static_cast(reg::SYNC_MANAGER + sizeof(SyncManager) * 3), _, sizeof(SyncManager))) + ON_CALL(esc_, read(static_cast(reg::SYNC_MANAGER + sizeof(SyncManager::Register) * 3), _, sizeof(SyncManager::Register))) .WillByDefault(DoAll( Invoke([bad](uint16_t, void *ptr, uint16_t) - { std::memcpy(ptr, &bad, sizeof(SyncManager)); }), - Return(sizeof(SyncManager)))); + { std::memcpy(ptr, &bad, sizeof(SyncManager::Register)); }), + Return(sizeof(SyncManager::Register)))); ASSERT_EQ(StatusCode::INVALID_OUTPUT_CONFIGURATION, pdo_.isConfigOk()); } @@ -163,7 +163,7 @@ TEST_F(PDOTest, isConfigOk_invalid_output_length_mismatch) TEST_F(PDOTest, activateOutput_unused_type_no_esc_calls) { - // Before configure(), sm_output_.type == SyncManagerType::Unused + // Before configure(), sm_output_.type == SyncManager::Unused EXPECT_CALL(esc_, write(_, _, _)).Times(0); pdo_.activateOutput(true); } diff --git a/unit/src/slave/slave-t.cc b/unit/src/slave/slave-t.cc index 84c87471..02111718 100644 --- a/unit/src/slave/slave-t.cc +++ b/unit/src/slave/slave-t.cc @@ -50,10 +50,10 @@ class SlaveTest : public ::testing::Test uint8_t buffer_in_[256]{}; uint8_t buffer_out_[256]{}; - SyncManager mbx_in_ {MBX_IN_ADDR, MBX_SIZE, SM_CONTROL_MODE_MAILBOX | SM_CONTROL_DIRECTION_READ, 0, SM_ACTIVATE_ENABLE, 0}; - SyncManager mbx_out_{MBX_OUT_ADDR, MBX_SIZE, SM_CONTROL_MODE_MAILBOX | SM_CONTROL_DIRECTION_WRITE, 0, SM_ACTIVATE_ENABLE, 0}; - SyncManager pdo_in_ {PDO_IN_ADDR, sizeof(buffer_in_), SM_CONTROL_MODE_BUFFERED | SM_CONTROL_DIRECTION_READ, 0, SM_ACTIVATE_ENABLE, 0}; - SyncManager pdo_out_{PDO_OUT_ADDR, sizeof(buffer_out_), SM_CONTROL_MODE_BUFFERED | SM_CONTROL_DIRECTION_WRITE, 0, SM_ACTIVATE_ENABLE, 0}; + SyncManager::Register mbx_in_ {MBX_IN_ADDR, MBX_SIZE, SM_CONTROL_MODE_MAILBOX | SM_CONTROL_DIRECTION_READ, 0, SM_ACTIVATE_ENABLE, 0}; + SyncManager::Register mbx_out_{MBX_OUT_ADDR, MBX_SIZE, SM_CONTROL_MODE_MAILBOX | SM_CONTROL_DIRECTION_WRITE, 0, SM_ACTIVATE_ENABLE, 0}; + SyncManager::Register pdo_in_ {PDO_IN_ADDR, sizeof(buffer_in_), SM_CONTROL_MODE_BUFFERED | SM_CONTROL_DIRECTION_READ, 0, SM_ACTIVATE_ENABLE, 0}; + SyncManager::Register pdo_out_{PDO_OUT_ADDR, sizeof(buffer_out_), SM_CONTROL_MODE_BUFFERED | SM_CONTROL_DIRECTION_WRITE, 0, SM_ACTIVATE_ENABLE, 0}; mailbox::response::Mailbox mbx_{&esc_, MBX_SIZE}; @@ -82,35 +82,35 @@ class SlaveTest : public ::testing::Test void setupSmDefaults() { - ON_CALL(esc_, read(reg::SYNC_MANAGER + sizeof(SyncManager) * 0, _, sizeof(SyncManager))) + ON_CALL(esc_, read(reg::SYNC_MANAGER + sizeof(SyncManager::Register) * 0, _, sizeof(SyncManager::Register))) .WillByDefault(DoAll( Invoke([this](uint16_t, void* ptr, uint16_t) - { std::memcpy(ptr, &mbx_in_, sizeof(SyncManager)); }), + { std::memcpy(ptr, &mbx_in_, sizeof(SyncManager::Register)); }), Return(0))); - ON_CALL(esc_, read(reg::SYNC_MANAGER + sizeof(SyncManager) * 1, _, sizeof(SyncManager))) + ON_CALL(esc_, read(reg::SYNC_MANAGER + sizeof(SyncManager::Register) * 1, _, sizeof(SyncManager::Register))) .WillByDefault(DoAll( Invoke([this](uint16_t, void* ptr, uint16_t) - { std::memcpy(ptr, &mbx_out_, sizeof(SyncManager)); }), + { std::memcpy(ptr, &mbx_out_, sizeof(SyncManager::Register)); }), Return(0))); - ON_CALL(esc_, read(reg::SYNC_MANAGER + sizeof(SyncManager) * 2, _, sizeof(SyncManager))) + ON_CALL(esc_, read(reg::SYNC_MANAGER + sizeof(SyncManager::Register) * 2, _, sizeof(SyncManager::Register))) .WillByDefault(DoAll( Invoke([this](uint16_t, void* ptr, uint16_t) - { std::memcpy(ptr, &pdo_in_, sizeof(SyncManager)); }), + { std::memcpy(ptr, &pdo_in_, sizeof(SyncManager::Register)); }), Return(0))); - ON_CALL(esc_, read(reg::SYNC_MANAGER + sizeof(SyncManager) * 3, _, sizeof(SyncManager))) + ON_CALL(esc_, read(reg::SYNC_MANAGER + sizeof(SyncManager::Register) * 3, _, sizeof(SyncManager::Register))) .WillByDefault(DoAll( Invoke([this](uint16_t, void* ptr, uint16_t) - { std::memcpy(ptr, &pdo_out_, sizeof(SyncManager)); }), + { std::memcpy(ptr, &pdo_out_, sizeof(SyncManager::Register)); }), Return(0))); - SyncManager sm_empty{}; - ON_CALL(esc_, read(reg::SYNC_MANAGER + sizeof(SyncManager) * 4, _, sizeof(SyncManager))) + SyncManager::Register sm_empty{}; + ON_CALL(esc_, read(reg::SYNC_MANAGER + sizeof(SyncManager::Register) * 4, _, sizeof(SyncManager::Register))) .WillByDefault(DoAll( Invoke([sm_empty](uint16_t, void* ptr, uint16_t) - { std::memcpy(ptr, &sm_empty, sizeof(SyncManager)); }), + { std::memcpy(ptr, &sm_empty, sizeof(SyncManager::Register)); }), Return(0))); }