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

traintastic / traintastic / 23769213849

30 Mar 2026 09:43PM UTC coverage: 25.799% (-0.07%) from 25.87%
23769213849

push

github

reinder
[cbus] request all used short/long event during interface initialization

0 of 110 new or added lines in 2 files covered. (0.0%)

11 existing lines in 3 files now uncovered.

8256 of 32001 relevant lines covered (25.8%)

179.77 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/eventloop.hpp"
46
#include "../../core/method.tpp"
47
#include "../../core/objectproperty.tpp"
48
#include "../../log/log.hpp"
49
#include "../../log/logmessageexception.hpp"
50
#include "../../utils/displayname.hpp"
51
#include "../../utils/inrange.hpp"
52
#include "../../world/world.hpp"
53

54
namespace {
55

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

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

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

70
}
71

UNCOV
72
CREATE_IMPL(CBUSInterface)
×
73

74
CBUSInterface::CBUSInterface(World& world, std::string_view _id)
×
75
  : Interface(world, _id)
76
  , DecoderController(*this, decoderListColumns)
77
  , InputController(static_cast<IdObject&>(*this))
78
  , OutputController(static_cast<IdObject&>(*this))
79
  , type{this, "type", CBUSInterfaceType::CANUSB, PropertyFlags::ReadWrite | PropertyFlags::Store,
×
80
      [this](CBUSInterfaceType /*value*/)
×
81
      {
82
        updateVisible();
×
83
      }}
×
84
  , device{this, "device", "", PropertyFlags::ReadWrite | PropertyFlags::Store}
×
85
  , hostname{this, "hostname", "", PropertyFlags::ReadWrite | PropertyFlags::Store}
×
86
  , port{this, "port", 0, PropertyFlags::ReadWrite | PropertyFlags::Store}
×
87
  , interface{this, "interface", "", PropertyFlags::ReadWrite | PropertyFlags::Store}
×
88
  , canId{this, "can_id", 0x7D, 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
  Attributes::addEnabled(canId, !online);
×
123
  Attributes::addMinMax(canId, CBUS::canIdMin, CBUS::canIdMax);
×
124
  Attributes::addVisible(canId, false);
×
125
  m_interfaceItems.insertBefore(canId, notes);
×
126

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

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

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

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

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

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

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

148
  updateVisible();
×
149
}
×
150

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

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

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

171
std::span<const DecoderProtocol> CBUSInterface::decoderProtocols() const
×
172
{
173
  static constexpr std::array<DecoderProtocol, 2> protocols{DecoderProtocol::DCCShort, DecoderProtocol::DCCLong};
174
  return std::span<const DecoderProtocol>{protocols.data(), protocols.size()};
×
175
}
176

177
void CBUSInterface::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber)
×
178
{
179
  if(m_kernel)
×
180
  {
181
    const bool longAddress = (decoder.protocol == DecoderProtocol::DCCLong);
×
182

183
    if(has(changes, DecoderChangeFlags::FunctionValue) && functionNumber <= CBUS::engineFunctionMax)
×
184
    {
185
      m_kernel->setEngineFunction(
×
186
        decoder.address,
187
        longAddress,
188
        static_cast<uint8_t>(functionNumber),
189
        decoder.getFunctionValue(functionNumber));
×
190
    }
191
    else
192
    {
193
      m_kernel->setEngineSpeedDirection(
×
194
        decoder.address,
195
        longAddress,
196
        Decoder::throttleToSpeedStep<uint8_t>(decoder.throttle, decoder.speedSteps),
×
197
        decoder.speedSteps,
198
        decoder.emergencyStop,
199
        decoder.direction == Direction::Forward);
×
200
    }
201
  }
202
}
×
203

204
std::span<const InputChannel> CBUSInterface::inputChannels() const
×
205
{
206
  static const std::array<InputChannel, 2> channels{
207
    InputChannel::ShortEvent,
208
    InputChannel::LongEvent,
209
  };
210
  return channels;
×
211
}
212

213
bool CBUSInterface::isInputLocation(InputChannel channel, const InputLocation& location) const
×
214
{
215
  if(hasNodeAddressLocation(channel))
×
216
  {
217
    return
218
      inRange<uint32_t>(std::get<InputNodeAddress>(location).node, nodeNumberRange) &&
×
219
      inRange<uint32_t>(std::get<InputNodeAddress>(location).address, inputAddressMinMax(channel));
×
220
  }
221
  return InputController::isInputLocation(channel, location);
×
222
}
223

224
std::pair<uint32_t, uint32_t> CBUSInterface::inputAddressMinMax(InputChannel /*channel*/) const
×
225
{
226
  return eventNumberRange;
×
227
}
228

229
void CBUSInterface::inputSimulateChange(InputChannel channel, const InputLocation& location, SimulateInputAction action)
×
230
{
231
  if(m_simulator)
×
232
  {
233
    boost::asio::post(m_kernel->ioContext(),
×
234
      [this, channel, location, action]
×
235
      {
236
        switch(channel)
×
237
        {
238
          case InputChannel::ShortEvent:
×
239
            m_simulator->shortEvent(std::get<InputAddress>(location).address, action);
×
240
            break;
×
241

242
          case InputChannel::LongEvent:
×
243
            m_simulator->longEvent(std::get<InputNodeAddress>(location).node, std::get<InputNodeAddress>(location).address, action);
×
244
            break;
×
245

246
          default: [[unlikely]]
×
247
            assert(false);
×
248
            break;
249
        }
250
      });
×
251
  }
252
}
×
253

254
std::span<const OutputChannel> CBUSInterface::outputChannels() const
×
255
{
256
  static const std::array<OutputChannel, 4> channels{
257
    OutputChannel::ShortEvent,
258
    OutputChannel::LongEvent,
259
    OutputChannel::AccessoryDCC,
260
    OutputChannel::DCCext,
261
  };
262
  return channels;
×
263
}
264

265
std::pair<uint32_t, uint32_t> CBUSInterface::outputNodeMinMax(OutputChannel /*channel*/) const
×
266
{
267
  return eventNumberRange;
×
268
}
269

270
std::pair<uint32_t, uint32_t> CBUSInterface::outputAddressMinMax(OutputChannel channel) const
×
271
{
272
  switch(channel)
×
273
  {
274
    case OutputChannel::LongEvent:
×
275
    case OutputChannel::ShortEvent:
276
      return {std::numeric_limits<uint16_t>::min(), std::numeric_limits<uint16_t>::max()};
×
277

278
    default:
×
279
      return OutputController::outputAddressMinMax(channel);
×
280
  }
281
}
282

283
bool CBUSInterface::setOutputValue(OutputChannel channel, const OutputLocation& location, OutputValue value)
×
284
{
285
  if(m_kernel)
×
286
  {
287
    switch(channel)
×
288
    {
289
      case OutputChannel::ShortEvent:
×
290
        assert(std::holds_alternative<TriState>(value));
×
291
        if(auto v = std::get<TriState>(value); v != TriState::Undefined)
×
292
        {
293
          assert(std::holds_alternative<OutputAddress>(location));
×
294
          const auto address = static_cast<uint16_t>(std::get<OutputAddress>(location).address);
×
295
          m_kernel->setAccessoryShort(address, v == TriState::True);
×
296
          return true;
×
297
        }
298
        break;
×
299

300
      case OutputChannel::LongEvent:
×
301
        assert(std::holds_alternative<TriState>(value));
×
302
        if(auto v = std::get<TriState>(value); v != TriState::Undefined)
×
303
        {
304
          assert(std::holds_alternative<OutputNodeAddress>(location));
×
305
          const auto node = static_cast<uint16_t>(std::get<OutputNodeAddress>(location).node);
×
306
          const auto address = static_cast<uint16_t>(std::get<OutputNodeAddress>(location).address);
×
307
          m_kernel->setAccessory(node, address, v == TriState::True);
×
308
          return true;
×
309
        }
310
        break;
×
311

312
      case OutputChannel::AccessoryDCC:
×
313
        assert(std::holds_alternative<OutputPairValue>(value));
×
314
        if(const auto v = std::get<OutputPairValue>(value); v == OutputPairValue::First || v == OutputPairValue::Second)
×
315
        {
316
          assert(std::holds_alternative<OutputAddress>(location));
×
317
          const auto address = static_cast<uint16_t>(std::get<OutputAddress>(location).address);
×
318
          m_kernel->setDccAccessory(address, v == OutputPairValue::Second);
×
319
          return true;
×
320
        }
321
        break;
×
322

323
      case OutputChannel::DCCext:
×
324
        assert(std::holds_alternative<int16_t>(value));
×
325
        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()))
×
326
        {
327
          assert(std::holds_alternative<OutputAddress>(location));
×
328
          const auto address = static_cast<uint16_t>(std::get<OutputAddress>(location).address);
×
329
          m_kernel->setDccAdvancedAccessoryValue(address, static_cast<uint8_t>(v));
×
330
          return true;
×
331
        }
332
        break;
×
333

334
      default: [[unlikely]]
×
335
        assert(false);
×
336
        break;
337
    }
338
  }
339
  return false;
×
340
}
341

342
void CBUSInterface::addToWorld()
×
343
{
344
  Interface::addToWorld();
×
345
  DecoderController::addToWorld();
×
346
  InputController::addToWorld(inputListColumns);
×
347
  OutputController::addToWorld(outputListColumns);
×
348
}
×
349

350
void CBUSInterface::loaded()
×
351
{
352
  Interface::loaded();
×
353

354
  updateVisible();
×
355
}
×
356

357
void CBUSInterface::destroying()
×
358
{
359
  m_cbusPropertyChanged.disconnect();
×
360
  OutputController::destroying();
×
361
  InputController::destroying();
×
362
  DecoderController::destroying();
×
363
  Interface::destroying();
×
364
}
×
365

366
void CBUSInterface::worldEvent(WorldState state, WorldEvent event)
×
367
{
368
  Interface::worldEvent(state, event);
×
369

370
  switch(event)
×
371
  {
372
    case WorldEvent::PowerOff:
×
373
      if(m_kernel)
×
374
      {
375
        m_kernel->trackOff();
×
376
      }
377
      break;
×
378

379
    case WorldEvent::PowerOn:
×
380
      if(m_kernel)
×
381
      {
382
        m_kernel->trackOn();
×
383
      }
384
      break;
×
385

386
    case WorldEvent::Stop:
×
387
      if(m_kernel)
×
388
      {
389
        m_kernel->requestEmergencyStop();
×
390
      }
391
      break;
×
392

393
    case WorldEvent::Run:
×
394
      if(m_kernel)
×
395
      {
396
        // TODO: send all known speed values
397
      }
398
      break;
×
399

400
    default:
×
401
      break;
×
402
  }
403
}
×
404

405
bool CBUSInterface::setOnline(bool& value, bool simulation)
×
406
{
407
  if(!m_kernel && value)
×
408
  {
409
    try
410
    {
411
      if(simulation)
×
412
      {
413
        m_simulator = std::make_unique<CBUS::Simulator>();
×
414
        m_simulator->addModule(std::make_unique<CBUS::Module::CANCMD>(*m_simulator));
×
415
        m_simulator->addModule(std::make_unique<CBUS::Module::CANCAB>(*m_simulator));
×
416
        m_kernel = CBUS::Kernel::create<CBUS::SimulationIOHandler>(id.value(), cbus->config(), std::ref(*m_simulator));
×
417
      }
418
      else
419
      {
420
        switch(type)
×
421
        {
422
          case CBUSInterfaceType::CANUSB:
×
423
            m_kernel = CBUS::Kernel::create<CBUS::CANUSBIOHandler>(id.value(), cbus->config(), device.value());
×
424
            break;
×
425

426
          case CBUSInterfaceType::CANEther:
×
427
            m_kernel = CBUS::Kernel::create<CBUS::CANEtherIOHandler>(id.value(), cbus->config(), hostname.value(), port.value());
×
428
            break;
×
429

430
          case CBUSInterfaceType::SocketCAN:
×
431
#ifdef __linux__
432
            m_kernel = CBUS::Kernel::create<CBUS::SocketCANIOHandler>(id.value(), cbus->config(), interface.value(), canId.value());
×
433
            break;
×
434
#else
435
            setState(InterfaceState::Error);
436
            Log::log(*this, LogMessage::C2005_SOCKETCAN_IS_ONLY_AVAILABLE_ON_LINUX);
437
            return false;
438
#endif
439
        }
440
      }
441

442
      setState(InterfaceState::Initializing);
×
443

444
      m_kernel->setOnStarted(
×
445
        [this]()
×
446
        {
447
          setState(InterfaceState::Online);
×
448
        });
×
449
      m_kernel->setOnError(
×
450
        [this]()
×
451
        {
452
          setState(InterfaceState::Error);
×
453
          online = false; // communication no longer possible
×
454
        });
×
455
      m_kernel->onPresenceOfNode =
×
456
        [this](uint8_t canId_, uint16_t nodeNumber, uint8_t manufacturerId, uint8_t moduleId, bool flimMode, bool supportsServiceDiscovery)
×
457
        {
458
          cbusNodeList->add(CBUSNodeList::Node{
×
459
            .nodeNumber = nodeNumber,
460
            .canId = canId_,
461
            .manufacturerId = manufacturerId,
462
            .moduleId = moduleId,
463
            .flim = flimMode,
464
            .vlcb = supportsServiceDiscovery,
465
            .parameters = {},
466
          });
467
        };
×
468
      m_kernel->onNodeParameterResponse =
×
469
        [this](uint8_t canId_, uint16_t nodeNumber, CBUS::NodeParameter parameter, uint8_t parameterValue)
×
470
        {
471
          auto& nodes = cbusNodeList->m_nodes;
×
472
          if(auto it = std::find_if(nodes.begin(), nodes.end(),
×
473
              [canId_, nodeNumber](const auto& item)
×
474
              {
475
                return item.canId == canId_ && item.nodeNumber == nodeNumber;
×
476
              }); it != nodes.end())
×
477
          {
478
            switch(parameter)
×
479
            {
480
              using enum CBUS::NodeParameter;
481

482
              case VersionMajor:
×
483
                it->parameters.versionMajor = parameterValue;
×
484
                break;
×
485

486
              case VersionMinor:
×
487
                it->parameters.versionMinor = parameterValue;
×
488
                break;
×
489

490
              case BetaReleaseCode:
×
491
                it->parameters.betaReleaseCode = parameterValue;
×
492
                break;
×
493

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

588
      {
NEW
589
        std::vector<uint16_t> shortEvents;
×
NEW
590
        std::vector<std::pair<uint16_t, uint16_t>> longEvents;
×
591

NEW
592
        for(const auto& [key, input] : m_inputs)
×
593
        {
NEW
594
          if(input->channel == InputChannel::ShortEvent)
×
595
          {
NEW
596
            shortEvents.emplace_back(input->address);
×
597
          }
NEW
598
          else if(input->channel == InputChannel::LongEvent)
×
599
          {
NEW
600
            longEvents.emplace_back(input->node, input->address);
×
601
          }
602
        }
603

NEW
604
        for(const auto& [key, output] : m_outputs)
×
605
        {
NEW
606
          if(output->channel == OutputChannel::ShortEvent)
×
607
          {
NEW
608
            shortEvents.emplace_back(static_cast<const AddressOutput&>(*output).address);
×
609
          }
NEW
610
          else if(output->channel == OutputChannel::LongEvent)
×
611
          {
NEW
612
            longEvents.emplace_back(static_cast<const AddressOutput&>(*output).node, static_cast<const AddressOutput&>(*output).address);
×
613
          }
614
        }
615

NEW
616
        deduplicate(shortEvents);
×
NEW
617
        deduplicate(longEvents);
×
618

NEW
619
        m_kernel->setRequestEventsDuringInitialize(std::move(shortEvents), std::move(longEvents));
×
NEW
620
      }
×
621

UNCOV
622
      m_kernel->start();
×
623

624
      Attributes::setEnabled({type, device, hostname, port, interface, canId}, false);
×
625
    }
626
    catch(const LogMessageException& e)
×
627
    {
628
      setState(InterfaceState::Offline);
×
629
      Log::log(*this, e.message(), e.args());
×
630
      return false;
×
631
    }
×
632
  }
633
  else if(m_kernel && !value)
×
634
  {
635
    Attributes::setEnabled({type, device, hostname, port, interface, canId}, true);
×
636

637
    m_kernel->stop();
×
638
    EventLoop::deleteLater(m_kernel.release());
×
639
    EventLoop::deleteLater(m_simulator.release());
×
640

641
    cbusNodeList->clear();
×
642

643
    if(status->state != InterfaceState::Error)
×
644
    {
645
      setState(InterfaceState::Offline);
×
646
    }
647
  }
648
  return true;
×
649
}
650

651
void CBUSInterface::updateVisible()
×
652
{
653
  Attributes::setVisible(device, (type == CBUSInterfaceType::CANUSB));
×
654
  Attributes::setVisible({hostname, port}, (type == CBUSInterfaceType::CANEther));
×
655
  Attributes::setVisible({interface, canId}, (type == CBUSInterfaceType::SocketCAN));
×
656
}
×
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