GCC Code Coverage Report


Directory: ./
File: pdserv/src/lib/Main.cpp
Date: 2026-10-18 04:09:42
Exec Total Coverage
Lines: 347 541 64.1%
Branches: 201 802 25.1%

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