Skip to content

Extract core logic to ethercat bus master#223

Merged
mcbed merged 7 commits into
ICube-Robotics:jazzyfrom
JenniferBuehler:feat/extract-core-logic-to-ethercat-bus-master
Jul 9, 2026
Merged

Extract core logic to ethercat bus master#223
mcbed merged 7 commits into
ICube-Robotics:jazzyfrom
JenniferBuehler:feat/extract-core-logic-to-ethercat-bus-master

Conversation

@JenniferBuehler

@JenniferBuehler JenniferBuehler commented May 22, 2026

Copy link
Copy Markdown
Contributor

This PR proposes to extract the core logic from ethercat_driver.cpp into two modules which are in itself largely ROS2-independent and can be re-used in different hardware interface implementations (or even without ros2_control):

  • A new EthercatBusManager which handles configuration, activation, reading, writing to/from the EtherCAT bus. That was previously all done in the EthercatDriver (ros2_control hardware interface).
  • XML parsing logic for <ec_module> blocks has been extracted to a separate parser function in ethercat_ros2_control_xml_parser.hpp/cpp

What remains ROS2 related (which is fine, we mainly want to extract from ros2_control hardware interface, so we can re-use in a different hardware interface!):

  • ROS2 logging
  • Pluginlib for loading ECSlave plugins. Reasoning: while plugins are part of the ROS2 ecosystem, it is a generic and very useful concept (it would actually be better if it lived outside the ROS2 ecosystem). We want the user to be able to load their own slaves through plugins. So we'll keep using this inside EthercatBusManager even if it's ROS2.

Important

Merge after #221 !

Important

This PR is focussed mainly on moving code, without changing logic significantly (though a few minor changes were made, see sections below).
However the diff still looks horrendous because code moving to new files can't be visualized well via github.
To make it easier to inspect the actual moving parts, I attached a script which uses meld (you'll need to sudo apt install meld) to show the diff.
image
This will show you the diff between the original ethercat_driver.cpp to the new place where the code was moved.
The white parts are the same - so moved code. Blue and green are where changes happened - when it looks like lots of changes in one diff, it's largely because that part has moved to the other file (e.g. xml parser). This is also not perfect, but at least a bit better.

Script: just run from the repo root folder. meld_diff.sh

Logic changes beyond just code moving

Beyond moving code and necessities (naming, splitting to different functions) this PR adds the following minimal changes to the logic.

Everything else in the diff is API shape required by the extraction (touched on in next section below), not a behavior change.

Bug fix: propagate errors

activateBus() propagates errors from setupMaster() and configNetwork(). The pre-refactor on_activate() called
both but ignored their return values; if master_id or control_frequency parsing failed it logged FATAL and then
segfaulted in master_->activate() on a null pointer. The new code returns failure as CallbackReturn::ERROR.

Guard: rejection of double bus activation

configureModules() rejects re-entry when the bus is active. If called while activated_ is true, it logs FATAL and returns false rather than clearing the internal state (if we want to add this feature later, we'll have to make sure the state is really properly cleaned, including going through stop). When using ros2_control this should never be hit because on_init is only called once.

Minor bug fix: empty transfer/fsoe config

Empty transfer_config / fsoe_config values now fail earlier with CallbackReturn::ERROR. Before: the key being present triggered transfer loading, then an empty path would fail later in YAML::LoadFile. Providing both transfer_config and fsoe_config now also fails via the early config conversion path (at the top of on_init). Previously it failed later, inside loadTransferConfigYamlFile().

Other (not real behavioral) changes

activation defaults to false

activated_ is now initialized to false in EthercatBusManager (EthercatDriver::activated_ was a raw with no initializer)

New result type

read() / write() now return a typed EthercatCycleResult (kCompleted / kSkippedInactive / kSkippedBusy / kError). The driver wrapper translates everything except kError to return_type::OK, preserving the original semantics. Nothing returns kError today, so the actual behavior is unchanged. The enum exists for future error paths.

API decoupling from ros2_control

Bus configuration is a typed EthercatBusConfig struct (master_id, control_frequency, transfer_config, fsoe_config) rather than a std::unordered_map<std::string,std::string> of ros2_control hardware parameters. EthercatDriver::on_init translates info_.hardware_parameters into the struct before handing it to the manager. This allows for different structure of input (e.g. from a differently structured URDF/yaml).

ConfiguredEcModule field renames: state_interface → input_values, command_interface → output_values. Removes
ros2_control vocabulary from the manager's public data type to avoid confusion.

Logging

Logger channel renamed from "EthercatDriver" to "EthercatBusManager" for every call inside the bus manager.

Unused code removal

configTransferNetwork() no-op method deleted. It had an empty body in the pre-refactor code; transfer-net registration is now done inline in activateBus().

Test removal

TestHelperEthercatSafetyDriver subclass deleted (testHelper_ethercat_safety_driver.hpp) as it wasn't needed any more: those methods are now pulbis on EthercatBusManager. The three tests now instantiate ethercat_driver::EthercatBusManager driver; and call driver.getEcTransferModuleParam(...) / driver.getEcTransferNets(...) directly. No actual runtime behavior change.

Test plan


Run the unit tests!

Important

I used IgH ethercat branch stable-1.6 for testing

colcon build --symlink-install --packages-up-to ethercat_driver
colcon test --packages-select ethercat_driver --event-handlers console_direct+

Expected: 9/9 tests pass

Then, test on the real HW 🤖 - just check that no regression happened (this is a code moving PR so it should be OK, but please do verify)

Call ecrt_release_master() in ~EcMaster() to properly release the kernel
EtherCAT master device. Without this, the master remains locked after the
process exits, preventing re-initialization on restart without unloading
the kernel module.

This is especially critical in multi-master setups where multiple
EcMaster instances coexist.
@tgaspar

tgaspar commented May 25, 2026

Copy link
Copy Markdown

I've ran the tests:

100% tests passed, 0 tests failed out of 9

Good work!

@JenniferBuehler
JenniferBuehler marked this pull request as ready for review June 11, 2026 12:05
@tgaspar
tgaspar force-pushed the feat/extract-core-logic-to-ethercat-bus-master branch from 56d7f46 to a550602 Compare June 12, 2026 06:39

@mcbed mcbed left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good for me, hardware and safety tests are passing

@mcbed
mcbed merged commit fcf673a into ICube-Robotics:jazzy Jul 9, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants