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

traintastic / traintastic / 23872903160

01 Apr 2026 09:58PM UTC coverage: 25.802% (+0.003%) from 25.799%
23872903160

push

github

reinder
[cbus] added cbus booster support, uses ACON2 events

44 of 136 new or added lines in 5 files covered. (32.35%)

62 existing lines in 7 files now uncovered.

8300 of 32168 relevant lines covered (25.8%)

179.72 hits per line

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

0.0
/server/src/hardware/interface/cbusinterface.cpp
1
/**
2
 * This file is part of Traintastic,
3
 * see <https://github.com/traintastic/traintastic>.
4
 *
5
 * Copyright (C) 2026 Reinder Feenstra
6
 *
7
 * This program is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU General Public License
9
 * as published by the Free Software Foundation; either version 2
10
 * of the License, or (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with this program; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20
 */
21

22
#include "cbusinterface.hpp"
23
#include "cbus/cbusnodelist.hpp"
24
#include "cbus/cbussessionlist.hpp"
25
#include "cbus/cbussettings.hpp"
26
#include "../decoder/decoderchangeflags.hpp"
27
#include "../decoder/list/decoderlist.hpp"
28
#include "../decoder/list/decoderlisttablemodel.hpp"
29
#include "../input/input.hpp"
30
#include "../input/list/inputlist.hpp"
31
#include "../output/addressoutput.hpp"
32
#include "../output/list/outputlist.hpp"
33
#include "../protocol/cbus/cbusconst.hpp"
34
#include "../protocol/cbus/cbuskernel.hpp"
35
#include "../protocol/cbus/iohandler/cbuscanusbiohandler.hpp"
36
#include "../protocol/cbus/iohandler/cbuscanetheriohandler.hpp"
37
#include "../protocol/cbus/iohandler/cbussimulationiohandler.hpp"
38
#ifdef __linux__
39
  #include "../protocol/cbus/iohandler/cbussocketcaniohandler.hpp"
40
#endif
41
#include "../protocol/cbus/simulator/cbussimulator.hpp"
42
#include "../protocol/cbus/simulator/module/cbuscancab.hpp"
43
#include "../protocol/cbus/simulator/module/cbuscancmd.hpp"
44
#include "../../core/attributes.hpp"
45
#include "../../core/controllerlist.hpp"
46
#include "../../core/eventloop.hpp"
47
#include "../../core/method.tpp"
48
#include "../../core/objectproperty.tpp"
49
#include "../../log/log.hpp"
50
#include "../../log/logmessageexception.hpp"
51
#include "../../utils/displayname.hpp"
52
#include "../../utils/inrange.hpp"
53
#include "../../world/world.hpp"
54

55
namespace {
56

57
constexpr auto decoderListColumns = DecoderListColumn::Id | DecoderListColumn::Name | DecoderListColumn::Address;
58
constexpr auto inputListColumns = InputListColumn::Channel | InputListColumn::Node | InputListColumn::Address;
59
constexpr auto outputListColumns = OutputListColumn::Channel | OutputListColumn::Node | OutputListColumn::Address;
60

61
constexpr auto nodeNumberRange = std::make_pair(std::numeric_limits<uint16_t>::min(), std::numeric_limits<uint16_t>::max());
62
constexpr auto eventNumberRange = std::make_pair(std::numeric_limits<uint16_t>::min(), std::numeric_limits<uint16_t>::max());
63

64
template<typename T>
65
void deduplicate(std::vector<T>& v)
×
66
{
67
  std::sort(v.begin(), v.end());
×
68
  v.erase(std::unique(v.begin(), v.end()), v.end());
×
69
}
×
70

71
}
72

73
CREATE_IMPL(CBUSInterface)
×
74

75
CBUSInterface::CBUSInterface(World& world, std::string_view _id)
×
76
  : Interface(world, _id)
77
  , DecoderController(*this, decoderListColumns)
78
  , InputController(static_cast<IdObject&>(*this))
79
  , OutputController(static_cast<IdObject&>(*this))
80
  , type{this, "type", CBUSInterfaceType::CANUSB, PropertyFlags::ReadWrite | PropertyFlags::Store,
×
81
      [this](CBUSInterfaceType /*value*/)
×
82
      {
83
        updateVisible();
×
84
      }}
×
85
  , device{this, "device", "", PropertyFlags::ReadWrite | PropertyFlags::Store}
×
86
  , hostname{this, "hostname", "", PropertyFlags::ReadWrite | PropertyFlags::Store}
×
87
  , port{this, "port", 0, PropertyFlags::ReadWrite | PropertyFlags::Store}
×
88
  , interface{this, "interface", "", PropertyFlags::ReadWrite | PropertyFlags::Store}
×
89
  , canId{this, "can_id", 0x7D, PropertyFlags::ReadWrite | PropertyFlags::Store}
×
90
  , cbus{this, "cbus", nullptr, PropertyFlags::ReadOnly | PropertyFlags::Store | PropertyFlags::SubObject}
×
91
  , cbusNodeList{this, "cbus_node_list", nullptr, PropertyFlags::ReadOnly | PropertyFlags::NoStore | PropertyFlags::SubObject}
×
92
  , cbusSessionList{this, "cbus_session_list", nullptr, PropertyFlags::ReadOnly | PropertyFlags::NoStore | PropertyFlags::SubObject}
×
93
{
94
  name = "CBUS/VLCB";
×
95
  cbus.setValueInternal(std::make_shared<CBUSSettings>(*this, cbus.name()));
×
96
  cbusNodeList.setValueInternal(std::make_shared<CBUSNodeList>(*this, cbusNodeList.name()));
×
97
  cbusSessionList.setValueInternal(std::make_shared<CBUSSessionList>(*this, cbusSessionList.name()));
×
98

99
  Attributes::addDisplayName(type, DisplayName::Interface::type);
×
100
  Attributes::addEnabled(type, !online);
×
101
  Attributes::addValues(type, CBUSInterfaceTypeValues);
×
102
  m_interfaceItems.insertBefore(type, notes);
×
103

104
  Attributes::addEnabled(device, !online);
×
105
  Attributes::addVisible(device, false);
×
106
  m_interfaceItems.insertBefore(device, notes);
×
107

108
  Attributes::addDisplayName(hostname, DisplayName::IP::hostname);
×
109
  Attributes::addEnabled(hostname, !online);
×
110
  Attributes::addVisible(hostname, false);
×
111
  m_interfaceItems.insertBefore(hostname, notes);
×
112

113
  Attributes::addDisplayName(port, DisplayName::IP::port);
×
114
  Attributes::addEnabled(port, !online);
×
115
  Attributes::addVisible(port, false);
×
116
  m_interfaceItems.insertBefore(port, notes);
×
117

118
  Attributes::addDisplayName(interface, DisplayName::Hardware::interface);
×
119
  Attributes::addEnabled(interface, !online);
×
120
  Attributes::addVisible(interface, false);
×
121
  m_interfaceItems.insertBefore(interface, notes);
×
122

123
  Attributes::addEnabled(canId, !online);
×
124
  Attributes::addMinMax(canId, CBUS::canIdMin, CBUS::canIdMax);
×
125
  Attributes::addVisible(canId, false);
×
126
  m_interfaceItems.insertBefore(canId, notes);
×
127

128
  m_interfaceItems.insertBefore(cbus, notes);
×
129

130
  m_interfaceItems.insertBefore(cbusNodeList, notes);
×
131

132
  m_interfaceItems.insertBefore(cbusSessionList, notes);
×
133

134
  m_interfaceItems.insertBefore(decoders, notes);
×
135

136
  m_interfaceItems.insertBefore(inputs, notes);
×
137

138
  m_interfaceItems.insertBefore(outputs, notes);
×
139

140
  m_cbusPropertyChanged = cbus->propertyChanged.connect(
×
141
    [this](BaseProperty& /*property*/)
×
142
    {
143
      if(m_kernel)
×
144
      {
145
        m_kernel->setConfig(cbus->config());
×
146
      }
147
    });
×
148

149
  updateVisible();
×
150
}
×
151

152
CBUSInterface::~CBUSInterface() = default;
×
153

154
bool CBUSInterface::send(std::vector<uint8_t> message)
×
155
{
156
  if(m_kernel)
×
157
  {
158
    return m_kernel->send(std::move(message));
×
159
  }
160
  return false;
×
161
}
162

163
bool CBUSInterface::sendDCC(std::vector<uint8_t> dccPacket, uint8_t repeat)
×
164
{
165
  if(m_kernel)
×
166
  {
167
    return m_kernel->sendDCC(std::move(dccPacket), repeat);
×
168
  }
169
  return false;
×
170
}
171

NEW
172
size_t CBUSInterface::registerOnReceive(CBUS::OpCode opCode, std::function<void(uint8_t, const CBUS::Message&)> callback)
×
173
{
NEW
174
  if(m_kernel)
×
175
  {
NEW
176
    return m_kernel->registerOnReceive(opCode, std::move(callback));
×
177
  }
NEW
178
  return 0;
×
179
}
180

NEW
181
void CBUSInterface::unregisterOnReceive(size_t handle)
×
182
{
NEW
183
  if(m_kernel)
×
184
  {
NEW
185
    m_kernel->unregisterOnReceive(handle);
×
186
  }
NEW
187
}
×
188

UNCOV
189
std::span<const DecoderProtocol> CBUSInterface::decoderProtocols() const
×
190
{
191
  static constexpr std::array<DecoderProtocol, 2> protocols{DecoderProtocol::DCCShort, DecoderProtocol::DCCLong};
192
  return std::span<const DecoderProtocol>{protocols.data(), protocols.size()};
×
193
}
194

195
void CBUSInterface::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber)
×
196
{
197
  if(m_kernel)
×
198
  {
199
    const bool longAddress = (decoder.protocol == DecoderProtocol::DCCLong);
×
200

201
    if(has(changes, DecoderChangeFlags::FunctionValue) && functionNumber <= CBUS::engineFunctionMax)
×
202
    {
203
      m_kernel->setEngineFunction(
×
204
        decoder.address,
205
        longAddress,
206
        static_cast<uint8_t>(functionNumber),
207
        decoder.getFunctionValue(functionNumber));
×
208
    }
209
    else
210
    {
211
      m_kernel->setEngineSpeedDirection(
×
212
        decoder.address,
213
        longAddress,
214
        Decoder::throttleToSpeedStep<uint8_t>(decoder.throttle, decoder.speedSteps),
×
215
        decoder.speedSteps,
216
        decoder.emergencyStop,
217
        decoder.direction == Direction::Forward);
×
218
    }
219
  }
220
}
×
221

222
std::span<const InputChannel> CBUSInterface::inputChannels() const
×
223
{
224
  static const std::array<InputChannel, 2> channels{
225
    InputChannel::ShortEvent,
226
    InputChannel::LongEvent,
227
  };
228
  return channels;
×
229
}
230

231
bool CBUSInterface::isInputLocation(InputChannel channel, const InputLocation& location) const
×
232
{
233
  if(hasNodeAddressLocation(channel))
×
234
  {
235
    return
236
      inRange<uint32_t>(std::get<InputNodeAddress>(location).node, nodeNumberRange) &&
×
237
      inRange<uint32_t>(std::get<InputNodeAddress>(location).address, inputAddressMinMax(channel));
×
238
  }
239
  return InputController::isInputLocation(channel, location);
×
240
}
241

242
std::pair<uint32_t, uint32_t> CBUSInterface::inputAddressMinMax(InputChannel /*channel*/) const
×
243
{
244
  return eventNumberRange;
×
245
}
246

247
void CBUSInterface::inputSimulateChange(InputChannel channel, const InputLocation& location, SimulateInputAction action)
×
248
{
249
  if(m_simulator)
×
250
  {
251
    boost::asio::post(m_kernel->ioContext(),
×
252
      [this, channel, location, action]
×
253
      {
254
        switch(channel)
×
255
        {
256
          case InputChannel::ShortEvent:
×
257
            m_simulator->shortEvent(std::get<InputAddress>(location).address, action);
×
258
            break;
×
259

260
          case InputChannel::LongEvent:
×
261
            m_simulator->longEvent(std::get<InputNodeAddress>(location).node, std::get<InputNodeAddress>(location).address, action);
×
262
            break;
×
263

264
          default: [[unlikely]]
×
265
            assert(false);
×
266
            break;
267
        }
268
      });
×
269
  }
270
}
×
271

272
std::span<const OutputChannel> CBUSInterface::outputChannels() const
×
273
{
274
  static const std::array<OutputChannel, 4> channels{
275
    OutputChannel::ShortEvent,
276
    OutputChannel::LongEvent,
277
    OutputChannel::AccessoryDCC,
278
    OutputChannel::DCCext,
279
  };
280
  return channels;
×
281
}
282

283
std::pair<uint32_t, uint32_t> CBUSInterface::outputNodeMinMax(OutputChannel /*channel*/) const
×
284
{
285
  return eventNumberRange;
×
286
}
287

288
std::pair<uint32_t, uint32_t> CBUSInterface::outputAddressMinMax(OutputChannel channel) const
×
289
{
290
  switch(channel)
×
291
  {
292
    case OutputChannel::LongEvent:
×
293
    case OutputChannel::ShortEvent:
294
      return {std::numeric_limits<uint16_t>::min(), std::numeric_limits<uint16_t>::max()};
×
295

296
    default:
×
297
      return OutputController::outputAddressMinMax(channel);
×
298
  }
299
}
300

301
bool CBUSInterface::setOutputValue(OutputChannel channel, const OutputLocation& location, OutputValue value)
×
302
{
303
  if(m_kernel)
×
304
  {
305
    switch(channel)
×
306
    {
307
      case OutputChannel::ShortEvent:
×
308
        assert(std::holds_alternative<TriState>(value));
×
309
        if(auto v = std::get<TriState>(value); v != TriState::Undefined)
×
310
        {
311
          assert(std::holds_alternative<OutputAddress>(location));
×
312
          const auto address = static_cast<uint16_t>(std::get<OutputAddress>(location).address);
×
313
          m_kernel->setAccessoryShort(address, v == TriState::True);
×
314
          return true;
×
315
        }
316
        break;
×
317

318
      case OutputChannel::LongEvent:
×
319
        assert(std::holds_alternative<TriState>(value));
×
320
        if(auto v = std::get<TriState>(value); v != TriState::Undefined)
×
321
        {
322
          assert(std::holds_alternative<OutputNodeAddress>(location));
×
323
          const auto node = static_cast<uint16_t>(std::get<OutputNodeAddress>(location).node);
×
324
          const auto address = static_cast<uint16_t>(std::get<OutputNodeAddress>(location).address);
×
325
          m_kernel->setAccessory(node, address, v == TriState::True);
×
326
          return true;
×
327
        }
328
        break;
×
329

330
      case OutputChannel::AccessoryDCC:
×
331
        assert(std::holds_alternative<OutputPairValue>(value));
×
332
        if(const auto v = std::get<OutputPairValue>(value); v == OutputPairValue::First || v == OutputPairValue::Second)
×
333
        {
334
          assert(std::holds_alternative<OutputAddress>(location));
×
335
          const auto address = static_cast<uint16_t>(std::get<OutputAddress>(location).address);
×
336
          m_kernel->setDccAccessory(address, v == OutputPairValue::Second);
×
337
          return true;
×
338
        }
339
        break;
×
340

341
      case OutputChannel::DCCext:
×
342
        assert(std::holds_alternative<int16_t>(value));
×
343
        if(const auto v = std::get<int16_t>(value); inRange<int16_t>(v, std::numeric_limits<uint8_t>::min(), std::numeric_limits<uint8_t>::max()))
×
344
        {
345
          assert(std::holds_alternative<OutputAddress>(location));
×
346
          const auto address = static_cast<uint16_t>(std::get<OutputAddress>(location).address);
×
347
          m_kernel->setDccAdvancedAccessoryValue(address, static_cast<uint8_t>(v));
×
348
          return true;
×
349
        }
350
        break;
×
351

352
      default: [[unlikely]]
×
353
        assert(false);
×
354
        break;
355
    }
356
  }
357
  return false;
×
358
}
359

360
void CBUSInterface::addToWorld()
×
361
{
362
  Interface::addToWorld();
×
363
  DecoderController::addToWorld();
×
364
  InputController::addToWorld(inputListColumns);
×
365
  OutputController::addToWorld(outputListColumns);
×
NEW
366
  m_world.cbusInterfaces->add(Object::shared_ptr<CBUSInterface>());
×
UNCOV
367
}
×
368

369
void CBUSInterface::loaded()
×
370
{
371
  Interface::loaded();
×
372

373
  updateVisible();
×
374
}
×
375

376
void CBUSInterface::destroying()
×
377
{
NEW
378
  m_world.cbusInterfaces->add(Object::shared_ptr<CBUSInterface>());
×
379
  m_cbusPropertyChanged.disconnect();
×
380
  OutputController::destroying();
×
381
  InputController::destroying();
×
382
  DecoderController::destroying();
×
383
  Interface::destroying();
×
384
}
×
385

386
void CBUSInterface::worldEvent(WorldState state, WorldEvent event)
×
387
{
388
  Interface::worldEvent(state, event);
×
389

390
  switch(event)
×
391
  {
392
    case WorldEvent::PowerOff:
×
393
      if(m_kernel)
×
394
      {
395
        m_kernel->trackOff();
×
396
      }
397
      break;
×
398

399
    case WorldEvent::PowerOn:
×
400
      if(m_kernel)
×
401
      {
402
        m_kernel->trackOn();
×
403
      }
404
      break;
×
405

406
    case WorldEvent::Stop:
×
407
      if(m_kernel)
×
408
      {
409
        m_kernel->requestEmergencyStop();
×
410
      }
411
      break;
×
412

413
    case WorldEvent::Run:
×
414
      if(m_kernel)
×
415
      {
416
        // TODO: send all known speed values
417
      }
418
      break;
×
419

420
    default:
×
421
      break;
×
422
  }
423
}
×
424

425
bool CBUSInterface::setOnline(bool& value, bool simulation)
×
426
{
427
  if(!m_kernel && value)
×
428
  {
429
    try
430
    {
431
      if(simulation)
×
432
      {
433
        m_simulator = std::make_unique<CBUS::Simulator>();
×
434
        m_simulator->addModule(std::make_unique<CBUS::Module::CANCMD>(*m_simulator));
×
435
        m_simulator->addModule(std::make_unique<CBUS::Module::CANCAB>(*m_simulator));
×
436
        m_kernel = CBUS::Kernel::create<CBUS::SimulationIOHandler>(id.value(), cbus->config(), std::ref(*m_simulator));
×
437
      }
438
      else
439
      {
440
        switch(type)
×
441
        {
442
          case CBUSInterfaceType::CANUSB:
×
443
            m_kernel = CBUS::Kernel::create<CBUS::CANUSBIOHandler>(id.value(), cbus->config(), device.value());
×
444
            break;
×
445

446
          case CBUSInterfaceType::CANEther:
×
447
            m_kernel = CBUS::Kernel::create<CBUS::CANEtherIOHandler>(id.value(), cbus->config(), hostname.value(), port.value());
×
448
            break;
×
449

450
          case CBUSInterfaceType::SocketCAN:
×
451
#ifdef __linux__
452
            m_kernel = CBUS::Kernel::create<CBUS::SocketCANIOHandler>(id.value(), cbus->config(), interface.value(), canId.value());
×
453
            break;
×
454
#else
455
            setState(InterfaceState::Error);
456
            Log::log(*this, LogMessage::C2005_SOCKETCAN_IS_ONLY_AVAILABLE_ON_LINUX);
457
            return false;
458
#endif
459
        }
460
      }
461

462
      setState(InterfaceState::Initializing);
×
463

464
      m_kernel->setOnStarted(
×
465
        [this]()
×
466
        {
467
          setState(InterfaceState::Online);
×
468
        });
×
469
      m_kernel->setOnError(
×
470
        [this]()
×
471
        {
472
          setState(InterfaceState::Error);
×
473
          online = false; // communication no longer possible
×
474
        });
×
475
      m_kernel->onPresenceOfNode =
×
476
        [this](uint8_t canId_, uint16_t nodeNumber, uint8_t manufacturerId, uint8_t moduleId, bool flimMode, bool supportsServiceDiscovery)
×
477
        {
478
          cbusNodeList->add(CBUSNodeList::Node{
×
479
            .nodeNumber = nodeNumber,
480
            .canId = canId_,
481
            .manufacturerId = manufacturerId,
482
            .moduleId = moduleId,
483
            .flim = flimMode,
484
            .vlcb = supportsServiceDiscovery,
485
            .parameters = {},
486
          });
487
        };
×
488
      m_kernel->onNodeParameterResponse =
×
489
        [this](uint8_t canId_, uint16_t nodeNumber, CBUS::NodeParameter parameter, uint8_t parameterValue)
×
490
        {
491
          auto& nodes = cbusNodeList->m_nodes;
×
492
          if(auto it = std::find_if(nodes.begin(), nodes.end(),
×
493
              [canId_, nodeNumber](const auto& item)
×
494
              {
495
                return item.canId == canId_ && item.nodeNumber == nodeNumber;
×
496
              }); it != nodes.end())
×
497
          {
498
            switch(parameter)
×
499
            {
500
              using enum CBUS::NodeParameter;
501

502
              case VersionMajor:
×
503
                it->parameters.versionMajor = parameterValue;
×
504
                break;
×
505

506
              case VersionMinor:
×
507
                it->parameters.versionMinor = parameterValue;
×
508
                break;
×
509

510
              case BetaReleaseCode:
×
511
                it->parameters.betaReleaseCode = parameterValue;
×
512
                break;
×
513

514
              default:
×
515
                return;
×
516
            }
517
            cbusNodeList->rowChanged(static_cast<uint32_t>(std::distance(nodes.begin(), it)));
×
518
          }
519
        };
×
520
      m_kernel->onTrackOff =
×
521
        [this]()
×
522
        {
523
          if(contains(m_world.state, WorldState::PowerOn))
×
524
          {
525
            m_world.powerOff();
×
526
          }
527
        };
×
528
      m_kernel->onTrackOn =
×
529
        [this]()
×
530
        {
531
          if(!contains(m_world.state, WorldState::PowerOn))
×
532
          {
533
            m_world.powerOn();
×
534
          }
535
        };
×
536
      m_kernel->onEmergencyStop =
×
537
        [this]()
×
538
        {
539
          if(contains(m_world.state, WorldState::Run))
×
540
          {
541
            m_world.stop();
×
542
          }
543
        };
×
544
      m_kernel->onEngineSessionAcquire =
×
545
        [this](uint8_t session, uint16_t address, bool isLongAddress)
×
546
        {
547
          if(auto it = cbusSessionList->findEngine(address, isLongAddress); it != cbusSessionList->end())
×
548
          {
549
            it->session = session;
×
550
            cbusSessionList->rowChanged(static_cast<uint32_t>(std::distance(cbusSessionList->begin(), it)));
×
551
          }
552
          else
553
          {
554
            cbusSessionList->add({session, address, isLongAddress});
×
555
          }
556
        };
×
557
      m_kernel->onEngineSpeedDirectionChanged =
×
558
        [this](uint8_t session, uint8_t speed, bool forward)
×
559
        {
560
          if(auto it = cbusSessionList->findSession(session); it != cbusSessionList->end())
×
561
          {
562
            it->speed = speed;
×
563
            it->direction = forward ? Direction::Forward : Direction::Reverse;
×
564
            cbusSessionList->rowChanged(static_cast<uint32_t>(std::distance(cbusSessionList->begin(), it)));
×
565
          }
566
        };
×
567
      m_kernel->onEngineFunctionChanged =
×
568
        [this](uint8_t session, uint8_t number, bool on)
×
569
        {
570
          if(auto it = cbusSessionList->findSession(session); it != cbusSessionList->end())
×
571
          {
572
            assert(number < it->functions.size());
×
573
            it->functions[number] = toTriState(on);
×
574
            cbusSessionList->rowChanged(static_cast<uint32_t>(std::distance(cbusSessionList->begin(), it)));
×
575
          }
576
        };
×
577
      m_kernel->onEngineSessionReleased =
×
578
        [this](uint8_t session)
×
579
        {
580
          if(auto it = cbusSessionList->findSession(session); it != cbusSessionList->end())
×
581
          {
582
            it->session = std::nullopt;
×
583
            cbusSessionList->rowChanged(static_cast<uint32_t>(std::distance(cbusSessionList->begin(), it)));
×
584
          }
585
        };
×
586
      m_kernel->onEngineSessionCancelled =
×
587
        [this](uint16_t address, bool isLongAddress)
×
588
        {
589
          if(auto it = cbusSessionList->findEngine(address, isLongAddress); it != cbusSessionList->end())
×
590
          {
591
            it->session = std::nullopt;
×
592
            cbusSessionList->rowChanged(static_cast<uint32_t>(std::distance(cbusSessionList->begin(), it)));
×
593
          }
594
        };
×
595
      m_kernel->onShortEvent =
×
596
        [this](uint16_t eventNumber, bool on)
×
597
        {
598
          updateInputValue(InputChannel::ShortEvent, InputAddress(eventNumber), toTriState(on));
×
599
          updateOutputValue(OutputChannel::ShortEvent, OutputAddress(eventNumber), toTriState(on));
×
600
        };
×
601
      m_kernel->onLongEvent =
×
602
        [this](uint16_t nodeNumber, uint16_t eventNumber, bool on)
×
603
        {
604
          updateInputValue(InputChannel::LongEvent, InputNodeAddress(nodeNumber, eventNumber), toTriState(on));
×
605
          updateOutputValue(OutputChannel::LongEvent, OutputNodeAddress(nodeNumber, eventNumber), toTriState(on));
×
606
        };
×
607

608
      {
609
        std::vector<uint16_t> shortEvents;
×
610
        std::vector<std::pair<uint16_t, uint16_t>> longEvents;
×
611

612
        for(const auto& [key, input] : m_inputs)
×
613
        {
614
          if(input->channel == InputChannel::ShortEvent)
×
615
          {
616
            shortEvents.emplace_back(input->address);
×
617
          }
618
          else if(input->channel == InputChannel::LongEvent)
×
619
          {
620
            longEvents.emplace_back(input->node, input->address);
×
621
          }
622
        }
623

624
        for(const auto& [key, output] : m_outputs)
×
625
        {
626
          if(output->channel == OutputChannel::ShortEvent)
×
627
          {
628
            shortEvents.emplace_back(static_cast<const AddressOutput&>(*output).address);
×
629
          }
630
          else if(output->channel == OutputChannel::LongEvent)
×
631
          {
632
            longEvents.emplace_back(static_cast<const AddressOutput&>(*output).node, static_cast<const AddressOutput&>(*output).address);
×
633
          }
634
        }
635

636
        deduplicate(shortEvents);
×
637
        deduplicate(longEvents);
×
638

639
        m_kernel->setRequestEventsDuringInitialize(std::move(shortEvents), std::move(longEvents));
×
640
      }
×
641

642
      m_kernel->start();
×
643

644
      Attributes::setEnabled({type, device, hostname, port, interface, canId}, false);
×
645
    }
646
    catch(const LogMessageException& e)
×
647
    {
648
      setState(InterfaceState::Offline);
×
649
      Log::log(*this, e.message(), e.args());
×
650
      return false;
×
651
    }
×
652
  }
653
  else if(m_kernel && !value)
×
654
  {
655
    Attributes::setEnabled({type, device, hostname, port, interface, canId}, true);
×
656

657
    m_kernel->stop();
×
658
    EventLoop::deleteLater(m_kernel.release());
×
659
    EventLoop::deleteLater(m_simulator.release());
×
660

661
    cbusNodeList->clear();
×
662

663
    if(status->state != InterfaceState::Error)
×
664
    {
665
      setState(InterfaceState::Offline);
×
666
    }
667
  }
668
  return true;
×
669
}
670

671
void CBUSInterface::updateVisible()
×
672
{
673
  Attributes::setVisible(device, (type == CBUSInterfaceType::CANUSB));
×
674
  Attributes::setVisible({hostname, port}, (type == CBUSInterfaceType::CANEther));
×
675
  Attributes::setVisible({interface, canId}, (type == CBUSInterfaceType::SocketCAN));
×
676
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc