Directory: | ./ |
---|---|
File: | pdserv/src/lib/Main.cpp |
Date: | 2025-07-20 04:11:05 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 340 | 478 | 71.1% |
Branches: | 193 | 553 | 34.9% |
Line | Branch | Exec | Source |
---|---|---|---|
1 | /***************************************************************************** | ||
2 | * | ||
3 | * Copyright 2010 Richard Hacker (lerichi at gmx dot net) | ||
4 | * 2023 Florian Pose <fp@igh.de> | ||
5 | * | ||
6 | * This file is part of the pdserv library. | ||
7 | * | ||
8 | * The pdserv library is free software: you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU Lesser General Public License as published | ||
10 | * by the Free Software Foundation, either version 3 of the License, or (at | ||
11 | * your option) any later version. | ||
12 | * | ||
13 | * The pdserv library is distributed in the hope that it will be useful, but | ||
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
15 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public | ||
16 | * License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU Lesser General Public License | ||
19 | * along with the pdserv library. If not, see <http://www.gnu.org/licenses/>. | ||
20 | * | ||
21 | *****************************************************************************/ | ||
22 | |||
23 | #include "Main.h" | ||
24 | |||
25 | #include "config.h" | ||
26 | #include "pdserv.h" | ||
27 | |||
28 | #include "Event.h" | ||
29 | #include "Parameter.h" | ||
30 | #include "Pointer.h" | ||
31 | #include "ShmemDataStructures.h" | ||
32 | #include "Signal.h" | ||
33 | #include "Task.h" | ||
34 | |||
35 | #include "../Config.h" | ||
36 | #include "../Database.h" | ||
37 | #include "../Debug.h" | ||
38 | #include "../Exceptions.h" | ||
39 | #include "../Session.h" | ||
40 | |||
41 | #include <cerrno> // EIO / errno | ||
42 | #include <cstdio> // perror() | ||
43 | #include <fstream> // ofstream | ||
44 | #include <iostream> | ||
45 | |||
46 | #include <pthread.h> | ||
47 | #include <signal.h> // signal() | ||
48 | #include <sys/mman.h> // mmap(), munmap() | ||
49 | #include <sys/time.h> // gettimeofday() | ||
50 | #include <sys/wait.h> | ||
51 | #include <unistd.h> // exit(), sleep() | ||
52 | #include <yaml.h> | ||
53 | |||
54 | #ifdef _PDSERV_CUSTOM_GCOV_HOOK | ||
55 | extern "C" void __gcov_dump(void); | ||
56 | #endif // _PDSERV_CUSTOM_GCOV_HOOK | ||
57 | |||
58 | ///////////////////////////////////////////////////////////////////////////// | ||
59 | struct SDO { | ||
60 | enum {ParamChange = 1, PollSignal} type; | ||
61 | |||
62 | union { | ||
63 | struct { | ||
64 | const Parameter *parameter; | ||
65 | unsigned int offset; | ||
66 | unsigned int count; | ||
67 | } paramChangeReq; | ||
68 | |||
69 | struct { | ||
70 | int rv; | ||
71 | struct timespec time; | ||
72 | } paramChangeAck; | ||
73 | |||
74 | const Signal* signal; | ||
75 | struct timespec time; | ||
76 | }; | ||
77 | }; | ||
78 | |||
79 | ///////////////////////////////////////////////////////////////////////////// | ||
80 | const double Main::bufferTime = 2.0; | ||
81 | |||
82 | ///////////////////////////////////////////////////////////////////////////// | ||
83 | ///////////////////////////////////////////////////////////////////////////// | ||
84 | 157 | Main::Main( const char *name, const char *version, | |
85 | 157 | int (*gettime)(struct timespec*)): | |
86 | PdServ::Main(name, version), | ||
87 |
1/2✓ Branch 3 taken 157 times.
✗ Branch 4 not taken.
|
314 | pthread::Thread(std::string("pdserv-main")), |
88 |
8/16✓ Branch 5 taken 157 times.
✗ Branch 6 not taken.
✓ Branch 12 taken 157 times.
✗ Branch 13 not taken.
✓ Branch 16 taken 157 times.
✗ Branch 17 not taken.
✓ Branch 30 taken 157 times.
✗ Branch 31 not taken.
✓ Branch 44 taken 157 times.
✗ Branch 45 not taken.
✓ Branch 62 taken 157 times.
✗ Branch 63 not taken.
✓ Branch 72 taken 157 times.
✗ Branch 73 not taken.
✓ Branch 74 taken 157 times.
✗ Branch 75 not taken.
|
471 | rttime(gettime ? gettime : &PdServ::Main::localtime) |
89 | 157 | {} | |
90 | |||
91 | ///////////////////////////////////////////////////////////////////////////// | ||
92 | 471 | Main::~Main() | |
93 | { | ||
94 | 157 | int cancel_state; | |
95 | 157 | pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state); | |
96 | |||
97 | 157 | terminate(); | |
98 | 157 | join(); | |
99 | |||
100 | |||
101 |
2/2✓ Branch 2 taken 314 times.
✓ Branch 3 taken 157 times.
|
785 | while (task.size()) { |
102 |
1/2✓ Branch 3 taken 314 times.
✗ Branch 4 not taken.
|
314 | delete task.front(); |
103 | 314 | task.pop_front(); | |
104 | } | ||
105 | |||
106 |
2/2✓ Branch 2 taken 942 times.
✓ Branch 3 taken 157 times.
|
942 | while (parameters.size()) { |
107 |
1/2✓ Branch 3 taken 942 times.
✗ Branch 4 not taken.
|
942 | delete parameters.front(); |
108 | 942 | parameters.pop_front(); | |
109 | } | ||
110 | |||
111 | // close pipes | ||
112 | 157 | ::close(terminatePipe); | |
113 | 157 | ::close(ipcTx); | |
114 | 157 | ::close(ipcRx); | |
115 | 157 | ::close(nrtFeedbackPipe); | |
116 | |||
117 |
2/2✓ Branch 3 taken 155 times.
✓ Branch 4 taken 2 times.
|
157 | if (pid > 0) |
118 | { | ||
119 | 155 | int stat = 0; | |
120 | 155 | waitpid(pid, &stat, 0); | |
121 | } | ||
122 | |||
123 |
2/2✓ Branch 3 taken 155 times.
✓ Branch 4 taken 2 times.
|
157 | if (shmem) |
124 | 155 | ::munmap(shmem, shmem_len); | |
125 | 157 | pthread_setcancelstate(cancel_state, nullptr); | |
126 | 314 | } | |
127 | |||
128 | ///////////////////////////////////////////////////////////////////////////// | ||
129 | 149 | void Main::setConfigFile(const char *file) | |
130 | { | ||
131 | 149 | configFile = file; | |
132 | 149 | } | |
133 | |||
134 | ///////////////////////////////////////////////////////////////////////////// | ||
135 | 157 | void Main::setParameterWriteLock(void (*fn)(int, void*), void* priv_data) | |
136 | { | ||
137 | 157 | writelock_cb = fn; | |
138 | 157 | writelock_data = priv_data; | |
139 | 157 | } | |
140 | |||
141 | ///////////////////////////////////////////////////////////////////////////// | ||
142 | 156 | int Main::setup() | |
143 | { | ||
144 | 156 | int rv; | |
145 | 156 | int ipc_pipe[4][2]; | |
146 | time_t persistTimeout; | ||
147 | 156 | fd_set fds; | |
148 | 156 | struct timeval timeout, now, delay; | |
149 | 156 | struct EventData* eventData = nullptr; | |
150 | |||
151 |
1/2✓ Branch 4 taken 156 times.
✗ Branch 5 not taken.
|
156 | rv = readConfiguration(); |
152 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 155 times.
|
156 | if (rv) |
153 | 1 | return rv; | |
154 |
1/2✓ Branch 5 taken 155 times.
✗ Branch 6 not taken.
|
155 | setupLogging(configFile); |
155 |
1/2✓ Branch 4 taken 155 times.
✗ Branch 5 not taken.
|
155 | persistTimeout = setupPersistent(); |
156 | |||
157 |
3/6✓ Branch 3 taken 155 times.
✗ Branch 4 not taken.
✓ Branch 9 taken 155 times.
✗ Branch 10 not taken.
✗ Branch 27 not taken.
✓ Branch 28 taken 5 times.
|
310 | auto exportPath {m_config["eventexport"].toString()}; |
158 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 155 times.
|
155 | if (not exportPath.empty()) { |
159 | ✗ | exportEvents(exportPath.c_str()); | |
160 | } | ||
161 | |||
162 | // Initialize library | ||
163 |
1/2✓ Branch 4 taken 155 times.
✗ Branch 5 not taken.
|
155 | rv = prefork_init(); |
164 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 155 times.
|
155 | if (rv) |
165 | ✗ | return rv; | |
166 | |||
167 | // Open a pipe between the two processes. This is used to inform the | ||
168 | // child that the parent has died | ||
169 |
3/6✓ Branch 2 taken 155 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 155 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 155 times.
|
465 | if (::pipe(ipc_pipe[0]) or ::pipe(ipc_pipe[1]) or ::pipe(ipc_pipe[2]) |
170 |
2/4✓ Branch 0 taken 155 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 155 times.
|
310 | or ::pipe(ipc_pipe[3])) { |
171 | ✗ | rv = errno; | |
172 | ✗ | ::perror("pipe()"); | |
173 | ✗ | return rv; | |
174 | } | ||
175 | |||
176 | // Immediately split off a child. The parent returns to the caller so | ||
177 | // that he can get on with his job. | ||
178 | // | ||
179 | // The child continues from here. | ||
180 | // | ||
181 | // It is intentional that the child has the same process group as | ||
182 | // the parent so that it gets all the signals too. | ||
183 | 155 | pid = ::fork(); | |
184 |
1/2✗ Branch 3 not taken.
✓ Branch 4 taken 309 times.
|
309 | if (pid < 0) { |
185 | // Some error occurred | ||
186 | ✗ | ::perror("fork()"); | |
187 | ✗ | return errno; | |
188 | } | ||
189 |
2/2✓ Branch 3 taken 155 times.
✓ Branch 4 taken 154 times.
|
309 | else if (pid) { |
190 | // Parent here. Return to the caller | ||
191 |
1/2✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
|
155 | ::close(ipc_pipe[0][0]); |
192 | 155 | ipcTx = ipc_pipe[0][1]; | |
193 | |||
194 | 155 | ipcRx = ipc_pipe[1][0]; | |
195 |
1/2✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
|
155 | ::close(ipc_pipe[1][1]); |
196 | |||
197 |
1/2✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
|
155 | ::close(ipc_pipe[2][0]); |
198 | 155 | terminatePipe = ipc_pipe[2][1]; | |
199 | |||
200 | 155 | nrtFeedbackPipe = ipc_pipe[3][0]; | |
201 |
1/2✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
|
155 | ::close(ipc_pipe[3][1]); |
202 | |||
203 | // Send PID to the child, indicating that parent is running | ||
204 |
2/4✓ Branch 4 taken 155 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 155 times.
|
155 | if (::write(terminatePipe, &pid, sizeof(pid)) != sizeof(pid)) { |
205 | ✗ | perror("Main::setup(): pid ::write() failed"); | |
206 | ✗ | return errno; | |
207 | } | ||
208 | // waiting for child to initialize | ||
209 |
2/4✓ Branch 4 taken 155 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 155 times.
|
155 | if (::read(nrtFeedbackPipe, &rv, sizeof(rv)) != sizeof(rv)) { |
210 | ✗ | rv = errno; | |
211 | ✗ | perror("Main::setup(): nRT setup: read() failed"); | |
212 | ✗ | ::close(nrtFeedbackPipe); | |
213 | ✗ | nrtFeedbackPipe = -1; | |
214 | ✗ | return rv; | |
215 | } | ||
216 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 150 times.
|
155 | if (rv != 0) { |
217 | // child init has failed, it will terminate itself | ||
218 |
1/2✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
|
5 | ::close(nrtFeedbackPipe); |
219 | 5 | nrtFeedbackPipe = -1; | |
220 | 5 | return rv; | |
221 | } | ||
222 | |||
223 |
1/2✓ Branch 4 taken 150 times.
✗ Branch 5 not taken.
|
150 | return postfork_rt_setup(); |
224 | } | ||
225 | |||
226 | // Only child runs after this point | ||
227 | 154 | ipcRx = ipc_pipe[0][0]; | |
228 |
1/2✓ Branch 1 taken 154 times.
✗ Branch 2 not taken.
|
154 | ::close(ipc_pipe[0][1]); |
229 | |||
230 |
1/2✓ Branch 1 taken 154 times.
✗ Branch 2 not taken.
|
154 | ::close(ipc_pipe[1][0]); |
231 | 154 | ipcTx = ipc_pipe[1][1]; | |
232 | |||
233 | 154 | terminatePipe = ipc_pipe[2][0]; | |
234 |
1/2✓ Branch 1 taken 154 times.
✗ Branch 2 not taken.
|
154 | ::close(ipc_pipe[2][1]); |
235 | |||
236 |
1/2✓ Branch 1 taken 154 times.
✗ Branch 2 not taken.
|
154 | ::close(ipc_pipe[3][0]); |
237 | 154 | nrtFeedbackPipe = ipc_pipe[3][1]; | |
238 | |||
239 | |||
240 | // Wait till main thread has been initialized | ||
241 |
2/4✓ Branch 4 taken 154 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 154 times.
|
308 | if (::read(terminatePipe, &pid, sizeof(pid)) != sizeof(pid) |
242 |
2/4✓ Branch 0 taken 154 times.
✗ Branch 1 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 154 times.
|
154 | or pid != getpid()) { |
243 | ✗ | perror("Main::setup(): pid ::read() failed"); | |
244 | ✗ | std::quick_exit(errno); | |
245 | } | ||
246 | |||
247 | // Ignore common terminating signals | ||
248 | 154 | ::signal(SIGINT, SIG_IGN); | |
249 | 154 | ::signal(SIGTERM, SIG_IGN); | |
250 | |||
251 |
1/2✓ Branch 4 taken 154 times.
✗ Branch 5 not taken.
|
154 | postfork_nrt_setup(); |
252 | |||
253 | 154 | rv = 0; | |
254 | try { | ||
255 |
2/2✓ Branch 4 taken 149 times.
✓ Branch 5 taken 5 times.
|
154 | startServers(); |
256 | } | ||
257 |
1/4✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
10 | catch (PdServ::Errno const & err) { |
258 | 5 | rv = -err.getErrno(); | |
259 | } | ||
260 | |||
261 |
2/4✓ Branch 4 taken 154 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 154 times.
|
154 | if (::write(nrtFeedbackPipe, &rv, sizeof(rv)) != sizeof(rv)) { |
262 | ✗ | perror("Main::setup(): nRT setup: write() failed"); | |
263 | ✗ | close(nrtFeedbackPipe); | |
264 | ✗ | std::quick_exit(errno); | |
265 | } | ||
266 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 154 times.
|
154 | if (rv == -1) |
267 | { | ||
268 | ✗ | close(nrtFeedbackPipe); | |
269 | #ifdef _PDSERV_CUSTOM_GCOV_HOOK | ||
270 | ✗ | __gcov_dump(); | |
271 | #endif | ||
272 | ✗ | std::quick_exit(-1); | |
273 | } | ||
274 | |||
275 | try { | ||
276 |
1/2✓ Branch 4 taken 154 times.
✗ Branch 5 not taken.
|
154 | postfork_nrt_subscribe_persistent_signals(); |
277 | ✗ | } catch (PdServ::Main::RtProcessExited&) { | |
278 | ✗ | goto exit; | |
279 | } | ||
280 | |||
281 | 154 | ::gettimeofday(&timeout, 0); | |
282 | 154 | timeout.tv_sec += persistTimeout; | |
283 | |||
284 | 154 | FD_ZERO(&fds); | |
285 | |||
286 | // Stay in this loop until real-time thread exits, in which case | ||
287 | // ipc_pipe[0] becomes readable | ||
288 | 154 | eventData = eventDataStart; | |
289 | 154 | ipc_error = false; | |
290 | 723 | do { | |
291 | try { | ||
292 |
0/2✗ Branch 7 not taken.
✗ Branch 8 not taken.
|
2631 | for (TaskList::iterator it = task.begin(); |
293 |
2/2✓ Branch 6 taken 1754 times.
✓ Branch 7 taken 877 times.
|
2631 | it != task.end(); ++it) |
294 |
1/2✓ Branch 6 taken 1754 times.
✗ Branch 7 not taken.
|
1754 | static_cast<Task*>(*it)->nrt_update(); |
295 | ✗ | } catch (RtProcessExited&) { | |
296 | ✗ | rv = 0; | |
297 | ✗ | break; | |
298 | } | ||
299 | |||
300 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 877 times.
|
877 | if (persistTimeout) { |
301 | ✗ | if (::gettimeofday(&now, 0)) { | |
302 | ✗ | rv = errno; | |
303 | ✗ | break; | |
304 | } | ||
305 | |||
306 | ✗ | if ( now.tv_sec >= timeout.tv_sec) { | |
307 | ✗ | timeout.tv_sec += persistTimeout; | |
308 | ✗ | savePersistent(); | |
309 | } | ||
310 | } | ||
311 | |||
312 |
2/2✓ Branch 4 taken 27 times.
✓ Branch 5 taken 877 times.
|
931 | while (eventData != *eventDataWp) { |
313 |
1/2✓ Branch 7 taken 27 times.
✗ Branch 8 not taken.
|
54 | newEvent(eventData->event, eventData->index, |
314 | 27 | eventData->state, &eventData->time); | |
315 |
1/2✗ Branch 3 not taken.
✓ Branch 4 taken 27 times.
|
27 | if (++eventData == eventDataEnd) |
316 | ✗ | eventData = eventDataStart; | |
317 | } | ||
318 | |||
319 |
1/2✗ Branch 3 not taken.
✓ Branch 4 taken 877 times.
|
877 | FD_SET(terminatePipe, &fds); |
320 | 877 | delay.tv_sec = 0; | |
321 | 877 | delay.tv_usec = 50000; // 20Hz | |
322 |
1/2✓ Branch 4 taken 877 times.
✗ Branch 5 not taken.
|
877 | rv = ::select(terminatePipe + 1, &fds, 0, 0, &delay); |
323 |
4/6✓ Branch 0 taken 723 times.
✓ Branch 1 taken 154 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 723 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 723 times.
|
877 | } while (!(rv or ipc_error)); |
324 | |||
325 | 154 | exit: | |
326 | // Ignore rv if ipc_pipe[0] is readable | ||
327 |
1/2✓ Branch 0 taken 154 times.
✗ Branch 1 not taken.
|
154 | if (rv == 1) |
328 | 154 | rv = 0; | |
329 | |||
330 |
1/2✓ Branch 4 taken 154 times.
✗ Branch 5 not taken.
|
154 | stopServers(); |
331 | |||
332 |
1/2✓ Branch 4 taken 154 times.
✗ Branch 5 not taken.
|
154 | close(nrtFeedbackPipe); |
333 | #ifdef _PDSERV_CUSTOM_GCOV_HOOK | ||
334 |
0/2✗ Branch 1 not taken.
✗ Branch 2 not taken.
|
154 | __gcov_dump(); |
335 | #endif | ||
336 | ✗ | std::quick_exit(rv); | |
337 | } | ||
338 | |||
339 | ///////////////////////////////////////////////////////////////////////////// | ||
340 | |||
341 | 3 | void Main::sleep(int msecs) const | |
342 | { | ||
343 | 3 | fd_set fds; | |
344 | 3 | FD_ZERO(&fds); | |
345 |
1/2✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
|
3 | FD_SET(terminatePipe, &fds); |
346 | |||
347 | 3 | struct timeval delay; | |
348 | |||
349 | 3 | delay.tv_sec = msecs / 1000; | |
350 | 3 | delay.tv_usec = (msecs - delay.tv_sec * 1000) * 1000; | |
351 | |||
352 |
1/2✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
|
3 | const int rv = ::select(terminatePipe + 1, &fds, 0, 0, &delay); |
353 |
2/6✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
3 | if (rv == 1 or (rv == -1 && errno != EINTR)) { |
354 | ✗ | throw PdServ::Main::RtProcessExited{}; | |
355 | } | ||
356 | 3 | } | |
357 | |||
358 | ///////////////////////////////////////////////////////////////////////////// | ||
359 | 156 | int Main::readConfiguration() | |
360 | { | ||
361 | const char *env; | ||
362 | 156 | const char *err = 0; | |
363 | |||
364 | // Load custom configuration file | ||
365 |
2/2✓ Branch 2 taken 148 times.
✓ Branch 3 taken 8 times.
|
156 | if (!configFile.empty()) { |
366 | 148 | err = m_config.load(configFile.c_str()); | |
367 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 147 times.
|
148 | if (err) |
368 | std::cerr | ||
369 | << "Error loading configuration file " | ||
370 | << configFile << " specified on command line: " | ||
371 | 1 | << err << std::endl; | |
372 | else { | ||
373 | log_debug("Loaded specified configuration file %s", | ||
374 | configFile.c_str()); | ||
375 | } | ||
376 | } | ||
377 |
3/8✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 8 times.
|
8 | else if ((env = ::getenv("PDSERV_CONFIG")) and ::strlen(env)) { |
378 | // Try to load environment configuration file | ||
379 | ✗ | err = m_config.load(env); | |
380 | |||
381 | ✗ | if (err) | |
382 | std::cerr << "Error loading configuration file " << env | ||
383 | << " specified in environment variable PDSERV_CONFIG: " | ||
384 | ✗ | << err << std::endl; | |
385 | else { | ||
386 | ✗ | configFile = env; | |
387 | log_debug("Loaded ENV config %s", env); | ||
388 | } | ||
389 | } | ||
390 | else { | ||
391 | // Try to load default configuration file | ||
392 | 8 | const char *f = QUOTE(SYSCONFDIR) "/pdserv.conf"; | |
393 | |||
394 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
|
8 | if (::access(f, R_OK) == 0) { |
395 | // file exists | ||
396 | 8 | err = m_config.load(f); | |
397 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
|
8 | if (err) { |
398 | std::cerr << "Error loading default configuration file " | ||
399 | ✗ | << f << ": " << err << std::endl; | |
400 | } | ||
401 | else { | ||
402 | 8 | configFile = f; | |
403 | log_debug("Loaded default configuration file %s", f); | ||
404 | } | ||
405 | } | ||
406 | else { | ||
407 | std::cerr << "No configuration file found at " << f | ||
408 | ✗ | << ". Using defaults." << std::endl; | |
409 | } | ||
410 | } | ||
411 | |||
412 | 156 | if (!m_config) { | |
413 | log_debug("No configuration loaded"); | ||
414 | } | ||
415 | |||
416 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 155 times.
|
156 | if (err) { |
417 | 1 | return -1; | |
418 | } | ||
419 | |||
420 | 155 | return 0; | |
421 | } | ||
422 | |||
423 | ///////////////////////////////////////////////////////////////////////////// | ||
424 | 1314 | PdServ::Config Main::config(const char* key) const | |
425 | { | ||
426 | 1314 | return m_config[key]; | |
427 | } | ||
428 | |||
429 | ///////////////////////////////////////////////////////////////////////////// | ||
430 | 314 | Task* Main::addTask(double sampleTime, const char *name) | |
431 | { | ||
432 |
2/4✓ Branch 7 taken 314 times.
✗ Branch 8 not taken.
✓ Branch 11 taken 314 times.
✗ Branch 12 not taken.
|
314 | task.push_back(new Task(this, task.size(), sampleTime, name)); |
433 | 314 | return task.back(); | |
434 | } | ||
435 | |||
436 | ///////////////////////////////////////////////////////////////////////////// | ||
437 | 211 | int Main::gettime(struct timespec* t) const | |
438 | { | ||
439 | 211 | return rttime(t); | |
440 | } | ||
441 | |||
442 | ///////////////////////////////////////////////////////////////////////////// | ||
443 | 314 | Event* Main::addEvent ( | |
444 | const char *path, size_t nelem, const char * const *messages) | ||
445 | { | ||
446 |
2/4✓ Branch 5 taken 314 times.
✗ Branch 6 not taken.
✓ Branch 10 taken 314 times.
✗ Branch 11 not taken.
|
314 | events.push_back(std::unique_ptr<Event>{new Event(this, path, nelem, messages)}); |
447 | |||
448 | 314 | return events.back().get(); | |
449 | } | ||
450 | |||
451 | ///////////////////////////////////////////////////////////////////////////// | ||
452 | 942 | Parameter* Main::addParameter( const char *path, | |
453 | unsigned int mode, const PdServ::DataType& datatype, | ||
454 | void *addr, size_t n, const size_t *dim) | ||
455 | { | ||
456 |
1/2✓ Branch 4 taken 942 times.
✗ Branch 5 not taken.
|
1884 | parameters.push_back( |
457 |
1/2✓ Branch 3 taken 942 times.
✗ Branch 4 not taken.
|
1884 | new Parameter(this, addr, path, mode, datatype, n, dim)); |
458 | |||
459 | 942 | return parameters.back(); | |
460 | } | ||
461 | |||
462 | ///////////////////////////////////////////////////////////////////////////// | ||
463 | 16 | void Main::setEvent(const Event* event, | |
464 | size_t element, PdServ::Event::Priority prio, const timespec *time) | ||
465 | { | ||
466 | 32 | pthread::MutexLock lock(eventMutex); | |
467 | |||
468 | 16 | struct EventData *eventData = *eventDataWp; | |
469 | 16 | eventData->event = event; | |
470 | 16 | eventData->index = element; | |
471 | 16 | eventData->state = prio; | |
472 | 16 | eventData->time = *time; | |
473 | |||
474 |
1/2✗ Branch 3 not taken.
✓ Branch 4 taken 16 times.
|
16 | if (++eventData == eventDataEnd) |
475 | ✗ | eventData = eventDataStart; | |
476 | 16 | *eventDataWp = eventData; | |
477 | 16 | } | |
478 | |||
479 | ///////////////////////////////////////////////////////////////////////////// | ||
480 | 11 | void Main::resetEvent(const Event* event, | |
481 | size_t element, const timespec *time) | ||
482 | { | ||
483 | 22 | pthread::MutexLock lock(eventMutex); | |
484 | |||
485 | 11 | struct EventData *eventData = *eventDataWp; | |
486 | 11 | eventData->event = event; | |
487 | 11 | eventData->index = element; | |
488 | 11 | eventData->state = -1; | |
489 | 11 | eventData->time = *time; | |
490 | |||
491 |
1/2✗ Branch 3 not taken.
✓ Branch 4 taken 11 times.
|
11 | if (++eventData == eventDataEnd) |
492 | ✗ | eventData = eventDataStart; | |
493 | 11 | *eventDataWp = eventData; | |
494 | 11 | } | |
495 | |||
496 | ///////////////////////////////////////////////////////////////////////////// | ||
497 | ✗ | static int yaml_output_handler( | |
498 | void *data, | ||
499 | unsigned char *buffer, | ||
500 | size_t size) | ||
501 | { | ||
502 | ✗ | std::ostream* out = static_cast<std::ostream *>(data); | |
503 | ✗ | out->write(reinterpret_cast<char *>(buffer), size); | |
504 | ✗ | return out->good() ? 1 : 0; | |
505 | } | ||
506 | |||
507 | ///////////////////////////////////////////////////////////////////////////// | ||
508 | ✗ | static int emit_helper(yaml_emitter_t *emitter, yaml_event_t *event, int line) | |
509 | { | ||
510 | ✗ | int ret = yaml_emitter_emit(emitter, event); | |
511 | if (not ret) { | ||
512 | log_debug("YAML emit error in line %u: %s", line, emitter->problem); | ||
513 | } | ||
514 | ✗ | return ret; | |
515 | } | ||
516 | #define emit(emitter, event) \ | ||
517 | if (not emit_helper(emitter, event, __LINE__)) goto out_error; | ||
518 | |||
519 | ///////////////////////////////////////////////////////////////////////////// | ||
520 | ✗ | int Main::exportEvents(const char *path) const | |
521 | { | ||
522 | ✗ | int success {0}; | |
523 | log_debug("Exporting events to %s", path); | ||
524 | |||
525 | ✗ | std::ofstream ofs(path); | |
526 | ✗ | if (not ofs) { | |
527 | ✗ | std::cerr << "Failed to open file.\n"; | |
528 | log_debug("Failed to open %s", path); | ||
529 | ✗ | return -EIO; | |
530 | } | ||
531 | |||
532 | ✗ | yaml_emitter_t emitter; | |
533 | ✗ | yaml_event_t event; | |
534 | |||
535 | ✗ | if (not yaml_emitter_initialize(&emitter)) { | |
536 | log_debug("Failed to initialize YAML emitter."); | ||
537 | ✗ | return -EIO; | |
538 | } | ||
539 | ✗ | yaml_emitter_set_output(&emitter, yaml_output_handler, &ofs); | |
540 | |||
541 | // Start stream | ||
542 | ✗ | yaml_stream_start_event_initialize(&event, YAML_UTF8_ENCODING); | |
543 | ✗ | emit(&emitter, &event); | |
544 | |||
545 | // Start document | ||
546 | ✗ | yaml_document_start_event_initialize(&event, NULL, NULL, NULL, 0); | |
547 | ✗ | emit(&emitter, &event); | |
548 | |||
549 | // Start Mapping | ||
550 | ✗ | yaml_mapping_start_event_initialize(&event, NULL, NULL, 1, | |
551 | YAML_BLOCK_MAPPING_STYLE); | ||
552 | ✗ | emit(&emitter, &event); | |
553 | |||
554 | // Version | ||
555 | ✗ | yaml_scalar_event_initialize(&event, NULL, NULL, | |
556 | (yaml_char_t *) "version", -1, 1, 0, YAML_PLAIN_SCALAR_STYLE); | ||
557 | ✗ | emit(&emitter, &event); | |
558 | |||
559 | ✗ | yaml_scalar_event_initialize(&event, NULL, NULL, | |
560 | (yaml_char_t *) "1", | ||
561 | -1, 1, 0, YAML_PLAIN_SCALAR_STYLE); | ||
562 | ✗ | emit(&emitter, &event); | |
563 | |||
564 | // Events | ||
565 | ✗ | yaml_scalar_event_initialize(&event, NULL, NULL, | |
566 | (yaml_char_t *) "events", -1, 1, 0, YAML_PLAIN_SCALAR_STYLE); | ||
567 | ✗ | emit(&emitter, &event); | |
568 | |||
569 | // Start sequence with events | ||
570 | ✗ | yaml_sequence_start_event_initialize(&event, NULL, NULL, 1, | |
571 | YAML_BLOCK_SEQUENCE_STYLE); | ||
572 | ✗ | emit(&emitter, &event); | |
573 | |||
574 | ✗ | for (auto pdserv_event : getEvents()) { | |
575 | ✗ | yaml_mapping_start_event_initialize(&event, NULL, NULL, 1, | |
576 | YAML_BLOCK_MAPPING_STYLE); | ||
577 | ✗ | emit(&emitter, &event); | |
578 | |||
579 | ✗ | yaml_scalar_event_initialize(&event, NULL, NULL, | |
580 | (yaml_char_t *) "path", -1, 1, 0, YAML_PLAIN_SCALAR_STYLE); | ||
581 | ✗ | emit(&emitter, &event); | |
582 | |||
583 | ✗ | yaml_scalar_event_initialize(&event, NULL, NULL, | |
584 | ✗ | (yaml_char_t *) pdserv_event->path.c_str(), | |
585 | -1, 1, 0, YAML_PLAIN_SCALAR_STYLE); | ||
586 | ✗ | emit(&emitter, &event); | |
587 | |||
588 | ✗ | yaml_scalar_event_initialize(&event, NULL, NULL, | |
589 | (yaml_char_t *) "nelem", -1, 1, 0, YAML_PLAIN_SCALAR_STYLE); | ||
590 | ✗ | emit(&emitter, &event); | |
591 | |||
592 | ✗ | std::string nelem { std::to_string(pdserv_event->nelem) }; | |
593 | ✗ | yaml_scalar_event_initialize(&event, NULL, NULL, | |
594 | ✗ | (yaml_char_t *) nelem.c_str(), | |
595 | -1, 1, 0, YAML_PLAIN_SCALAR_STYLE); | ||
596 | ✗ | emit(&emitter, &event); | |
597 | |||
598 | ✗ | if (pdserv_event->message) { | |
599 | ✗ | yaml_scalar_event_initialize(&event, NULL, NULL, | |
600 | (yaml_char_t *) "messages", -1, 1, 0, | ||
601 | YAML_PLAIN_SCALAR_STYLE); | ||
602 | ✗ | emit(&emitter, &event); | |
603 | |||
604 | // Start sequence with messages | ||
605 | ✗ | yaml_sequence_start_event_initialize(&event, NULL, NULL, 1, | |
606 | YAML_BLOCK_SEQUENCE_STYLE); | ||
607 | ✗ | emit(&emitter, &event); | |
608 | |||
609 | ✗ | for (unsigned int i = 0; i < pdserv_event->nelem; i++) { | |
610 | ✗ | yaml_scalar_event_initialize(&event, NULL, NULL, | |
611 | ✗ | (yaml_char_t *) pdserv_event->message[i], | |
612 | -1, 1, 0, YAML_PLAIN_SCALAR_STYLE); | ||
613 | ✗ | emit(&emitter, &event); | |
614 | } | ||
615 | |||
616 | // End sequence | ||
617 | ✗ | yaml_sequence_end_event_initialize(&event); | |
618 | ✗ | emit(&emitter, &event); | |
619 | } | ||
620 | |||
621 | ✗ | yaml_mapping_end_event_initialize(&event); | |
622 | ✗ | emit(&emitter, &event); | |
623 | } | ||
624 | |||
625 | // End sequence | ||
626 | ✗ | yaml_sequence_end_event_initialize(&event); | |
627 | ✗ | emit(&emitter, &event); | |
628 | |||
629 | // End mapping | ||
630 | ✗ | yaml_mapping_end_event_initialize(&event); | |
631 | ✗ | emit(&emitter, &event); | |
632 | |||
633 | // End document | ||
634 | ✗ | yaml_document_end_event_initialize(&event, 1); | |
635 | ✗ | emit(&emitter, &event); | |
636 | |||
637 | // End stream | ||
638 | ✗ | yaml_stream_end_event_initialize(&event); | |
639 | ✗ | emit(&emitter, &event); | |
640 | |||
641 | ✗ | success = 1; | |
642 | |||
643 | ✗ | out_error: | |
644 | ✗ | yaml_emitter_delete(&emitter); | |
645 | ✗ | ofs.close(); | |
646 | |||
647 | ✗ | if (not ofs or not success) { | |
648 | log_debug("Failed to write all YAML data."); | ||
649 | ✗ | return -EIO; | |
650 | } | ||
651 | |||
652 | ✗ | return 0; | |
653 | } | ||
654 | |||
655 | ///////////////////////////////////////////////////////////////////////////// | ||
656 | 43 | int Main::setValue(const PdServ::ProcessParameter* p, | |
657 | const char* buf, size_t offset, size_t count, | ||
658 | const char** value, const struct timespec** time) | ||
659 | { | ||
660 | 86 | pthread::MutexLock lock(sdoMutex); | |
661 |
1/2✓ Branch 0 taken 43 times.
✗ Branch 1 not taken.
|
43 | const Parameter* param = static_cast<const Parameter*>(p); |
662 | 43 | char* shmAddr = param->shmAddr + offset; | |
663 | |||
664 | // Backup old values in case of write failure | ||
665 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 43 times.
|
43 | char backup[count]; |
666 |
1/2✓ Branch 1 taken 43 times.
✗ Branch 2 not taken.
|
43 | std::copy(shmAddr, shmAddr + count, backup); |
667 | |||
668 | // Copy new data to shared memory | ||
669 |
1/2✓ Branch 1 taken 43 times.
✗ Branch 2 not taken.
|
43 | std::copy(buf, buf + count, shmAddr); |
670 | |||
671 | // Setup change request | ||
672 | 43 | struct SDO sdo; | |
673 | 43 | sdo.type = SDO::ParamChange; | |
674 | 43 | sdo.paramChangeReq.parameter = param; | |
675 | 43 | sdo.paramChangeReq.offset = offset; | |
676 | 43 | sdo.paramChangeReq.count = count; | |
677 | |||
678 |
2/4✓ Branch 4 taken 43 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 43 times.
|
43 | if (::write(ipcTx, &sdo, sizeof(sdo)) != sizeof(sdo)) { |
679 | log_debug("Main::setValue(): SDO ::write() failed"); | ||
680 | ✗ | ipc_error = true; | |
681 | ✗ | return -EIO; | |
682 | } | ||
683 | |||
684 |
2/4✓ Branch 4 taken 43 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 43 times.
|
43 | if (::read(ipcRx, &sdo, sizeof(sdo)) != sizeof(sdo)) { |
685 | log_debug("Main::setValue(): SDO ::read() failed"); | ||
686 | ✗ | ipc_error = true; | |
687 | ✗ | return -EIO; | |
688 | } | ||
689 | |||
690 |
1/2✓ Branch 0 taken 43 times.
✗ Branch 1 not taken.
|
43 | if (!sdo.paramChangeAck.rv) |
691 | 43 | param->mtime = sdo.paramChangeAck.time; // Save time of update | |
692 | else | ||
693 | // Write failure. Restore data | ||
694 | ✗ | std::copy(backup, backup + count, shmAddr); | |
695 | |||
696 | 43 | *value = param->shmAddr; | |
697 | 43 | *time = ¶m->mtime; | |
698 | |||
699 | 86 | return sdo.paramChangeAck.rv; | |
700 | } | ||
701 | |||
702 | ///////////////////////////////////////////////////////////////////////////// | ||
703 | 5 | void Main::initializeParameter(PdServ::Parameter* p, | |
704 | const char* data, const struct timespec* mtime, | ||
705 | const PdServ::Signal* s) | ||
706 | { | ||
707 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
|
5 | if (data) { |
708 | log_debug("Restoring %s", p->path.c_str()); | ||
709 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | const Parameter *parameter = static_cast<const Parameter*>(p); |
710 | 4 | std::copy(data, data + parameter->memSize, parameter->addr); | |
711 | 4 | parameter->mtime = *mtime; | |
712 | } | ||
713 | |||
714 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
|
5 | if (s) { |
715 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | const Signal* signal = static_cast<const Signal*>(s); |
716 | 4 | signal->task->makePersistent(signal); | |
717 | } | ||
718 | 5 | } | |
719 | |||
720 | ///////////////////////////////////////////////////////////////////////////// | ||
721 | 3 | bool Main::getPersistentSignalValue(const PdServ::Signal *s, | |
722 | char* buf, struct timespec* time) | ||
723 | { | ||
724 | 3 | const struct timespec* t; | |
725 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | bool rv = static_cast<const Signal*>(s)->task->getPersistentValue( |
726 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | s, buf, &t); |
727 | |||
728 |
2/4✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
|
3 | if (rv and time) |
729 | 3 | *time = *t; | |
730 | |||
731 | 3 | return rv; | |
732 | } | ||
733 | |||
734 | ///////////////////////////////////////////////////////////////////////////// | ||
735 | 57 | int Main::getValue(const Signal *signal, void* dest, struct timespec* time) | |
736 | { | ||
737 | 114 | pthread::MutexLock lock(sdoMutex); | |
738 | 57 | struct SDO sdo; | |
739 | |||
740 | 57 | sdo.type = SDO::PollSignal; | |
741 | 57 | sdo.signal = signal; | |
742 | |||
743 |
2/4✓ Branch 4 taken 57 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 57 times.
|
57 | if (::write(ipcTx, &sdo, sizeof(sdo)) != sizeof(sdo)) { |
744 | log_debug("Main::getValue(): SDO ::write() failed"); | ||
745 | ✗ | ipc_error = true; | |
746 | ✗ | return -EIO; | |
747 | } | ||
748 | |||
749 |
2/4✓ Branch 4 taken 57 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 57 times.
|
57 | if (::read(ipcRx, &sdo, sizeof(sdo)) != sizeof(sdo)) { |
750 | log_debug("Main::getValue(): SDO ::read() failed"); | ||
751 | ✗ | ipc_error = true; | |
752 | ✗ | return -EIO; | |
753 | } | ||
754 | |||
755 |
1/2✓ Branch 10 taken 57 times.
✗ Branch 11 not taken.
|
57 | std::copy(signalData, signalData + signal->memSize, |
756 | reinterpret_cast<char*>(dest)); | ||
757 | |||
758 |
1/2✓ Branch 0 taken 57 times.
✗ Branch 1 not taken.
|
57 | if (time) |
759 | 57 | *time = sdo.time; | |
760 | |||
761 | 57 | return 0; | |
762 | } | ||
763 | |||
764 | ///////////////////////////////////////////////////////////////////////////// | ||
765 | 6 | PdServ::Parameter* Main::findParameter(const std::string& path) const | |
766 | { | ||
767 |
2/4✗ Branch 4 not taken.
✓ Branch 5 taken 6 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 6 times.
|
28 | for (ParameterList::const_iterator it = parameters.begin(); |
768 |
1/2✓ Branch 6 taken 28 times.
✗ Branch 7 not taken.
|
28 | it != parameters.end(); ++it) |
769 |
2/2✓ Branch 5 taken 6 times.
✓ Branch 6 taken 22 times.
|
28 | if ((*it)->path == path) |
770 | 6 | return *it; | |
771 | ✗ | return 0; | |
772 | } | ||
773 | |||
774 | ///////////////////////////////////////////////////////////////////////////// | ||
775 | // Orangization of shared memory: | ||
776 | // struct SDOStruct sdo | ||
777 | // char parameterData (binary data of all parameters) | ||
778 | // char pdoData | ||
779 | // struct EventData eventDataStart | ||
780 | // | ||
781 | 155 | int Main::prefork_init() | |
782 | { | ||
783 | 155 | size_t numTasks = task.size(); | |
784 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 155 times.
|
155 | size_t taskMemSize[numTasks]; |
785 | size_t i, eventCount; | ||
786 | 155 | size_t maxSignalSize = 0; | |
787 | |||
788 | // Find out the largest signal size to reserve space in | ||
789 | // shared memory for polling | ||
790 | 465 | for (TaskList::const_iterator it = task.begin(); | |
791 |
2/2✓ Branch 6 taken 310 times.
✓ Branch 7 taken 155 times.
|
465 | it != task.end(); ++it) { |
792 | 310 | std::list<const PdServ::Signal*> signals = | |
793 |
1/2✓ Branch 5 taken 310 times.
✗ Branch 6 not taken.
|
620 | static_cast<PdServ::Task*>(*it)->getSignals(); |
794 | |||
795 |
2/2✓ Branch 1 taken 930 times.
✓ Branch 2 taken 310 times.
|
2170 | while (!signals.empty()) { |
796 | 930 | const PdServ::Signal* s = signals.front(); | |
797 | |||
798 |
2/2✓ Branch 3 taken 155 times.
✓ Branch 4 taken 775 times.
|
930 | if (s->memSize > maxSignalSize) |
799 | 155 | maxSignalSize = s->memSize; | |
800 | |||
801 | 930 | signals.pop_front(); | |
802 | } | ||
803 | |||
804 | } | ||
805 | 155 | shmem_len += maxSignalSize; | |
806 | |||
807 | // The following two variables are used to organize parameters according | ||
808 | // to the size of their elements so that their data type alignment is | ||
809 | // correct. | ||
810 | // | ||
811 | // dataTypeIndex[] maps the data type to the index in parameterDataOffset, | ||
812 | // e.g. a parameter with data type double (sizeof() = 8) will then go into | ||
813 | // container parameterDataOffset[dataTypeIndex[8]] | ||
814 | // | ||
815 | // parameterDataOffset[] holds the start index of a data types with | ||
816 | // 8, 4, 2 and 1 bytes alignment | ||
817 | 155 | const size_t dataTypeIndex[PdServ::DataType::maxWidth+1] = { | |
818 | 3 /*0*/, 3 /*1*/, 2 /*2*/, 3 /*3*/, | ||
819 | 1 /*4*/, 3 /*5*/, 3 /*6*/, 3 /*7*/, 0 /*8*/ | ||
820 | }; | ||
821 | 155 | size_t parameterDataOffset[5] = {0, 0, 0, 0, 0}; // need one extra! | |
822 | |||
823 | 1085 | for (ParameterList::iterator it = parameters.begin(); | |
824 |
2/2✓ Branch 6 taken 930 times.
✓ Branch 7 taken 155 times.
|
1085 | it != parameters.end(); it++) { |
825 | 930 | const Parameter *p = static_cast<const Parameter*>(*it); | |
826 | |||
827 | // Push the next smaller data type forward by the parameter's | ||
828 | // memory requirement | ||
829 |
1/2✓ Branch 15 taken 930 times.
✗ Branch 16 not taken.
|
930 | parameterDataOffset[dataTypeIndex[p->dtype.align()] + 1] += p->memSize; |
830 | } | ||
831 | |||
832 | // Accumulate the offsets so that they follow each other in the shared | ||
833 | // data space. This also has the effect, that the value of | ||
834 | // parameterDataOffset[4] is the total memory requirement of all | ||
835 | // parameters | ||
836 |
2/2✓ Branch 0 taken 620 times.
✓ Branch 1 taken 155 times.
|
775 | for (i = 1; i < 5; ++i) |
837 | 620 | parameterDataOffset[i] += parameterDataOffset[i-1]; | |
838 | |||
839 | // Extend shared memory size with the parameter memory requirement | ||
840 | // and as many sdo's for every parameter. | ||
841 | 155 | shmem_len += parameterDataOffset[4]; | |
842 | |||
843 | // Now check how much memory is required for events | ||
844 | 155 | eventCount = 0; | |
845 |
2/2✓ Branch 8 taken 310 times.
✓ Branch 9 taken 155 times.
|
465 | for (EventList::iterator it = events.begin(); it != events.end(); ++it) |
846 | 310 | eventCount += (*it)->nelem; | |
847 | |||
848 | // Increase shared memory by the number of events as well as | ||
849 | // enough capacity to store eventDataLen event changes | ||
850 | 155 | const size_t eventLen = 10; // Arbitrary | |
851 | 155 | shmem_len += sizeof(*eventDataStart) * eventLen * eventCount; | |
852 | |||
853 | 155 | shmem_len += sizeof(*eventDataWp); // Memory location for write pointer | |
854 | |||
855 | // Find out the memory requirement for the tasks to pipe their variables | ||
856 | // out of the real time environment | ||
857 | 155 | i = 0; | |
858 |
2/4✓ Branch 2 taken 155 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 155 times.
✗ Branch 7 not taken.
|
155 | size_t bufLimit = m_config["sharedmemlimit"].toUInt(); |
859 | 465 | for (TaskList::const_iterator it = task.begin(); | |
860 |
2/2✓ Branch 6 taken 310 times.
✓ Branch 7 taken 155 times.
|
465 | it != task.end(); ++it) { |
861 |
1/2✓ Branch 1 taken 310 times.
✗ Branch 2 not taken.
|
310 | taskMemSize[i] = ptr_align( |
862 | 310 | static_cast<const Task*>(*it)->getShmemSpace(bufferTime, bufLimit)); | |
863 | 310 | shmem_len += taskMemSize[i++]; | |
864 | } | ||
865 | |||
866 | // Fudge factor. Every ptr_align can silently increase the pointer in | ||
867 | // shmem by sizeof(unsigned long). | ||
868 | // At the moment there are roughly 6 ptr_align's, take 10 to make sure! | ||
869 | 155 | shmem_len += (10 + task.size())*sizeof(unsigned long); | |
870 | |||
871 | 155 | shmem = ::mmap(0, shmem_len, PROT_READ | PROT_WRITE, | |
872 | MAP_SHARED | MAP_ANON, -1, 0); | ||
873 |
1/2✗ Branch 3 not taken.
✓ Branch 4 taken 155 times.
|
155 | if (MAP_FAILED == shmem) { |
874 | // log(LOGCRIT, "could not mmap | ||
875 | // err << "mmap(): " << strerror(errno); | ||
876 | ✗ | ::perror("mmap()"); | |
877 | ✗ | return errno; | |
878 | } | ||
879 | |||
880 | // Clear memory; at the same time prefault it, so it does not | ||
881 | // get swapped out | ||
882 |
1/2✗ Branch 6 not taken.
✓ Branch 7 taken 155 times.
|
155 | ::memset(shmem, 0, shmem_len); |
883 | |||
884 | // Now spread the shared memory for the users thereof | ||
885 | |||
886 | // 1: Parameter data | ||
887 | 155 | parameterData = ptr_align<char>(shmem); | |
888 | 1085 | for (ParameterList::iterator it = parameters.begin(); | |
889 |
2/2✓ Branch 6 taken 930 times.
✓ Branch 7 taken 155 times.
|
1085 | it != parameters.end(); it++) { |
890 | 930 | Parameter *p = *it; | |
891 | 2790 | p->shmAddr = parameterData | |
892 |
1/2✓ Branch 15 taken 930 times.
✗ Branch 16 not taken.
|
1860 | + parameterDataOffset[dataTypeIndex[p->dtype.align()]]; |
893 |
1/2✓ Branch 15 taken 930 times.
✗ Branch 16 not taken.
|
930 | parameterDataOffset[dataTypeIndex[p->dtype.align()]] += p->memSize; |
894 | |||
895 |
1/2✓ Branch 13 taken 930 times.
✗ Branch 14 not taken.
|
930 | std::copy(p->addr, p->addr + p->memSize, p->shmAddr); |
896 | } | ||
897 | |||
898 | // 2: Signal data area for polling | ||
899 | 155 | signalData = ptr_align<char>(parameterData + parameterDataOffset[4]); | |
900 | |||
901 | // 3: Streaming data for tasks | ||
902 | 155 | char* buf = ptr_align<char>(signalData + maxSignalSize); | |
903 | 155 | i = 0; | |
904 |
2/2✓ Branch 8 taken 310 times.
✓ Branch 9 taken 155 times.
|
465 | for (TaskList::iterator it = task.begin(); it != task.end(); ++it) { |
905 |
1/2✓ Branch 8 taken 310 times.
✗ Branch 9 not taken.
|
310 | static_cast<Task*>(*it)->prepare(buf, buf + taskMemSize[i]); |
906 | 310 | buf = ptr_align<char>(buf + taskMemSize[i++]); | |
907 | } | ||
908 | |||
909 | // 4: Event data | ||
910 | 155 | struct EventData** ptr = ptr_align<struct EventData*>(buf); | |
911 | 155 | eventDataWp = ptr++; | |
912 | 155 | eventDataStart = ptr_align<struct EventData>(ptr++); | |
913 | 155 | eventDataEnd = eventDataStart + eventLen * eventCount; | |
914 | 155 | *eventDataWp = eventDataStart; | |
915 | |||
916 | log_debug("shmem=%p shmem_end=%p(%zu)\n" | ||
917 | "param=%p(%zi)\n" | ||
918 | "stream=%p(%zi)\n" | ||
919 | "end=%p(%zi)\n", | ||
920 | shmem, (char*)shmem + shmem_len, shmem_len, | ||
921 | signalData, (char*)signalData - (char*)shmem, | ||
922 | eventDataWp, (char*)eventDataWp - (char*)shmem, | ||
923 | eventDataEnd, (char*)eventDataEnd - (char*)shmem); | ||
924 | |||
925 |
1/2✗ Branch 9 not taken.
✓ Branch 10 taken 155 times.
|
155 | if ((void*)(eventDataEnd + 1) > (void*)((char*)shmem + shmem_len)) { |
926 | log_debug("Not enough memory"); | ||
927 | ✗ | return ENOMEM; | |
928 | } | ||
929 | |||
930 | 310 | return 0; | |
931 | } | ||
932 | |||
933 | ///////////////////////////////////////////////////////////////////////////// | ||
934 | 150 | int Main::postfork_rt_setup() | |
935 | { | ||
936 | // Set the priority of the real time helper thread. This priority | ||
937 | // should be set to the highest real time priority. | ||
938 | 150 | set_priority(sched_get_priority_max(SCHED_FIFO)); | |
939 | |||
940 | // Parent here; go back to the caller | ||
941 | 450 | for (TaskList::iterator it = task.begin(); | |
942 |
2/2✓ Branch 6 taken 300 times.
✓ Branch 7 taken 150 times.
|
450 | it != task.end(); ++it) |
943 |
1/2✓ Branch 6 taken 300 times.
✗ Branch 7 not taken.
|
300 | static_cast<Task*>(*it)->rt_init(); |
944 | |||
945 | // Start supervisor thread | ||
946 | 150 | return start(); | |
947 | } | ||
948 | |||
949 | ///////////////////////////////////////////////////////////////////////////// | ||
950 | 154 | int Main::postfork_nrt_setup() | |
951 | { | ||
952 | // Parent here; go back to the caller | ||
953 | 462 | for (TaskList::iterator it = task.begin(); | |
954 |
2/2✓ Branch 6 taken 308 times.
✓ Branch 7 taken 154 times.
|
462 | it != task.end(); ++it) |
955 |
1/2✓ Branch 6 taken 308 times.
✗ Branch 7 not taken.
|
308 | static_cast<Task*>(*it)->nrt_init(); |
956 | 154 | return 0; | |
957 | } | ||
958 | |||
959 | ///////////////////////////////////////////////////////////////////////////// | ||
960 | |||
961 | 154 | void Main::postfork_nrt_subscribe_persistent_signals() | |
962 | { | ||
963 |
2/2✓ Branch 6 taken 308 times.
✓ Branch 7 taken 154 times.
|
462 | for (auto i : task) |
964 |
1/2✓ Branch 4 taken 308 times.
✗ Branch 5 not taken.
|
308 | i->nrt_init_persistent(); |
965 | 154 | } | |
966 | |||
967 | ///////////////////////////////////////////////////////////////////////////// | ||
968 | 154 | std::list<const PdServ::Parameter*> Main::getParameters() const | |
969 | { | ||
970 | return std::list<const PdServ::Parameter*>( | ||
971 |
1/2✓ Branch 7 taken 154 times.
✗ Branch 8 not taken.
|
154 | parameters.begin(), parameters.end()); |
972 | } | ||
973 | |||
974 | ///////////////////////////////////////////////////////////////////////////// | ||
975 | ✗ | std::list<const PdServ::Event*> Main::getEvents() const | |
976 | { | ||
977 | ✗ | std::list<const PdServ::Event*> ans; | |
978 | ✗ | for (const auto& e : events) | |
979 | ✗ | ans.push_back(e.get()); | |
980 | ✗ | return ans; | |
981 | } | ||
982 | |||
983 | ///////////////////////////////////////////////////////////////////////////// | ||
984 | 308 | std::list<const PdServ::Task*> Main::getTasks() const | |
985 | { | ||
986 |
1/2✓ Branch 7 taken 308 times.
✗ Branch 8 not taken.
|
308 | return std::list<const PdServ::Task*>(task.begin(), task.end()); |
987 | } | ||
988 | |||
989 | ///////////////////////////////////////////////////////////////////////////// | ||
990 | 148 | void Main::prepare(PdServ::Session* session) const | |
991 | { | ||
992 | 148 | PdServ::Main::prepare(session); | |
993 | 148 | } | |
994 | |||
995 | ///////////////////////////////////////////////////////////////////////////// | ||
996 | 148 | void Main::cleanup(const PdServ::Session* session) const | |
997 | { | ||
998 | 148 | PdServ::Main::cleanup(session); | |
999 | 148 | } | |
1000 | |||
1001 | ///////////////////////////////////////////////////////////////////////////// | ||
1002 | 148 | void Main::run() | |
1003 | { | ||
1004 | 148 | struct SDO sdo; | |
1005 | |||
1006 | while (true) { | ||
1007 |
3/4✓ Branch 4 taken 100 times.
✓ Branch 5 taken 148 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 100 times.
|
348 | if (::read(ipcRx, &sdo, sizeof(sdo)) != sizeof(sdo)) |
1008 | ✗ | throw std::runtime_error("Main::run(): SDO ::read() failed"); | |
1009 | |||
1010 |
3/5✗ Branch 0 not taken.
✓ Branch 1 taken 100 times.
✓ Branch 2 taken 43 times.
✓ Branch 3 taken 57 times.
✗ Branch 4 not taken.
|
100 | switch (sdo.type) { |
1011 | 43 | case SDO::ParamChange: | |
1012 |
1/2✓ Branch 4 taken 43 times.
✗ Branch 5 not taken.
|
129 | sdo.paramChangeAck.rv = setParameterValue( |
1013 | sdo.paramChangeReq.parameter, | ||
1014 | 43 | sdo.paramChangeReq.offset, | |
1015 | 43 | sdo.paramChangeReq.count, | |
1016 | &sdo.paramChangeAck.time); | ||
1017 | |||
1018 | log_debug("Parameter change rv=%i\n", sdo.paramChangeAck.rv); | ||
1019 | |||
1020 | 43 | break; | |
1021 | |||
1022 | 57 | case SDO::PollSignal: | |
1023 |
1/2✓ Branch 16 taken 57 times.
✗ Branch 17 not taken.
|
114 | sdo.signal->task->pollSignalValue( |
1024 | 57 | sdo.signal, signalData, &sdo.time); | |
1025 | 57 | break; | |
1026 | }; | ||
1027 | |||
1028 |
2/4✓ Branch 4 taken 100 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 100 times.
|
100 | if (::write(ipcTx, &sdo, sizeof(sdo)) != sizeof(sdo)) |
1029 | ✗ | throw std::runtime_error("Main::run(): SDO ::write() failed"); | |
1030 | } | ||
1031 | } | ||
1032 | |||
1033 | ///////////////////////////////////////////////////////////////////////////// | ||
1034 | 43 | int Main::setParameterValue(const Parameter* param, | |
1035 | size_t offset, size_t len, struct timespec* mtime) const | ||
1036 | { | ||
1037 | struct LockGuard { | ||
1038 | 43 | LockGuard(const Main* m): main(m) { | |
1039 |
1/2✓ Branch 6 taken 43 times.
✗ Branch 7 not taken.
|
43 | if (main->writelock_cb) |
1040 | 43 | main->writelock_cb(1, main->writelock_data); | |
1041 | 43 | } | |
1042 | 86 | ~LockGuard() { | |
1043 |
1/2✓ Branch 6 taken 43 times.
✗ Branch 7 not taken.
|
43 | if (main->writelock_cb) |
1044 | 43 | main->writelock_cb(0, main->writelock_data); | |
1045 | 43 | } | |
1046 | const Main* const main; | ||
1047 | }; | ||
1048 |
1/2✓ Branch 2 taken 43 times.
✗ Branch 3 not taken.
|
86 | LockGuard guard(this); |
1049 | |||
1050 |
1/2✓ Branch 4 taken 43 times.
✗ Branch 5 not taken.
|
86 | return param->setValue(offset, len, mtime); |
1051 | } | ||
1052 | |||
1053 | |||
1054 | ///////////////////////////////////////////////////////////////////////////// | ||
1055 | 148 | void Main::final() | |
1056 | { | ||
1057 | 148 | ::close(ipcRx); | |
1058 | 148 | ::close(ipcTx); | |
1059 | 157 | } | |
1060 |