GCC Code Coverage Report


Directory: ./
File: ecapp/IoLinkDevice.h
Date: 2026-09-03 16:05:57
Exec Total Coverage
Lines: 8 16 50.0%
Branches: 1 2 50.0%

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