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

ossia / score / 32671464175

23 Aug 2026 10:43PM UTC coverage: 23.21% (+1.6%) from 21.606%
32671464175

push

github

jcelerier
scenario: export NodalIntervalView

test_integration_nodal_viewport and test_integration_cable_drag_view_scroll
call into the class from outside the plug-in, so a shared-plugin build could
not link either of them: the library held 136 NodalIntervalView symbols and
exported none. A static-plugin build never sees it, which is why CI does not.

51875 of 223502 relevant lines covered (23.21%)

63063.32 hits per line

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

28.0
/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)
139✔
29
    : devModel{root}
139✔
30
{
31
}
139✔
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)
×
39
{
40
  auto n = devModel.loadDeviceFromNode(node);
×
41
  if(n)
×
42
  {
43
    devModel.explorer().addDevice(std::move(*n));
×
44
  }
×
45
  else
46
  {
47
    devModel.explorer().addDevice(node);
×
48
  }
49
}
×
50

51
void NodeUpdateProxy::updateDevice(
26✔
52
    const QString& name, const Device::DeviceSettings& dev)
53
{
54
  auto& device = devModel.list().device(name);
26✔
55

56
  // The device reconnects with its new settings - synchronously or not,
57
  // depending on the protocol, hence hooking before applying them; once it
58
  // has, its namespace is explored again so that the explorer shows the tree
59
  // of e.g. the new OSCQuery host, and not the one of the previous host
60
  // replayed into it.
61
  devModel.refreshDeviceTreeOnReconnect(device);
26✔
62

63
  device.updateSettings(dev);
26✔
64
  devModel.explorer().updateDevice(name, dev);
26✔
65
}
26✔
66

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

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

81
  devModel.list().removeDevice(dev.name);
4✔
82
}
4✔
83

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

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

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

102
  // Add in the device implementation
103
  devModel.list()
×
104
      .device(dev_node.template get<Device::DeviceSettings>().name)
×
105
      .addAddress(full);
×
106

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

118
void NodeUpdateProxy::addAddress(const Device::FullAddressSettings& full)
×
119
{
120
  // Add in the device implementation
121
  auto& dev = devModel.list().device(full.address.device);
×
122
  bool learning = dev.isLearning();
×
123
  dev.setLearning(true);
×
124
  dev.addAddress(full);
×
125
  dev.setLearning(learning);
×
126
  // Add in the device explorer
127
  // addLocalAddress(*parentnode, settings, row);
128
}
×
129

130
void NodeUpdateProxy::rec_addNode(
×
131
    Device::NodePath parentPath, const Device::Node& n, int row)
132
{
133
  addAddress(parentPath, n.template get<Device::AddressSettings>(), row);
×
134

135
  parentPath.push_back(row);
×
136

137
  int r = 0;
×
138
  for(const auto& child : n.children())
×
139
  {
140
    rec_addNode(parentPath, child, r++);
×
141
  }
142
}
×
143

144
void NodeUpdateProxy::addNode(
×
145
    const Device::NodePath& parentPath, const Device::Node& node, int row)
146
{
147
  SCORE_ASSERT(node.template is<Device::AddressSettings>());
×
148

149
  rec_addNode(parentPath, node, row);
×
150
}
×
151

152
void NodeUpdateProxy::updateAddress(
10✔
153
    const Device::NodePath& nodePath, const Device::AddressSettings& settings)
154
{
155
  auto node = nodePath.toNode(&devModel.rootNode());
10✔
156
  if(!node)
10✔
157
    return;
×
158

159
  const auto addr = Device::address(*node);
10✔
160
  // Make a full path
161
  Device::FullAddressSettings full
162
      = Device::FullAddressSettings::make<Device::FullAddressSettings::as_child>(
10✔
163
          settings, addr);
10✔
164
  full.address.path.last() = settings.name;
10✔
165

166
  // Update in the device implementation
167
  devModel.list().device(addr.address.device).updateAddress(addr.address, full);
10✔
168

169
  // Update in the device explorer
170
  devModel.explorer().updateAddress(node, settings);
10✔
171
}
10✔
172

173
void NodeUpdateProxy::removeNode(
×
174
    const Device::NodePath& parentPath, const Device::AddressSettings& settings)
175
{
176
  Device::Node* parentnode = parentPath.toNode(&devModel.rootNode());
×
177
  if(!parentnode)
×
178
    return;
×
179

180
  if(settings.name.contains('/'))
×
181
  {
182
    auto names = settings.name.split('/');
×
183
    const Device::Node* lastnode = parentnode;
×
184
    for(auto& n : names)
×
185
    {
186
      auto res = findChildNode(*lastnode, n);
×
187
      if(!res)
×
188
      {
189
        qDebug() << "Did not find" << n << "in" << lastnode->displayName();
×
190
        return;
×
191
      }
192
      else
193
      {
194
        lastnode = res;
×
195
      }
196
    }
197

198
    {
199
      Device::Node* lastparentnode = lastnode->parent();
×
200
      auto addr = Device::address(*lastnode);
×
201

202
      // Remove from the device implementation
203
      const auto& dev_node = devModel.rootNode().childAt(parentPath.at(0));
×
204
      devModel.list()
×
205
          .device(dev_node.template get<Device::DeviceSettings>().name)
×
206
          .removeNode(addr.address);
×
207

208
      // Remove from the device explorer
209
      auto it = findChildNode_it(*lastparentnode, lastnode->displayName());
×
210
      if(it == parentnode->end())
×
211
        return;
×
212

213
      devModel.explorer().removeNode(it);
×
214

215
      lastnode = lastparentnode;
×
216
      lastparentnode = lastparentnode->parent();
×
217
    }
×
218
  }
×
219
  else
220
  {
221
    auto addr = Device::address(*parentnode).address;
×
222
    addr.path.append(settings.name);
×
223

224
    // Remove from the device implementation
225
    const auto& dev_node = devModel.rootNode().childAt(parentPath.at(0));
×
226
    devModel.list()
×
227
        .device(dev_node.template get<Device::DeviceSettings>().name)
×
228
        .removeNode(addr);
×
229

230
    // Remove from the device explorer
231
    auto it = std::find_if(
×
232
        parentnode->begin(), parentnode->end(), [&](const Device::Node& n) {
×
233
          return n.get<Device::AddressSettings>().name == settings.name;
×
234
        });
235

236
    // The node may have been removed by a previous command already
237
    if(it != parentnode->end())
×
238
    {
239
      devModel.explorer().removeNode(it);
×
240
    }
×
241
  }
×
242
}
×
243

244
void NodeUpdateProxy::addLocalAddress(
×
245
    Device::Node& parentnode, const Device::AddressSettings& settings, int row)
246
{
247
  devModel.explorer().addAddress(&parentnode, settings, row);
×
248
}
×
249

250
void NodeUpdateProxy::addLocalAddresses(
×
251
    Device::Node& parentnode, Device::AddressSettings settings, int row)
252
{
253
  // Find the first node that does not exist
254
  auto names = settings.name.split('/');
×
255

256
  int k = 0;
×
257
  const Device::Node* node = &parentnode;
×
258
  while(k < names.size())
×
259
  {
260
    auto cld = ossia::find_if(
×
261
        node->children(), [&](auto& node) { return node.displayName() == names[k]; });
×
262

263
    if(cld != node->children().end())
×
264
    {
265
      node = &*cld;
×
266
      ++k;
×
267
    }
×
268
    else
269
    {
270
      break;
×
271
    }
272
  }
273

274
  while(k < int(names.size()) - 1)
×
275
  {
276
    Device::AddressSettings set;
×
277
    set.name = names[k];
×
278
    node = devModel.explorer().addAddress(const_cast<Device::Node*>(node), set, row);
×
279
    ++k;
×
280
  }
×
281

282
  settings.name = names.back();
×
283

284
  devModel.explorer().addAddress(const_cast<Device::Node*>(node), settings, row);
×
285
}
×
286

287
void NodeUpdateProxy::updateLocalValue(
26✔
288
    const State::AddressAccessor& addr, const ossia::value& v)
289
{
290
  auto n = Device::try_getNodeFromAddress(devModel.rootNode(), addr.address);
26✔
291
  if(!n)
26✔
292
    return;
2✔
293

294
  if(!n->template is<Device::AddressSettings>())
24✔
295
  {
296
    qDebug() << "Updating invalid node";
×
297
    return;
×
298
  }
299

300
  devModel.explorer().updateValue(n, addr, v);
24✔
301
}
26✔
302

303
void NodeUpdateProxy::updateLocalSettings(
4,006✔
304
    const State::Address& addr, const Device::AddressSettings& set,
305
    Device::DeviceInterface& newdev)
306
{
307
  auto n = Device::try_getNodeFromAddress(devModel.rootNode(), addr);
4,006✔
308
  if(!n)
4,006✔
309
  {
310
    // FIXME A subtle bug is introduced if we want to add the root node...
311
    if(addr.path.size() > 0)
4,006✔
312
    {
313
      auto parentAddr = addr;
4,006✔
314
      parentAddr.path.removeLast();
4,006✔
315

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

336
  if(!n->template is<Device::AddressSettings>())
×
337
  {
338
    qDebug() << "Updating invalid node";
×
339
    return;
×
340
  }
341

342
  devModel.explorer().updateAddress(n, set);
×
343
}
4,006✔
344

345
void NodeUpdateProxy::updateRemoteValue(
9✔
346
    const State::Address& addr, const ossia::value& val)
347
{
348
  // TODO add these checks everywhere.
349
  if(auto dev = devModel.list().findDevice(addr.device))
9✔
350
  {
351
    // Update in the device implementation
352
    dev->sendMessage(addr, val);
9✔
353
  }
9✔
354
}
9✔
355

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

364
  auto& dev = *dev_p;
×
365

366
  auto& n = Device::getNodeFromAddress(devModel.rootNode(), addr)
×
367
                .template get<Device::AddressSettings>();
×
368
  if(dev.capabilities().canRefreshValue)
×
369
  {
370
    if(auto val = dev.refresh(addr))
×
371
    {
372
      n.value = *val;
×
373
    }
×
374
  }
×
375

376
  return n.value;
×
377
}
×
378

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

388
  auto& dev = *dev_p;
×
389

390
  auto node = Device::try_getNodeFromAddress(devModel.rootNode(), addr);
×
391
  if(!node)
×
392
    return {};
×
393

394
  auto& n = node->template get<Device::AddressSettings>();
×
395
  if(dev.capabilities().canRefreshValue)
×
396
  {
397
    if(auto val = dev.refresh(addr))
×
398
    {
399
      n.value = *val;
×
400
    }
×
401
  }
×
402

403
  return n.value;
×
404
}
×
405

406
static void rec_refreshRemoteValues(Device::Node& n, Device::DeviceInterface& dev)
×
407
{
408
  // OPTIMIZEME
409
  auto val = dev.refresh(Device::address(n).address);
×
410
  if(val)
×
411
    n.template get<Device::AddressSettings>().value = *val;
×
412

413
  for(auto& child : n)
×
414
  {
415
    rec_refreshRemoteValues(child, dev);
×
416
  }
417
}
×
418

419
void NodeUpdateProxy::refreshRemoteValues(const Device::NodeList& nodes)
×
420
{
421
  // For each node, get its device.
422
  for(auto n : nodes)
×
423
  {
424
    if(n->template is<Device::DeviceSettings>())
×
425
    {
426
      auto dev_name = n->template get<Device::DeviceSettings>().name;
×
427
      auto& dev = devModel.list().device(dev_name);
×
428
      if(!dev.capabilities().canRefreshValue)
×
429
        continue;
×
430

431
      for(auto& child : *n)
×
432
      {
433
        rec_refreshRemoteValues(child, dev);
×
434
      }
435
    }
×
436
    else
437
    {
438
      auto addr = Device::address(*n);
×
439
      auto& dev = devModel.list().device(addr.address.device);
×
440
      if(!dev.capabilities().canRefreshValue)
×
441
        continue;
×
442

443
      rec_refreshRemoteValues(*n, dev);
×
444
    }
×
445
  }
446
}
×
447

448
void NodeUpdateProxy::addLocalNode(Device::Node& parent, Device::Node&& node)
2✔
449
{
450
  if(node.is<Device::AddressSettings>())
2✔
451
  {
452
    int row = parent.childCount();
2✔
453
    devModel.explorer().addNode(&parent, std::move(node), row);
2✔
454
  }
2✔
455
}
2✔
456

457
void NodeUpdateProxy::removeLocalNode(const State::Address& addr)
4,371✔
458
{
459
  auto parentAddr = addr;
4,371✔
460
  auto nodeName = parentAddr.path.takeLast();
4,371✔
461
  auto parentNode = Device::try_getNodeFromAddress(devModel.rootNode(), parentAddr);
4,371✔
462
  if(parentNode)
4,371✔
463
  {
464
    auto it = ossia::find_if(*parentNode, [&](const Device::Node& n) {
×
465
      return n.get<Device::AddressSettings>().name == nodeName;
×
466
    });
467
    if(it != parentNode->end())
×
468
    {
469
      devModel.explorer().removeNode(it);
×
470
    }
×
471
  }
×
472
}
4,371✔
473
}
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