GCC Code Coverage Report


Directory: ./
File: pdserv/src/msrproto/SubscriptionManager.cpp
Date: 2024-12-15 04:08:34
Exec Total Coverage
Lines: 88 146 60.3%
Branches: 70 180 38.9%

Line Branch Exec Source
1 /*****************************************************************************
2 *
3 * $Id$
4 *
5 * Copyright 2010 Richard Hacker (lerichi at gmx dot net)
6 *
7 * This file is part of the pdserv library.
8 *
9 * The pdserv library is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU Lesser General Public License as published
11 * by the Free Software Foundation, either version 3 of the License, or (at
12 * your option) any later version.
13 *
14 * The pdserv library is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
17 * License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with the pdserv library. If not, see <http://www.gnu.org/licenses/>.
21 *
22 *****************************************************************************/
23
24 #include "../Debug.h"
25 #include "../Main.h"
26 #include "../Signal.h"
27 #include "../Task.h"
28 #include "../TaskStatistics.h"
29 #include "SubscriptionManager.h"
30 #include "Channel.h"
31 #include "Session.h"
32 #include "Subscription.h"
33
34 using namespace MsrProto;
35
36 /////////////////////////////////////////////////////////////////////////////
37 struct timespec SubscriptionManager::dummyTime;
38 PdServ::TaskStatistics SubscriptionManager::dummyTaskStatistics;
39
40 /////////////////////////////////////////////////////////////////////////////
41 296 SubscriptionManager::SubscriptionManager(
42 296 Session *s, const PdServ::Task* task):
43 296 SessionTask(task), session(s)
44 {
45 296 taskTime = &dummyTime;
46 296 taskStatistics = &dummyTaskStatistics;
47
48 // Call rxPdo() once so that taskTime and taskStatistics are updated
49
1/2
✓ Branch 6 taken 296 times.
✗ Branch 7 not taken.
296 task->rxPdo(this, &taskTime, &taskStatistics);
50 296 }
51
52 /////////////////////////////////////////////////////////////////////////////
53 888 SubscriptionManager::~SubscriptionManager()
54 {
55 296 clear();
56 592 }
57
58 /////////////////////////////////////////////////////////////////////////////
59 44 void SubscriptionManager::subscribe (const Channel *c, size_t group,
60 size_t decimation, size_t blocksize, bool base64,
61 std::streamsize precision)
62 {
63 44 Subscription** s = &signalSubscriptionMap[c->signal][c][group];
64
65 // First remove possible subscription. It doesn't matter if it is null
66
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 44 times.
44 if (*s)
67 remove(*s, group);
68
69
1/2
✓ Branch 3 taken 44 times.
✗ Branch 4 not taken.
44 *s = new Subscription(c, decimation, blocksize, base64, precision);
70
71 // Call subscribe on this signal. It doesn't matter if it is already
72 // subscribed, but it is useful because newSignal() is called for us
73 // in this special case
74 44 c->signal->subscribe(this);
75 44 }
76
77 /////////////////////////////////////////////////////////////////////////////
78 void SubscriptionManager::unsubscribe(const Channel *c, size_t group)
79 {
80 // Go through all groups (signal, channel, group) to find the signal,
81 // keeping in mind that the channel may not even be subscribed yet!
82
83 SignalSubscriptionMap::iterator sit = signalSubscriptionMap.find(c->signal);
84 if (sit == signalSubscriptionMap.end())
85 return;
86
87 ChannelSubscriptionMap::iterator cit = sit->second.find(c);
88 if (cit == sit->second.end())
89 return;
90
91 SubscriptionGroup::iterator git = cit->second.find(group);
92 if (git == cit->second.end())
93 return;
94
95 // Remove channel from active list
96 remove(git->second, group);
97
98 // Now that the channel is unsubscribed, check for empty groups,
99 // jumping out if the group is not empty
100
101 cit->second.erase(git);
102 if (!cit->second.empty())
103 return;
104
105 sit->second.erase(cit);
106 if (!sit->second.empty())
107 return;
108
109 // Don't require signal any more
110 signalSubscriptionMap.erase(sit);
111 c->signal->unsubscribe(this);
112 }
113
114 /////////////////////////////////////////////////////////////////////////////
115 296 void SubscriptionManager::clear()
116 {
117 592 SignalSubscriptionMap::const_iterator sit;
118 592 ChannelSubscriptionMap::const_iterator cit;
119 592 SubscriptionGroup::const_iterator git;
120
121 296 activeSignals.clear();
122
123 340 for (sit = signalSubscriptionMap.begin();
124
2/2
✓ Branch 6 taken 44 times.
✓ Branch 7 taken 296 times.
340 sit != signalSubscriptionMap.end(); ++sit) {
125
2/2
✓ Branch 14 taken 44 times.
✓ Branch 15 taken 44 times.
88 for (cit = sit->second.begin(); cit != sit->second.end(); ++cit) {
126
1/2
✓ Branch 8 taken 44 times.
✗ Branch 9 not taken.
44 sit->first->unsubscribe(this);
127
2/2
✓ Branch 14 taken 44 times.
✓ Branch 15 taken 44 times.
88 for (git = cit->second.begin(); git != cit->second.end(); ++git)
128
1/2
✓ Branch 2 taken 44 times.
✗ Branch 3 not taken.
44 delete git->second;
129 }
130 }
131
132 296 signalSubscriptionMap.clear();
133 296 }
134
135 /////////////////////////////////////////////////////////////////////////////
136 44 void SubscriptionManager::newSignal( const PdServ::Signal *s)
137 {
138
2/4
✓ Branch 3 taken 44 times.
✗ Branch 4 not taken.
✓ Branch 10 taken 44 times.
✗ Branch 11 not taken.
88 SignalSubscriptionMap::const_iterator sit = signalSubscriptionMap.find(s);
139
140 // Find out whether this signal is used or whether sit is active already
141
1/2
✗ Branch 6 not taken.
✓ Branch 7 taken 44 times.
44 if (sit == signalSubscriptionMap.end())
142 return;
143
144
1/2
✓ Branch 7 taken 44 times.
✗ Branch 8 not taken.
88 for (ChannelSubscriptionMap::const_iterator cit = sit->second.begin();
145
2/2
✓ Branch 7 taken 44 times.
✓ Branch 8 taken 44 times.
88 cit != sit->second.end(); ++cit) {
146 88 for (SubscriptionGroup::const_iterator git = cit->second.begin();
147
2/2
✓ Branch 7 taken 44 times.
✓ Branch 8 taken 44 times.
88 git != cit->second.end(); ++git) {
148
149 44 Subscription* s = git->second;
150
151 // Put subscription into active list
152 // Note: for event subscriptions, blocksize == 0
153 SubscriptionSet* g =
154
3/6
✓ Branch 6 taken 44 times.
✗ Branch 7 not taken.
✓ Branch 10 taken 44 times.
✗ Branch 11 not taken.
✓ Branch 14 taken 44 times.
✗ Branch 15 not taken.
44 &activeSignals[git->first][s->decimation][s->blocksize];
155
156
2/2
✓ Branch 2 taken 36 times.
✓ Branch 3 taken 8 times.
44 if (g->empty()) {
157 // The blocksize group jas just been created, add data space for
158 // current time
159
2/4
✓ Branch 3 taken 36 times.
✗ Branch 4 not taken.
✓ Branch 9 taken 36 times.
✗ Branch 10 not taken.
36 g->time.reset(new uint64_t[s->blocksize + !s->blocksize]);
160 36 g->timePtr = g->time.get();
161 log_debug("s=%zu %zu:%zu:%zu %p", s->blocksize,
162 git->first, s->decimation, s->blocksize, g->time);
163 }
164
165
1/2
✓ Branch 2 taken 44 times.
✗ Branch 3 not taken.
44 g->insert(s);
166 }
167 }
168 }
169
170 /////////////////////////////////////////////////////////////////////////////
171 void SubscriptionManager::remove (Subscription *s, size_t group)
172 {
173 // Find the map for the group
174 ActiveSignals::iterator git = activeSignals.find(group);
175 if (git == activeSignals.end())
176 return;
177
178 // Find the decimation group
179 DecimationGroup::iterator dit = git->second.find(s->decimation);
180 if (dit == git->second.end())
181 return;
182
183 // Find the blocksize group
184 BlocksizeGroup::iterator bit = dit->second.find(s->blocksize);
185 if (bit == dit->second.end())
186 return;
187
188 // Delete and remove subscription from set
189 delete s;
190 bit->second.erase(s);
191 if (!bit->second.empty())
192 return;
193
194 // blocksize group is empty, erase it
195 dit->second.erase(bit);
196 if (!dit->second.empty())
197 return;
198
199 // decimation group is empty, erase it
200 git->second.erase(dit);
201 if (!git->second.empty())
202 return;
203
204 // group is emty, erase it
205 activeSignals.erase(git);
206 }
207
208 /////////////////////////////////////////////////////////////////////////////
209 18958 void SubscriptionManager::rxPdo(bool quiet)
210 {
211 37916 ActiveSignals::iterator git;
212 37916 DecimationGroup::iterator dit;
213 37916 BlocksizeGroup::iterator bit;
214 37916 SubscriptionSet::iterator sit;
215 Subscription* s;
216 18958 Subscription* printQ, **printQEnd;
217 bool print;
218
219
3/4
✓ Branch 24 taken 22534 times.
✗ Branch 25 not taken.
✓ Branch 26 taken 3576 times.
✓ Branch 27 taken 18958 times.
26110 while (task->rxPdo(this, &taskTime, &taskStatistics)) {
220
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3576 times.
3576 if (quiet)
221 continue;
222
223 // Go through all groups
224
2/2
✓ Branch 12 taken 1990 times.
✓ Branch 13 taken 3576 times.
5566 for (git = activeSignals.begin(); git != activeSignals.end(); ++git) {
225
226 // Go through all decimations
227
2/2
✓ Branch 14 taken 1990 times.
✓ Branch 15 taken 1990 times.
3980 for (dit = git->second.begin(); dit != git->second.end(); ++dit) {
228
229 // Check decimation counter
230
2/2
✓ Branch 5 taken 1132 times.
✓ Branch 6 taken 858 times.
1990 if (dit->second.busy(dit->first))
231 1132 continue;
232
233 // Go through all blocksizes
234 1716 for (bit = dit->second.begin();
235
2/2
✓ Branch 7 taken 858 times.
✓ Branch 8 taken 858 times.
1716 bit != dit->second.end(); ++bit) {
236
237 // Capture time
238 1716 *bit->second.timePtr =
239 1716 1000000000ULL * taskTime->tv_sec + taskTime->tv_nsec;
240
241 // For non-event groups, increment timePtr and check
242 // whether it points to the end
243 858 print = false;
244
2/2
✓ Branch 2 taken 846 times.
✓ Branch 3 taken 12 times.
858 if (bit->first) {
245 846 ++bit->second.timePtr;
246 1692 print = (bit->second.time.get() + bit->first)
247 846 == bit->second.timePtr;
248 }
249
250 // Prepare print queue
251 858 printQ = 0;
252 858 printQEnd = &printQ;
253
254 // Go through all subscriptions
255 2036 for (sit = bit->second.begin();
256
2/2
✓ Branch 7 taken 1178 times.
✓ Branch 8 taken 858 times.
2036 sit != bit->second.end(); ++sit) {
257 1178 s = *sit;
258
1/2
✓ Branch 25 taken 1178 times.
✗ Branch 26 not taken.
1178 const char *data = s->channel->signal->getValue(this);
259
260
9/10
✓ Branch 2 taken 1178 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1168 times.
✓ Branch 5 taken 10 times.
✓ Branch 8 taken 1166 times.
✓ Branch 9 taken 2 times.
✓ Branch 10 taken 501 times.
✓ Branch 11 taken 675 times.
✓ Branch 12 taken 503 times.
✓ Branch 13 taken 675 times.
1178 if ((s->newValue(data) and !bit->first) or print) {
261 503 *printQEnd = s;
262 503 printQEnd = &s->next;
263 503 s->next = 0; // Sentinel
264 }
265 }
266
267 // Check if any signals need printing
268
2/2
✓ Branch 0 taken 343 times.
✓ Branch 1 taken 515 times.
858 if (printQ) {
269
1/2
✓ Branch 5 taken 343 times.
✗ Branch 6 not taken.
686 XmlElement dataTag(session->createElement("data"));
270
2/2
✓ Branch 2 taken 341 times.
✓ Branch 3 taken 2 times.
343 if (git->first)
271
1/2
✓ Branch 1 taken 341 times.
✗ Branch 2 not taken.
682 XmlElement::Attribute(dataTag, "group")
272
1/2
✓ Branch 5 taken 341 times.
✗ Branch 6 not taken.
682 << git->first;
273
274
2/4
✓ Branch 2 taken 343 times.
✗ Branch 3 not taken.
✓ Branch 8 taken 343 times.
✗ Branch 9 not taken.
343 XmlElement::Attribute(dataTag, "level") << 0;
275
2/4
✓ Branch 2 taken 343 times.
✗ Branch 3 not taken.
✓ Branch 10 taken 343 times.
✗ Branch 11 not taken.
343 XmlElement::Attribute(dataTag, "time") << *taskTime;
276
277 // Print time channel
278 {
279 size_t len = sizeof(uint64_t)
280 343 * (bit->first + !bit->first);
281
282
1/2
✓ Branch 2 taken 343 times.
✗ Branch 3 not taken.
686 XmlElement time(dataTag.createChild("time"));
283
1/2
✓ Branch 2 taken 343 times.
✗ Branch 3 not taken.
686 XmlElement::Attribute value(time, "d");
284
1/2
✓ Branch 4 taken 343 times.
✗ Branch 5 not taken.
343 value.base64(bit->second.time.get(), len);
285 }
286
287 // Reset timePtr
288 343 bit->second.timePtr = bit->second.time.get();
289
290 // Print every subscription
291
2/2
✓ Branch 0 taken 503 times.
✓ Branch 1 taken 343 times.
846 for (s = printQ; s; s = s->next)
292
1/2
✓ Branch 2 taken 503 times.
✗ Branch 3 not taken.
503 s->print(dataTag);
293 }
294 }
295 }
296 }
297 }
298 18958 }
299
300 /////////////////////////////////////////////////////////////////////////////
301 void SubscriptionManager::sync()
302 {
303 ActiveSignals::iterator git;
304 DecimationGroup::iterator dit;
305 BlocksizeGroup::iterator bit;
306 SubscriptionSet::iterator sit;
307
308 for (git = activeSignals.begin();
309 git != activeSignals.end(); ++git) {
310 for (dit = git->second.begin();
311 dit != git->second.end(); ++dit) {
312 for (bit = dit->second.begin();
313 bit != dit->second.end(); ++bit) {
314 bit->second.timePtr = bit->second.time.get();
315 for (sit = bit->second.begin();
316 sit != bit->second.end(); ++sit)
317 (*sit)->reset();
318 }
319 }
320 }
321 }
322