• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ossia / score / 33560887155

01 Sep 2026 09:24PM UTC coverage: 24.648% (+0.7%) from 23.997%
33560887155

Pull #2179

github

web-flow
Merge ecc0a2ad3 into 50d28933c
Pull Request #2179: Survive a build that does not have every plug-in

408 of 757 new or added lines in 47 files covered. (53.9%)

15 existing lines in 12 files now uncovered.

55851 of 226593 relevant lines covered (24.65%)

61526.39 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

43.75
/src/plugins/score-plugin-deviceexplorer/Explorer/DocumentPlugin/NodeUpdateProxy.cpp
1
// This is an open source non-commercial project. Dear PVS-Studio, please check
2
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
3
#include "NodeUpdateProxy.hpp"
4

5
#include "DeviceDocumentPlugin.hpp"
6

7
#include <State/Address.hpp>
8

9
#include <Device/Address/AddressSettings.hpp>
10
#include <Device/Node/DeviceNode.hpp>
11
#include <Device/Protocol/DeviceInterface.hpp>
12
#include <Device/Protocol/DeviceSettings.hpp>
13

14
#include <Explorer/DeviceList.hpp>
15
#include <Explorer/Explorer/DeviceExplorerModel.hpp>
16

17
#include <score/model/tree/TreeNode.hpp>
18
#include <score/tools/std/Optional.hpp>
19

20
#include <ossia/detail/algorithms.hpp>
21

22
#include <QDebug>
23

24
#include <vector>
25

26
namespace Explorer
27
{
28
NodeUpdateProxy::NodeUpdateProxy(DeviceDocumentPlugin& root)
192✔
29
    : devModel{root}
192✔
30
{
31
}
192✔
32

33
void NodeUpdateProxy::addDevice(const Device::Node& node)
41✔
34
{
35
  devModel.explorer().addDevice(devModel.createDeviceFromNode(node));
41✔
36
}
41✔
37

38
void NodeUpdateProxy::loadDevice(const Device::Node& node)
9✔
39
{
40
  auto n = devModel.loadDeviceFromNode(node);
9✔
41
  if(n)
9✔
42
  {
43
    devModel.explorer().addDevice(std::move(*n));
1✔
44
  }
1✔
45
  else
46
  {
47
    devModel.explorer().addDevice(node);
8✔
48
  }
49
}
9✔
50

51
void NodeUpdateProxy::updateDevice(
27✔
52
    const QString& name, const Device::DeviceSettings& dev)
53
{
54
  // The device reconnects with its new settings - synchronously or not,
55
  // depending on the protocol, hence hooking before applying them; once it
56
  // has, its namespace is explored again so that the explorer shows the tree
57
  // of e.g. the new OSCQuery host, and not the one of the previous host
58
  // replayed into it.
59
  if(auto* impl = devModel.list().findDevice(name))
27✔
60
  {
61
    devModel.refreshDeviceTreeOnReconnect(*impl);
26✔
62
    impl->updateSettings(dev);
26✔
63
  }
26✔
64
  devModel.explorer().updateDevice(name, dev);
27✔
65
}
27✔
66

67
void NodeUpdateProxy::removeDevice(const Device::DeviceSettings& dev)
5✔
68
{
69
  const auto& rootNode = devModel.rootNode();
5✔
70
  auto it = ossia::find_if(rootNode, [&](const Device::Node& val) {
10✔
71
    return val.is<Device::DeviceSettings>()
10✔
72
           && val.get<Device::DeviceSettings>().name == dev.name;
5✔
73
  });
74
  SCORE_ASSERT(it != rootNode.end());
5✔
75
  auto dev_i = devModel.list().findDevice(dev.name);
5✔
76
  if(dev_i)
5✔
77
    devModel.setupConnections(*dev_i, false);
4✔
78

79
  devModel.explorer().removeNode(it);
5✔
80

81
  if(dev_i)
5✔
82
    devModel.list().removeDevice(dev.name);
4✔
83
}
5✔
84

85
void NodeUpdateProxy::addAddress(
1✔
86
    const Device::NodePath& parentPath, const Device::AddressSettings& settings, int row)
87
{
88
  auto parentnode = parentPath.toNode(&devModel.rootNode());
1✔
89
  if(!parentnode)
1✔
90
    return;
×
91

92
  // Add in the device impl
93
  // Get the device node :
94
  // TODO row isn't managed here.
95
  const Device::Node& dev_node = devModel.rootNode().childAt(parentPath.at(0));
1✔
96
  SCORE_ASSERT(dev_node.template is<Device::DeviceSettings>());
1✔
97

98
  // Make a full path
99
  Device::FullAddressSettings full
100
      = Device::FullAddressSettings::make<Device::FullAddressSettings::as_parent>(
1✔
101
          settings, Device::address(*parentnode));
1✔
102

103
  // Add in the device implementation
104
  if(auto* impl = devModel.list().findDevice(
2✔
105
         dev_node.template get<Device::DeviceSettings>().name))
1✔
NEW
106
    impl->addAddress(full);
×
107

108
  // Add in the device explorer
109
  if(settings.name.contains('/'))
1✔
110
  {
111
    addLocalAddresses(*parentnode, settings, row);
×
112
  }
×
113
  else
114
  {
115
    addLocalAddress(*parentnode, settings, row);
1✔
116
  }
117
}
1✔
118

119
void NodeUpdateProxy::addAddress(const Device::FullAddressSettings& full)
×
120
{
121
  // Add in the device implementation
NEW
122
  auto* dev_p = devModel.list().findDevice(full.address.device);
×
NEW
123
  if(!dev_p)
×
NEW
124
    return;
×
125

NEW
126
  auto& dev = *dev_p;
×
127
  bool learning = dev.isLearning();
×
128
  dev.setLearning(true);
×
129
  dev.addAddress(full);
×
130
  dev.setLearning(learning);
×
131
  // Add in the device explorer
132
  // addLocalAddress(*parentnode, settings, row);
133
}
×
134

135
void NodeUpdateProxy::rec_addNode(
×
136
    Device::NodePath parentPath, const Device::Node& n, int row)
137
{
138
  addAddress(parentPath, n.template get<Device::AddressSettings>(), row);
×
139

140
  parentPath.push_back(row);
×
141

142
  int r = 0;
×
143
  for(const auto& child : n.children())
×
144
  {
145
    rec_addNode(parentPath, child, r++);
×
146
  }
147
}
×
148

149
void NodeUpdateProxy::addNode(
×
150
    const Device::NodePath& parentPath, const Device::Node& node, int row)
151
{
152
  SCORE_ASSERT(node.template is<Device::AddressSettings>());
×
153

154
  rec_addNode(parentPath, node, row);
×
155
}
×
156

157
void NodeUpdateProxy::updateAddress(
10✔
158
    const Device::NodePath& nodePath, const Device::AddressSettings& settings)
159
{
160
  auto node = nodePath.toNode(&devModel.rootNode());
10✔
161
  if(!node)
10✔
162
    return;
×
163

164
  const auto addr = Device::address(*node);
10✔
165
  // Make a full path
166
  Device::FullAddressSettings full
167
      = Device::FullAddressSettings::make<Device::FullAddressSettings::as_child>(
10✔
168
          settings, addr);
10✔
169
  full.address.path.last() = settings.name;
10✔
170

171
  // Update in the device implementation
172
  if(auto* impl = devModel.list().findDevice(addr.address.device))
10✔
173
    impl->updateAddress(addr.address, full);
10✔
174

175
  // Update in the device explorer
176
  devModel.explorer().updateAddress(node, settings);
10✔
177
}
10✔
178

179
void NodeUpdateProxy::removeNode(
1✔
180
    const Device::NodePath& parentPath, const Device::AddressSettings& settings)
181
{
182
  Device::Node* parentnode = parentPath.toNode(&devModel.rootNode());
1✔
183
  if(!parentnode)
1✔
184
    return;
×
185

186
  if(settings.name.contains('/'))
1✔
187
  {
188
    auto names = settings.name.split('/');
×
189
    const Device::Node* lastnode = parentnode;
×
190
    for(auto& n : names)
×
191
    {
192
      auto res = findChildNode(*lastnode, n);
×
193
      if(!res)
×
194
      {
195
        qDebug() << "Did not find" << n << "in" << lastnode->displayName();
×
196
        return;
×
197
      }
198
      else
199
      {
200
        lastnode = res;
×
201
      }
202
    }
203

204
    {
205
      Device::Node* lastparentnode = lastnode->parent();
×
206
      auto addr = Device::address(*lastnode);
×
207

208
      // Remove from the device implementation
209
      const auto& dev_node = devModel.rootNode().childAt(parentPath.at(0));
×
NEW
210
      if(auto* impl = devModel.list().findDevice(
×
NEW
211
             dev_node.template get<Device::DeviceSettings>().name))
×
NEW
212
        impl->removeNode(addr.address);
×
213

214
      // Remove from the device explorer
215
      auto it = findChildNode_it(*lastparentnode, lastnode->displayName());
×
216
      if(it == parentnode->end())
×
217
        return;
×
218

219
      devModel.explorer().removeNode(it);
×
220

221
      lastnode = lastparentnode;
×
222
      lastparentnode = lastparentnode->parent();
×
223
    }
×
224
  }
×
225
  else
226
  {
227
    auto addr = Device::address(*parentnode).address;
1✔
228
    addr.path.append(settings.name);
1✔
229

230
    // Remove from the device implementation
231
    const auto& dev_node = devModel.rootNode().childAt(parentPath.at(0));
1✔
232
    if(auto* impl = devModel.list().findDevice(
2✔
233
           dev_node.template get<Device::DeviceSettings>().name))
1✔
NEW
234
      impl->removeNode(addr);
×
235

236
    // Remove from the device explorer
237
    auto it = std::find_if(
1✔
238
        parentnode->begin(), parentnode->end(), [&](const Device::Node& n) {
2✔
239
          return n.get<Device::AddressSettings>().name == settings.name;
1✔
240
        });
241

242
    // The node may have been removed by a previous command already
243
    if(it != parentnode->end())
1✔
244
    {
245
      devModel.explorer().removeNode(it);
1✔
246
    }
1✔
247
  }
1✔
248
}
1✔
249

250
void NodeUpdateProxy::addLocalAddress(
1✔
251
    Device::Node& parentnode, const Device::AddressSettings& settings, int row)
252
{
253
  devModel.explorer().addAddress(&parentnode, settings, row);
1✔
254
}
1✔
255

256
void NodeUpdateProxy::addLocalAddresses(
×
257
    Device::Node& parentnode, Device::AddressSettings settings, int row)
258
{
259
  // Find the first node that does not exist
260
  auto names = settings.name.split('/');
×
261

262
  int k = 0;
×
263
  const Device::Node* node = &parentnode;
×
264
  while(k < names.size())
×
265
  {
266
    auto cld = ossia::find_if(
×
267
        node->children(), [&](auto& node) { return node.displayName() == names[k]; });
×
268

269
    if(cld != node->children().end())
×
270
    {
271
      node = &*cld;
×
272
      ++k;
×
273
    }
×
274
    else
275
    {
276
      break;
×
277
    }
278
  }
279

280
  while(k < int(names.size()) - 1)
×
281
  {
282
    Device::AddressSettings set;
×
283
    set.name = names[k];
×
284
    node = devModel.explorer().addAddress(const_cast<Device::Node*>(node), set, row);
×
285
    ++k;
×
286
  }
×
287

288
  settings.name = names.back();
×
289

290
  devModel.explorer().addAddress(const_cast<Device::Node*>(node), settings, row);
×
291
}
×
292

293
void NodeUpdateProxy::updateLocalValue(
26✔
294
    const State::AddressAccessor& addr, const ossia::value& v)
295
{
296
  auto n = Device::try_getNodeFromAddress(devModel.rootNode(), addr.address);
26✔
297
  if(!n)
26✔
298
    return;
2✔
299

300
  if(!n->template is<Device::AddressSettings>())
24✔
301
  {
302
    qDebug() << "Updating invalid node";
×
303
    return;
×
304
  }
305

306
  devModel.explorer().updateValue(n, addr, v);
24✔
307
}
26✔
308

309
void NodeUpdateProxy::updateLocalSettings(
5,223✔
310
    const State::Address& addr, const Device::AddressSettings& set,
311
    Device::DeviceInterface& newdev)
312
{
313
  auto n = Device::try_getNodeFromAddress(devModel.rootNode(), addr);
5,223✔
314
  if(!n)
5,223✔
315
  {
316
    // FIXME A subtle bug is introduced if we want to add the root node...
317
    if(addr.path.size() > 0)
5,223✔
318
    {
319
      auto parentAddr = addr;
5,223✔
320
      parentAddr.path.removeLast();
5,223✔
321

322
      Device::Node* parent
5,223✔
323
          = Device::try_getNodeFromAddress(devModel.rootNode(), parentAddr);
5,223✔
324
      if(parent)
5,223✔
325
      {
326
        const auto& last = addr.path.back();
×
327
        auto it = ossia::find_if(
×
328
            *parent, [&](const auto& n) { return n.displayName() == last; });
×
329
        if(it == parent->cend())
×
330
        {
331
          addLocalNode(*parent, newdev.getNode(addr));
×
332
        }
×
333
        else
334
        {
335
          // TODO update the node with the new information
336
        }
337
      }
×
338
    }
5,223✔
339
    return;
5,223✔
340
  }
341

342
  if(!n->template is<Device::AddressSettings>())
×
343
  {
344
    qDebug() << "Updating invalid node";
×
345
    return;
×
346
  }
347

348
  devModel.explorer().updateAddress(n, set);
×
349
}
5,223✔
350

351
void NodeUpdateProxy::updateRemoteValue(
9✔
352
    const State::Address& addr, const ossia::value& val)
353
{
354
  if(auto dev = devModel.list().findDevice(addr.device))
9✔
355
  {
356
    // Update in the device implementation
357
    dev->sendMessage(addr, val);
9✔
358
  }
9✔
359
}
9✔
360

361
ossia::value NodeUpdateProxy::refreshRemoteValue(const State::Address& addr) const
×
362
{
363
  // TODO here and in the following function, we should still update
364
  // the device explorer.
365
  auto dev_p = devModel.list().findDevice(addr.device);
×
366
  if(!dev_p)
×
367
    return {};
×
368

369
  auto& dev = *dev_p;
×
370

371
  auto& n = Device::getNodeFromAddress(devModel.rootNode(), addr)
×
372
                .template get<Device::AddressSettings>();
×
373
  if(dev.capabilities().canRefreshValue)
×
374
  {
375
    if(auto val = dev.refresh(addr))
×
376
    {
377
      n.value = *val;
×
378
    }
×
379
  }
×
380

381
  return n.value;
×
382
}
×
383

384
std::optional<ossia::value>
385
NodeUpdateProxy::try_refreshRemoteValue(const State::Address& addr) const
×
386
{
387
  // TODO here and in the following function, we should still update
388
  // the device explorer.
389
  auto dev_p = devModel.list().findDevice(addr.device);
×
390
  if(!dev_p)
×
391
    return {};
×
392

393
  auto& dev = *dev_p;
×
394

395
  auto node = Device::try_getNodeFromAddress(devModel.rootNode(), addr);
×
396
  if(!node)
×
397
    return {};
×
398

399
  auto& n = node->template get<Device::AddressSettings>();
×
400
  if(dev.capabilities().canRefreshValue)
×
401
  {
402
    if(auto val = dev.refresh(addr))
×
403
    {
404
      n.value = *val;
×
405
    }
×
406
  }
×
407

408
  return n.value;
×
409
}
×
410

411
static void rec_refreshRemoteValues(Device::Node& n, Device::DeviceInterface& dev)
×
412
{
413
  // OPTIMIZEME
414
  auto val = dev.refresh(Device::address(n).address);
×
415
  if(val)
×
416
    n.template get<Device::AddressSettings>().value = *val;
×
417

418
  for(auto& child : n)
×
419
  {
420
    rec_refreshRemoteValues(child, dev);
×
421
  }
422
}
×
423

424
void NodeUpdateProxy::refreshRemoteValues(const Device::NodeList& nodes)
×
425
{
426
  // For each node, get its device.
427
  for(auto n : nodes)
×
428
  {
429
    if(n->template is<Device::DeviceSettings>())
×
430
    {
431
      auto dev_name = n->template get<Device::DeviceSettings>().name;
×
NEW
432
      auto* dev = devModel.list().findDevice(dev_name);
×
NEW
433
      if(!dev || !dev->capabilities().canRefreshValue)
×
UNCOV
434
        continue;
×
435

436
      for(auto& child : *n)
×
437
      {
NEW
438
        rec_refreshRemoteValues(child, *dev);
×
439
      }
440
    }
×
441
    else
442
    {
443
      auto addr = Device::address(*n);
×
NEW
444
      auto* dev = devModel.list().findDevice(addr.address.device);
×
NEW
445
      if(!dev || !dev->capabilities().canRefreshValue)
×
UNCOV
446
        continue;
×
447

NEW
448
      rec_refreshRemoteValues(*n, *dev);
×
449
    }
×
450
  }
451
}
×
452

453
void NodeUpdateProxy::addLocalNode(Device::Node& parent, Device::Node&& node)
2✔
454
{
455
  if(node.is<Device::AddressSettings>())
2✔
456
  {
457
    int row = parent.childCount();
2✔
458
    devModel.explorer().addNode(&parent, std::move(node), row);
2✔
459
  }
2✔
460
}
2✔
461

462
void NodeUpdateProxy::removeLocalNode(const State::Address& addr)
6,082✔
463
{
464
  auto parentAddr = addr;
6,082✔
465
  auto nodeName = parentAddr.path.takeLast();
6,082✔
466
  auto parentNode = Device::try_getNodeFromAddress(devModel.rootNode(), parentAddr);
6,082✔
467
  if(parentNode)
6,082✔
468
  {
469
    auto it = ossia::find_if(*parentNode, [&](const Device::Node& n) {
×
470
      return n.get<Device::AddressSettings>().name == nodeName;
×
471
    });
472
    if(it != parentNode->end())
×
473
    {
474
      devModel.explorer().removeNode(it);
×
475
    }
×
476
  }
×
477
}
6,082✔
478
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc