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

traintastic / traintastic / 26124759034

19 May 2026 08:54PM UTC coverage: 25.552% (-0.003%) from 25.555%
26124759034

push

github

reinder
[cbus] added CAN ID setting, defaults to 0x7A (122), official assignment is pending

0 of 21 new or added lines in 5 files covered. (0.0%)

1 existing line in 1 file now uncovered.

8436 of 33015 relevant lines covered (25.55%)

176.82 hits per line

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

0.0
/server/src/hardware/interface/cbus/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 "cbusnodelist.hpp"
24
#include "cbussessionlist.hpp"
25
#include "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
  , cbus{this, "cbus", nullptr, PropertyFlags::ReadOnly | PropertyFlags::Store | PropertyFlags::SubObject}
×
90
  , cbusNodeList{this, "cbus_node_list", nullptr, PropertyFlags::ReadOnly | PropertyFlags::NoStore | PropertyFlags::SubObject}
×
91
  , cbusSessionList{this, "cbus_session_list", nullptr, PropertyFlags::ReadOnly | PropertyFlags::NoStore | PropertyFlags::SubObject}
×
92
{
93
  name = "CBUS/VLCB";
×
94
  cbus.setValueInternal(std::make_shared<CBUSSettings>(*this, cbus.name()));
×
95
  cbusNodeList.setValueInternal(std::make_shared<CBUSNodeList>(*this, cbusNodeList.name()));
×
96
  cbusSessionList.setValueInternal(std::make_shared<CBUSSessionList>(*this, cbusSessionList.name()));
×
97

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

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

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

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

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

122
  m_interfaceItems.insertBefore(cbus, notes);
×
123

124
  m_interfaceItems.insertBefore(cbusNodeList, notes);
×
125

126
  m_interfaceItems.insertBefore(cbusSessionList, notes);
×
127

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

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

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

134
  m_cbusPropertyChanged = cbus->propertyChanged.connect(
×
135
    [this](BaseProperty& /*property*/)
×
136
    {
137
      if(m_kernel)
×
138
      {
139
        m_kernel->setConfig(cbus->config());
×
140
      }
141
    });
×
142

143
  updateVisible();
×
144
}
×
145

146
CBUSInterface::~CBUSInterface() = default;
×
147

148
bool CBUSInterface::send(std::vector<uint8_t> message)
×
149
{
150
  if(m_kernel)
×
151
  {
152
    return m_kernel->send(std::move(message));
×
153
  }
154
  return false;
×
155
}
156

157
bool CBUSInterface::sendDCC(std::vector<uint8_t> dccPacket, uint8_t repeat)
×
158
{
159
  if(m_kernel)
×
160
  {
161
    return m_kernel->sendDCC(std::move(dccPacket), repeat);
×
162
  }
163
  return false;
×
164
}
165

166
size_t CBUSInterface::registerOnReceive(CBUS::OpCode opCode, std::function<void(uint8_t, const CBUS::Message&)> callback)
×
167
{
168
  if(m_kernel)
×
169
  {
170
    return m_kernel->registerOnReceive(opCode, std::move(callback));
×
171
  }
172
  return 0;
×
173
}
174

175
void CBUSInterface::unregisterOnReceive(size_t handle)
×
176
{
177
  if(m_kernel)
×
178
  {
179
    m_kernel->unregisterOnReceive(handle);
×
180
  }
181
}
×
182

183
std::span<const DecoderProtocol> CBUSInterface::decoderProtocols() const
×
184
{
185
  static constexpr std::array<DecoderProtocol, 2> protocols{DecoderProtocol::DCCShort, DecoderProtocol::DCCLong};
186
  return std::span<const DecoderProtocol>{protocols.data(), protocols.size()};
×
187
}
188

189
void CBUSInterface::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber)
×
190
{
191
  if(m_kernel)
×
192
  {
193
    const bool longAddress = (decoder.protocol == DecoderProtocol::DCCLong);
×
194

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

216
std::span<const InputChannel> CBUSInterface::inputChannels() const
×
217
{
218
  static const std::array<InputChannel, 2> channels{
219
    InputChannel::ShortEvent,
220
    InputChannel::LongEvent,
221
  };
222
  return channels;
×
223
}
224

225
bool CBUSInterface::isInputLocation(InputChannel channel, const InputLocation& location) const
×
226
{
227
  if(hasNodeAddressLocation(channel))
×
228
  {
229
    return
230
      inRange<uint32_t>(std::get<InputNodeAddress>(location).node, nodeNumberRange) &&
×
231
      inRange<uint32_t>(std::get<InputNodeAddress>(location).address, inputAddressMinMax(channel));
×
232
  }
233
  return InputController::isInputLocation(channel, location);
×
234
}
235

236
std::pair<uint32_t, uint32_t> CBUSInterface::inputAddressMinMax(InputChannel /*channel*/) const
×
237
{
238
  return eventNumberRange;
×
239
}
240

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

254
          case InputChannel::LongEvent:
×
255
            m_simulator->longEvent(std::get<InputNodeAddress>(location).node, std::get<InputNodeAddress>(location).address, action);
×
256
            break;
×
257

258
          default: [[unlikely]]
×
259
            assert(false);
×
260
            break;
261
        }
262
      });
×
263
  }
264
}
×
265

266
std::span<const OutputChannel> CBUSInterface::outputChannels() const
×
267
{
268
  static const std::array<OutputChannel, 4> channels{
269
    OutputChannel::ShortEvent,
270
    OutputChannel::LongEvent,
271
    OutputChannel::AccessoryDCC,
272
    OutputChannel::DCCext,
273
  };
274
  return channels;
×
275
}
276

277
std::pair<uint32_t, uint32_t> CBUSInterface::outputNodeMinMax(OutputChannel /*channel*/) const
×
278
{
279
  return eventNumberRange;
×
280
}
281

282
std::pair<uint32_t, uint32_t> CBUSInterface::outputAddressMinMax(OutputChannel channel) const
×
283
{
284
  switch(channel)
×
285
  {
286
    case OutputChannel::LongEvent:
×
287
    case OutputChannel::ShortEvent:
288
      return {std::numeric_limits<uint16_t>::min(), std::numeric_limits<uint16_t>::max()};
×
289

290
    default:
×
291
      return OutputController::outputAddressMinMax(channel);
×
292
  }
293
}
294

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

312
      case OutputChannel::LongEvent:
×
313
        assert(std::holds_alternative<TriState>(value));
×
314
        if(auto v = std::get<TriState>(value); v != TriState::Undefined)
×
315
        {
316
          assert(std::holds_alternative<OutputNodeAddress>(location));
×
317
          const auto node = static_cast<uint16_t>(std::get<OutputNodeAddress>(location).node);
×
318
          const auto address = static_cast<uint16_t>(std::get<OutputNodeAddress>(location).address);
×
319
          m_kernel->setAccessory(node, address, v == TriState::True);
×
320
          return true;
×
321
        }
322
        break;
×
323

324
      case OutputChannel::AccessoryDCC:
×
325
        assert(std::holds_alternative<OutputPairValue>(value));
×
326
        if(const auto v = std::get<OutputPairValue>(value); v == OutputPairValue::First || v == OutputPairValue::Second)
×
327
        {
328
          assert(std::holds_alternative<OutputAddress>(location));
×
329
          const auto address = static_cast<uint16_t>(std::get<OutputAddress>(location).address);
×
330
          m_kernel->setDccAccessory(address, v == OutputPairValue::Second);
×
331
          return true;
×
332
        }
333
        break;
×
334

335
      case OutputChannel::DCCext:
×
336
        assert(std::holds_alternative<int16_t>(value));
×
337
        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()))
×
338
        {
339
          assert(std::holds_alternative<OutputAddress>(location));
×
340
          const auto address = static_cast<uint16_t>(std::get<OutputAddress>(location).address);
×
341
          m_kernel->setDccAdvancedAccessoryValue(address, static_cast<uint8_t>(v));
×
342
          return true;
×
343
        }
344
        break;
×
345

346
      default: [[unlikely]]
×
347
        assert(false);
×
348
        break;
349
    }
350
  }
351
  return false;
×
352
}
353

354
void CBUSInterface::addToWorld()
×
355
{
356
  Interface::addToWorld();
×
357
  DecoderController::addToWorld();
×
358
  InputController::addToWorld(inputListColumns);
×
359
  OutputController::addToWorld(outputListColumns);
×
360
  m_world.cbusInterfaces->add(Object::shared_ptr<CBUSInterface>());
×
361
}
×
362

363
void CBUSInterface::loaded()
×
364
{
365
  Interface::loaded();
×
366

367
  updateVisible();
×
368
}
×
369

370
void CBUSInterface::destroying()
×
371
{
372
  m_world.cbusInterfaces->add(Object::shared_ptr<CBUSInterface>());
×
373
  m_cbusPropertyChanged.disconnect();
×
374
  OutputController::destroying();
×
375
  InputController::destroying();
×
376
  DecoderController::destroying();
×
377
  Interface::destroying();
×
378
}
×
379

380
void CBUSInterface::worldEvent(WorldState state, WorldEvent event)
×
381
{
382
  Interface::worldEvent(state, event);
×
383

384
  switch(event)
×
385
  {
386
    case WorldEvent::PowerOff:
×
387
      if(m_kernel)
×
388
      {
389
        m_kernel->trackOff();
×
390
      }
391
      break;
×
392

393
    case WorldEvent::PowerOn:
×
394
      if(m_kernel)
×
395
      {
396
        m_kernel->trackOn();
×
397
      }
398
      break;
×
399

400
    case WorldEvent::Stop:
×
401
      if(m_kernel)
×
402
      {
403
        m_kernel->requestEmergencyStop();
×
404
      }
405
      break;
×
406

407
    case WorldEvent::Run:
×
408
      if(m_kernel)
×
409
      {
410
        // TODO: send all known speed values
411
      }
412
      break;
×
413

414
    default:
×
415
      break;
×
416
  }
417
}
×
418

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

440
          case CBUSInterfaceType::CANEther:
×
NEW
441
            m_kernel = CBUS::Kernel::create<CBUS::CANEtherIOHandler>(id.value(), cbus->config(), hostname.value(), port.value());
×
442
            break;
×
443

444
          case CBUSInterfaceType::SocketCAN:
×
445
#ifdef __linux__
NEW
446
            m_kernel = CBUS::Kernel::create<CBUS::SocketCANIOHandler>(id.value(), cbus->config(), interface.value());
×
447
            break;
×
448
#else
449
            setState(InterfaceState::Error);
450
            Log::log(*this, LogMessage::C2005_SOCKETCAN_IS_ONLY_AVAILABLE_ON_LINUX);
451
            return false;
452
#endif
453
        }
454
      }
455

456
      setState(InterfaceState::Initializing);
×
457

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

496
              case VersionMajor:
×
497
                it->parameters.versionMajor = parameterValue;
×
498
                break;
×
499

500
              case VersionMinor:
×
501
                it->parameters.versionMinor = parameterValue;
×
502
                break;
×
503

504
              case BetaReleaseCode:
×
505
                it->parameters.betaReleaseCode = parameterValue;
×
506
                break;
×
507

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

604
      {
605
        std::vector<uint16_t> shortEvents;
×
606
        std::vector<std::pair<uint16_t, uint16_t>> longEvents;
×
607

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

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

632
        deduplicate(shortEvents);
×
633
        deduplicate(longEvents);
×
634

635
        m_kernel->setRequestEventsDuringInitialize(std::move(shortEvents), std::move(longEvents));
×
636
      }
×
637

638
      m_kernel->start();
×
639

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

653
    m_kernel->stop();
×
654
    EventLoop::deleteLater(m_kernel.release());
×
655
    EventLoop::deleteLater(m_simulator.release());
×
656

657
    cbusNodeList->clear();
×
658

659
    if(status->state != InterfaceState::Error)
×
660
    {
661
      setState(InterfaceState::Offline);
×
662
    }
663
  }
664
  return true;
×
665
}
666

667
void CBUSInterface::updateVisible()
×
668
{
669
  Attributes::setVisible(device, (type == CBUSInterfaceType::CANUSB));
×
670
  Attributes::setVisible({hostname, port}, (type == CBUSInterfaceType::CANEther));
×
NEW
671
  Attributes::setVisible(interface, (type == CBUSInterfaceType::SocketCAN));
×
672
}
×
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