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 |