| Line |
Branch |
Exec |
Source |
| 1 |
|
|
/***************************************************************************** |
| 2 |
|
|
* |
| 3 |
|
|
* This file is part of the ecapp library (EtherCAT application devices). |
| 4 |
|
|
* |
| 5 |
|
|
* Copyright (C) 2026 Florian Pose <fp@igh.de> |
| 6 |
|
|
* |
| 7 |
|
|
* The ecapp library is free software: you can redistribute it and/or |
| 8 |
|
|
* modify it under the terms of the GNU Lesser General Public License |
| 9 |
|
|
* as published by the Free Software Foundation, version 3 of the |
| 10 |
|
|
* License. |
| 11 |
|
|
* |
| 12 |
|
|
* The ecapp library is distributed in the hope that it will be useful, |
| 13 |
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 |
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 15 |
|
|
* Lesser General Public License for more details. |
| 16 |
|
|
* |
| 17 |
|
|
* You should have received a copy of the GNU Lesser General Public |
| 18 |
|
|
* License along with the ecapp library. If not, see |
| 19 |
|
|
* <https://www.gnu.org/licenses/>. |
| 20 |
|
|
* |
| 21 |
|
|
****************************************************************************/ |
| 22 |
|
|
|
| 23 |
|
|
#ifndef ECAPP_IOLINKDEVICE_H |
| 24 |
|
|
#define ECAPP_IOLINKDEVICE_H |
| 25 |
|
|
|
| 26 |
|
|
/****************************************************************************/ |
| 27 |
|
|
|
| 28 |
|
|
#include "ecapp/SubDevice.h" |
| 29 |
|
|
#include "ecapp_export.h" |
| 30 |
|
|
|
| 31 |
|
|
#include <ecrt.h> // EtherCAT realtime interface |
| 32 |
|
|
#include <functional> |
| 33 |
|
|
#include <memory> |
| 34 |
|
|
#include <pdserv.h> |
| 35 |
|
|
#include <string> |
| 36 |
|
|
#include <utility> |
| 37 |
|
|
#include <vector> |
| 38 |
|
|
|
| 39 |
|
|
/****************************************************************************/ |
| 40 |
|
|
|
| 41 |
|
|
namespace EcApp { |
| 42 |
|
|
|
| 43 |
|
|
class Domain; |
| 44 |
|
|
|
| 45 |
|
|
/** Base class for a driver plugged into one port of an IO-Link master |
| 46 |
|
|
* terminal (e.g. EL6224). An IO-Link master's ports are themselves |
| 47 |
|
|
* tiny independent devices, each with its own fixed set of channels |
| 48 |
|
|
* that only becomes known once a concrete driver is assigned to the |
| 49 |
|
|
* port -- so rather than inventing a second generic-access mechanism |
| 50 |
|
|
* for them, IoLinkDevice simply *is* a SubDevice; the hosting |
| 51 |
|
|
* terminal's IoLinkMaster::port() hands one back as a plain SubDevice. |
| 52 |
|
|
* |
| 53 |
|
|
* A fresh instance (from createIoLinkDevice()) is unconfigured; the |
| 54 |
|
|
* hosting terminal drives the one-time setup sequence per port, in |
| 55 |
|
|
* this order: addConfig() (identifies the device to the master via |
| 56 |
|
|
* SDO), then addOutputs()/addInputs() (dynamically maps this port's |
| 57 |
|
|
* slot into the terminal's PDOs -- returning false leaves that |
| 58 |
|
|
* direction unused), then registerOutputs()/registerInputs() (resolves |
| 59 |
|
|
* the mapped PDO entries against the terminal's shared domains), then |
| 60 |
|
|
* registerVariables() (optional pdserv publishing, guarded by the |
| 61 |
|
|
* caller passing non-null pdserv/task, same convention as every other |
| 62 |
|
|
* ecapp device). */ |
| 63 |
|
2 |
class ECAPP_EXPORT IoLinkDevice : public SubDevice |
| 64 |
|
|
{ |
| 65 |
|
|
public: |
| 66 |
|
|
virtual void addConfig(ec_slave_config_t *, uint16_t sdoIndex) = 0; |
| 67 |
|
|
|
| 68 |
|
✗ |
virtual bool addOutputs( |
| 69 |
|
|
ec_slave_config_t *, |
| 70 |
|
|
uint16_t pdoIndex, |
| 71 |
|
|
uint16_t pdoEntryIndex, |
| 72 |
|
|
Domain *domainOut) |
| 73 |
|
|
{ |
| 74 |
|
✗ |
return false; |
| 75 |
|
|
} |
| 76 |
|
|
|
| 77 |
|
✗ |
virtual bool addInputs( |
| 78 |
|
|
ec_slave_config_t *, |
| 79 |
|
|
uint16_t pdoIndex, |
| 80 |
|
|
uint16_t pdoEntryIndex, |
| 81 |
|
|
Domain *domainIn) |
| 82 |
|
|
{ |
| 83 |
|
✗ |
return false; |
| 84 |
|
|
} |
| 85 |
|
|
|
| 86 |
|
✗ |
virtual void registerOutputs(ec_slave_config_t *, Domain *domainOut) {} |
| 87 |
|
|
|
| 88 |
|
✗ |
virtual void registerInputs(ec_slave_config_t *, Domain *domainIn) {} |
| 89 |
|
|
|
| 90 |
|
|
virtual void |
| 91 |
|
✗ |
registerVariables(pdserv *, pdtask *, const std::string &prefix) |
| 92 |
|
|
{} |
| 93 |
|
|
}; |
| 94 |
|
|
|
| 95 |
|
|
/****************************************************************************/ |
| 96 |
|
|
|
| 97 |
|
|
using IoLinkDeviceFactory = std::function<std::unique_ptr<IoLinkDevice>()>; |
| 98 |
|
|
|
| 99 |
|
|
/** One registered IO-Link driver's identity (its factory itself is not |
| 100 |
|
|
* exposed here -- see IoLinkDeviceRegistry::find()/createIoLinkDevice()). */ |
| 101 |
|
✗ |
struct IoLinkDriverDescriptor |
| 102 |
|
|
{ |
| 103 |
|
|
std::string name; // e.g. "ifm-SU6020" |
| 104 |
|
|
std::string sourceFile; // __FILE__ of the registering translation |
| 105 |
|
|
// unit, e.g. for a "see the source" link |
| 106 |
|
|
}; |
| 107 |
|
|
|
| 108 |
|
|
/** Process-wide table of known IO-Link port drivers, populated by |
| 109 |
|
|
* self-registration (see ECAPP_REGISTER_IOLINK_DEVICE) -- the same |
| 110 |
|
|
* pattern as the bus-level Registry in Factory.h, just scoped to what |
| 111 |
|
|
* can be plugged into an IO-Link master's port instead of what can be |
| 112 |
|
|
* plugged into the EtherCAT bus itself. */ |
| 113 |
|
8 |
class ECAPP_EXPORT IoLinkDeviceRegistry |
| 114 |
|
|
{ |
| 115 |
|
|
public: |
| 116 |
|
|
static IoLinkDeviceRegistry &instance(); |
| 117 |
|
|
|
| 118 |
|
|
void |
| 119 |
|
|
add(std::string name, |
| 120 |
|
|
std::string sourceFile, |
| 121 |
|
|
IoLinkDeviceFactory factory); |
| 122 |
|
|
|
| 123 |
|
|
/** Throws UnknownDevice if no driver is registered under this |
| 124 |
|
|
* name. */ |
| 125 |
|
|
const IoLinkDeviceFactory &find(const std::string &name) const; |
| 126 |
|
|
|
| 127 |
|
|
/** Every registered driver, in registration order. For |
| 128 |
|
|
* introspection (e.g. generating a list of supported devices) -- |
| 129 |
|
|
* createIoLinkDevice()/find() remain the way to actually |
| 130 |
|
|
* instantiate one. */ |
| 131 |
|
|
std::vector<IoLinkDriverDescriptor> all() const; |
| 132 |
|
|
|
| 133 |
|
|
private: |
| 134 |
|
8 |
IoLinkDeviceRegistry() = default; |
| 135 |
|
|
|
| 136 |
|
232 |
struct Entry |
| 137 |
|
|
{ |
| 138 |
|
|
std::string name; |
| 139 |
|
|
std::string sourceFile; |
| 140 |
|
|
IoLinkDeviceFactory factory; |
| 141 |
|
|
}; |
| 142 |
|
|
std::vector<Entry> factories; |
| 143 |
|
|
}; |
| 144 |
|
|
|
| 145 |
|
|
/** Throws UnknownDevice if no driver is registered under this name. */ |
| 146 |
|
|
ECAPP_EXPORT std::unique_ptr<IoLinkDevice> |
| 147 |
|
|
createIoLinkDevice(const std::string &name); |
| 148 |
|
|
|
| 149 |
|
|
/** Registers one IoLinkDeviceFactory as a static-init side effect; see |
| 150 |
|
|
* ECAPP_REGISTER_IOLINK_DEVICE. */ |
| 151 |
|
|
struct IoLinkRegistrar |
| 152 |
|
|
{ |
| 153 |
|
40 |
IoLinkRegistrar( |
| 154 |
|
|
std::string name, |
| 155 |
|
|
std::string sourceFile, |
| 156 |
|
|
IoLinkDeviceFactory factory) |
| 157 |
|
|
{ |
| 158 |
1/2
✓ Branch 5 taken 40 times.
✗ Branch 6 not taken.
|
160 |
IoLinkDeviceRegistry::instance().add( |
| 159 |
|
120 |
std::move(name), std::move(sourceFile), std::move(factory)); |
| 160 |
|
40 |
} |
| 161 |
|
|
}; |
| 162 |
|
|
|
| 163 |
|
|
} // namespace EcApp |
| 164 |
|
|
|
| 165 |
|
|
/****************************************************************************/ |
| 166 |
|
|
|
| 167 |
|
|
#define ECAPP_IOLINK_CONCAT_(a, b) a##b |
| 168 |
|
|
#define ECAPP_IOLINK_CONCAT(a, b) ECAPP_IOLINK_CONCAT_(a, b) |
| 169 |
|
|
|
| 170 |
|
|
/** Place at namespace scope in an IO-Link driver's .cpp to make it |
| 171 |
|
|
* discoverable via createIoLinkDevice(), e.g. |
| 172 |
|
|
* ECAPP_REGISTER_IOLINK_DEVICE("ifm-SU6020", IfmSU6020); */ |
| 173 |
|
|
#define ECAPP_REGISTER_IOLINK_DEVICE(name, Type) \ |
| 174 |
|
|
static const ::EcApp::IoLinkRegistrar ECAPP_IOLINK_CONCAT( \ |
| 175 |
|
|
ecappIoLinkRegistrar_, __LINE__)( \ |
| 176 |
|
|
name, __FILE__, [] { return std::make_unique<Type>(); }) |
| 177 |
|
|
|
| 178 |
|
|
#endif // ECAPP_IOLINKDEVICE_H |
| 179 |
|
|
|
| 180 |
|
|
/****************************************************************************/ |
| 181 |
|
|
|