GCC Code Coverage Report


Directory: ./
File: pdserv/src/lib/Main.cpp
Date: 2025-08-17 04:10:43
Exec Total Coverage
Lines: 347 534 65.0%
Branches: 201 688 29.2%

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