Merge branch 'fake_without_domain' into 'stable-1.6'

Remove domain information from  RtIPC path.

See merge request etherlab.org/ethercat!149
This commit is contained in:
Florian Pose 2025-02-06 08:27:18 +00:00
commit 5b3663e15b
2 changed files with 28 additions and 69 deletions

View File

@ -1,16 +1,16 @@
FakeEtherCAT Library {#libfakeethercat} FakeEtherCAT Library {#libfakeethercat}
==================== ====================
Libfakeethercat is a userspace library which has the same API as Libfakeethercat is a userspace library which has the same API as the EtherCAT
the EtherCAT master interface library libethercat. master interface library libethercat. Libfakeethercat can be used to spin up
Libfakeethercat can be used to spin up your RT application in a dry-run mode, your RT application in a dry-run mode, without any master configured or slaves
without any master configured or slaves attached. attached. Furthermore, it is possible to emulate EtherCAT slaves on process
Furthermore, it is possible to emulate EtherCAT slaves on process data level data level by running two applications back to back.
by running two applications back to back.
## Supported features ## Supported features
Currently, only a very limited subset of libethercat functionality is supported: Currently, only a very limited subset of libethercat functionality is
supported:
- Creating master and domain instances - Creating master and domain instances
- Activating master, `send`/`receive`. - Activating master, `send`/`receive`.
@ -18,26 +18,24 @@ Currently, only a very limited subset of libethercat functionality is supported:
- Configuring PDOs - Configuring PDOs
- Configuring SDOs using `ecrt_slave_config_sdo*` - Configuring SDOs using `ecrt_slave_config_sdo*`
The SDO config does not do anything, The SDO config does not do anything, but when activating the master the SDO
but when activating the master the SDO config will config will be dumped into a JSON file.
be dumped into a JSON file.
ecrt_master_state() and ecrt_domain_state() both return states as if `ecrt_master_state()` and `ecrt_domain_state()` both return states as if the
the bus works without errors. bus works without errors. So currently, a bus error cannot be simulated.
So currenty, a bus error cannot be simulated.
## How to build ## How to build
[RtIPC](https://gitlab.com/etherlab.org/rtipc) is needed. [RtIPC](https://gitlab.com/etherlab.org/rtipc) is needed. Simply pass
Simply pass `--enable-fakeuserlib` to your `./configure` call `--enable-fakeuserlib` to your `./configure` call and the library will be
and the library will be built for you. built for you.
## How to set up dry run mode ## How to set up dry run mode
### Redirect library loading ### Redirect library loading
To avoid recompiling your application, To avoid recompiling your application, we will use `LD_LIBRARY_PATH` to load
we will use `LD_LIBRARY_PATH` to load `libfakeethercat` instead of `libethercat`. `libfakeethercat` instead of `libethercat`.
```sh ```sh
# pick a location for an empty directory # pick a location for an empty directory
@ -52,16 +50,15 @@ ln -s /usr/lib64/libfakeethercat.so.1 $MY_LIB_LOCATION/libethercat.so.1
export LD_LIBRARY_PATH=$MY_LIB_LOCATION export LD_LIBRARY_PATH=$MY_LIB_LOCATION
# check whether everything is done right # check whether everything is done right
ldd my_application | grep ethercat ldd my_application | grep ethercat
libethercat.so.1 => /home/vh/fake_lib64/libethercat.so.1 (0x00007fa5a5c59000) libethercat.so.1 => /home/vh/fake_lib64/libethercat.so.1 (0x7fa5c590)
``` ```
### Set up FakeEtherCAT Home ### Set up FakeEtherCAT Home
RtIPC needs a place to store its configuration. RtIPC needs a place to store its configuration. Set `FAKE_EC_HOMEDIR`
Set `FAKE_EC_HOMEDIR` environment variabe to a path to an empty directory, environment variable to a path to an empty directory, for instance
for instance `/tmp/FakeEtherCAT`. `/tmp/FakeEtherCAT`. `FAKE_EC_NAME` can be set to a useful name of your
`FAKE_EC_NAME` can be set to a useful name of your application, application, default is `FakeEtherCAT`.
default is `FakeEtherCAT`.
```sh ```sh
export FAKE_EC_HOMEDIR=/tmp/FakeEtherCAT export FAKE_EC_HOMEDIR=/tmp/FakeEtherCAT
@ -113,13 +110,9 @@ Then, in another shell, do the same thing with your simulator,
but do not remove the `FAKE_EC_HOMEDIR` directory and but do not remove the `FAKE_EC_HOMEDIR` directory and
pick another `FAKE_EC_NAME`. pick another `FAKE_EC_NAME`.
Carefully watch the PDO configuration on stderr and compare them. Carefully watch the PDO configuration on stderr and compare them. All paths
All paths configured as Output on the control application have to configured as output on the control application have to be configured as input
be configured as Input on your simulator and vice versa. on your simulator and vice versa.
If you use multiple domains and there is a mismatch of the domain IDs,
set `FAKE_EC_DOMAIN_PERMUTATION` to a space-separated list of integers to
permutate the domain IDs of one application.
`FAKE_EC_DOMAIN_PERMUTATION="0 1"` swaps domains 0 and 1, for instance.
Finally, your control application needs to be restarted Finally, your control application needs to be restarted
so it can find the RtIPC variables which contains the process data of the so it can find the RtIPC variables which contains the process data of the
@ -127,7 +120,7 @@ simulator.
## Environment variables ## Environment variables
- FAKE_EC_DOMAIN_PERMUTATION: Permutate the domain IDs, useful to match control and simulation applications. - FAKE_EC_HOMEDIR: Directory for RtIPC bulletin board and SDO json files
- FAKE_EC_HOMEDIR: Directory for RtIPC builletin board and SDO json files
- FAKE_EC_NAME: Will be used for naming RtIPC config and SDO json file - FAKE_EC_NAME: Will be used for naming RtIPC config and SDO json file
- FAKE_EC_PREFIX: Prefix for RtIPC variables, useful to run multiple simulators side by side. - FAKE_EC_PREFIX: Prefix for RtIPC variables, useful to run multiple
simulators side by side.

View File

@ -31,8 +31,6 @@
#include <iterator> #include <iterator>
#include <ios> #include <ios>
static std::vector<size_t> getPermutationVector(size_t count);
static std::ostream &operator<<(std::ostream &os, const sdo_address &a) static std::ostream &operator<<(std::ostream &os, const sdo_address &a)
{ {
os << std::setfill('0') << std::hex << std::setw(6) << a.getCombined(); os << std::setfill('0') << std::hex << std::setw(6) << a.getCombined();
@ -118,7 +116,7 @@ int ec_domain::activate(int domain_id)
slaves.insert(pdo.slave_address.getCombined()); slaves.insert(pdo.slave_address.getCombined());
void *rt_pdo = nullptr; void *rt_pdo = nullptr;
char buf[512]; char buf[512];
const auto fmt = snprintf(buf, sizeof(buf), "%s/%d/%d/%08X/%04X", prefix, master->getId(), domain_id, pdo.slave_address.getCombined(), pdo.pdo_index); const auto fmt = snprintf(buf, sizeof(buf), "%s/%d/%08X/%04X", prefix, master->getId(), pdo.slave_address.getCombined(), pdo.pdo_index);
if (fmt < 0 || fmt >= (int)sizeof(buf)) if (fmt < 0 || fmt >= (int)sizeof(buf))
{ {
return -ENOBUFS; return -ENOBUFS;
@ -213,14 +211,6 @@ int ecrt_domain_state(
int ec_master::activate() int ec_master::activate()
{ {
const auto permutate = getPermutationVector(domains.size());
int i = 0;
for (auto &domain : domains)
{
if (domain.activate(permutate[i]))
return -1;
++i;
}
{ {
std::ofstream out(rt_ipc_dir + "/" + rt_ipc_name + "_slaves.json"); std::ofstream out(rt_ipc_dir + "/" + rt_ipc_name + "_slaves.json");
if (!out.is_open()) if (!out.is_open())
@ -433,30 +423,6 @@ static const char *getRtIpcDir()
return "/tmp/FakeEtherCAT"; return "/tmp/FakeEtherCAT";
} }
static std::vector<size_t> getPermutationVector(size_t count)
{
std::vector<size_t> ans;
for (size_t i = 0; i < count; ++i)
{
ans.push_back(i);
}
const auto spec = getenv("FAKE_EC_DOMAIN_PERMUTATION");
if (!spec)
return ans;
std::istringstream is(spec);
std::istream_iterator<int> begin(is), end;
std::vector<int> values(begin, end);
if (values.size() % 2)
{
throw std::invalid_argument("Specify an even number of indices to permutate.\n");
}
for (size_t i = 0; i < values.size() / 2; ++i)
{
std::swap(ans.at(values[2 * i]), ans.at(values[2 * i + 1]));
}
return ans;
}
ec_master::ec_master(int id) : rt_ipc_dir(getRtIpcDir()), rt_ipc_name(getName()), rt_ipc(rtipc_create(rt_ipc_name.c_str(), rt_ipc_dir.c_str())), id_(id) ec_master::ec_master(int id) : rt_ipc_dir(getRtIpcDir()), rt_ipc_name(getName()), rt_ipc(rtipc_create(rt_ipc_name.c_str(), rt_ipc_dir.c_str())), id_(id)
{ {
} }