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

traintastic / traintastic / 22823086659

08 Mar 2026 02:30PM UTC coverage: 26.831% (+0.06%) from 26.774%
22823086659

push

github

reinder
[cbus] renamed CBUSAccessory(Short) to Long/Short event and added node setting for Long events.

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

1909 existing lines in 38 files now uncovered.

8230 of 30674 relevant lines covered (26.83%)

186.8 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/cbussettings.hpp"
24
#include "../decoder/decoderchangeflags.hpp"
25
#include "../decoder/list/decoderlist.hpp"
26
#include "../decoder/list/decoderlisttablemodel.hpp"
27
#include "../output/list/outputlist.hpp"
28
#include "../protocol/cbus/cbusconst.hpp"
29
#include "../protocol/cbus/cbuskernel.hpp"
30
#include "../protocol/cbus/iohandler/cbuscanusbiohandler.hpp"
31
#include "../protocol/cbus/iohandler/cbuscanetheriohandler.hpp"
32
#include "../protocol/cbus/iohandler/cbussimulationiohandler.hpp"
33
#include "../protocol/cbus/simulator/cbussimulator.hpp"
34
#include "../protocol/cbus/simulator/module/cbuscancmd.hpp"
35
#include "../../core/attributes.hpp"
36
#include "../../core/eventloop.hpp"
37
#include "../../core/method.tpp"
38
#include "../../core/objectproperty.tpp"
39
#include "../../log/log.hpp"
40
#include "../../log/logmessageexception.hpp"
41
#include "../../utils/displayname.hpp"
42
#include "../../world/world.hpp"
43

44
constexpr auto decoderListColumns = DecoderListColumn::Id | DecoderListColumn::Name | DecoderListColumn::Address;
45
constexpr auto outputListColumns = OutputListColumn::Channel | OutputListColumn::Node | OutputListColumn::Address;
46

47
CREATE_IMPL(CBUSInterface)
×
48

49
CBUSInterface::CBUSInterface(World& world, std::string_view _id)
×
50
  : Interface(world, _id)
51
  , DecoderController(*this, decoderListColumns)
52
  , OutputController(static_cast<IdObject&>(*this))
53
  , type{this, "type", CBUSInterfaceType::CANUSB, PropertyFlags::ReadWrite | PropertyFlags::Store,
×
54
      [this](CBUSInterfaceType /*value*/)
×
55
      {
56
        updateVisible();
×
57
      }}
×
58
  , device{this, "device", "", PropertyFlags::ReadWrite | PropertyFlags::Store}
×
59
  , hostname{this, "hostname", "", PropertyFlags::ReadWrite | PropertyFlags::Store}
×
60
  , port{this, "port", 0, PropertyFlags::ReadWrite | PropertyFlags::Store}
×
61
  , cbus{this, "cbus", nullptr, PropertyFlags::ReadOnly | PropertyFlags::Store | PropertyFlags::SubObject}
×
62
{
63
  name = "CBUS/VLCB";
×
64
  cbus.setValueInternal(std::make_shared<CBUSSettings>(*this, cbus.name()));
×
65

66
  Attributes::addDisplayName(type, DisplayName::Interface::type);
×
67
  Attributes::addEnabled(type, !online);
×
68
  Attributes::addValues(type, CBUSInterfaceTypeValues);
×
69
  m_interfaceItems.insertBefore(type, notes);
×
70

71
  Attributes::addEnabled(device, !online);
×
72
  Attributes::addVisible(device, false);
×
73
  m_interfaceItems.insertBefore(device, notes);
×
74

75
  Attributes::addDisplayName(hostname, DisplayName::IP::hostname);
×
76
  Attributes::addEnabled(hostname, !online);
×
77
  Attributes::addVisible(hostname, false);
×
78
  m_interfaceItems.insertBefore(hostname, notes);
×
79

80
  Attributes::addDisplayName(port, DisplayName::IP::port);
×
81
  Attributes::addEnabled(port, !online);
×
82
  Attributes::addVisible(port, false);
×
83
  m_interfaceItems.insertBefore(port, notes);
×
84

85
  m_interfaceItems.insertBefore(cbus, notes);
×
86

87
  m_interfaceItems.insertBefore(decoders, notes);
×
88

89
  m_interfaceItems.insertBefore(outputs, notes);
×
90

91
  m_cbusPropertyChanged = cbus->propertyChanged.connect(
×
92
    [this](BaseProperty& /*property*/)
×
93
    {
94
      if(m_kernel)
×
95
      {
96
        m_kernel->setConfig(cbus->config());
×
97
      }
98
    });
×
99

100
  updateVisible();
×
101
}
×
102

103
CBUSInterface::~CBUSInterface() = default;
×
104

105
bool CBUSInterface::send(std::vector<uint8_t> message)
×
106
{
107
  if(m_kernel)
×
108
  {
109
    return m_kernel->send(std::move(message));
×
110
  }
111
  return false;
×
112
}
113

114
bool CBUSInterface::sendDCC(std::vector<uint8_t> dccPacket, uint8_t repeat)
×
115
{
116
  if(m_kernel)
×
117
  {
118
    return m_kernel->sendDCC(std::move(dccPacket), repeat);
×
119
  }
120
  return false;
×
121
}
122

123
std::span<const DecoderProtocol> CBUSInterface::decoderProtocols() const
×
124
{
125
  static constexpr std::array<DecoderProtocol, 2> protocols{DecoderProtocol::DCCShort, DecoderProtocol::DCCLong};
126
  return std::span<const DecoderProtocol>{protocols.data(), protocols.size()};
×
127
}
128

129
void CBUSInterface::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber)
×
130
{
131
  if(m_kernel)
×
132
  {
133
    const bool longAddress = (decoder.protocol == DecoderProtocol::DCCLong);
×
134

135
    if(has(changes, DecoderChangeFlags::FunctionValue) && functionNumber <= CBUS::engineFunctionMax)
×
136
    {
137
      m_kernel->setEngineFunction(
×
138
        decoder.address,
139
        longAddress,
140
        static_cast<uint8_t>(functionNumber),
141
        decoder.getFunctionValue(functionNumber));
×
142
    }
143
    else
144
    {
145
      m_kernel->setEngineSpeedDirection(
×
146
        decoder.address,
147
        longAddress,
148
        Decoder::throttleToSpeedStep<uint8_t>(decoder.throttle, decoder.speedSteps),
×
149
        decoder.speedSteps,
150
        decoder.emergencyStop,
151
        decoder.direction == Direction::Forward);
×
152
    }
153
  }
154
}
×
155

156
std::span<const OutputChannel> CBUSInterface::outputChannels() const
×
157
{
158
  static const std::array<OutputChannel, 2> channels{
159
    OutputChannel::ShortEvent,
160
    OutputChannel::LongEvent,
161
    //OutputChannel::AccessoryDCC,
162
    //OutputChannel::DCCext,
163
  };
164
  return channels;
×
165
}
166

NEW
167
std::pair<uint32_t, uint32_t> CBUSInterface::outputNodeMinMax(OutputChannel /*channel*/) const
×
168
{
NEW
169
  return {std::numeric_limits<uint16_t>::min(), std::numeric_limits<uint16_t>::max()};
×
170
}
171

UNCOV
172
std::pair<uint32_t, uint32_t> CBUSInterface::outputAddressMinMax(OutputChannel channel) const
×
173
{
174
  switch(channel)
×
175
  {
NEW
176
    case OutputChannel::LongEvent:
×
177
    case OutputChannel::ShortEvent:
UNCOV
178
      return {std::numeric_limits<uint16_t>::min(), std::numeric_limits<uint16_t>::max()};
×
179

180
    default:
×
181
      return OutputController::outputAddressMinMax(channel);
×
182
  }
183
}
184

NEW
185
bool CBUSInterface::setOutputValue(OutputChannel channel, const OutputLocation& location, OutputValue value)
×
186
{
187
  if(m_kernel)
×
188
  {
189
    switch(channel)
×
190
    {
NEW
191
      case OutputChannel::ShortEvent:
×
192
        if(auto v = std::get<TriState>(value); v != TriState::Undefined)
×
193
        {
NEW
194
          const auto address = static_cast<uint16_t>(std::get<OutputAddress>(location).address);
×
NEW
195
          m_kernel->setAccessoryShort(address, v == TriState::True);
×
UNCOV
196
          return true;
×
197
        }
198
        break;
×
199

NEW
200
      case OutputChannel::LongEvent:
×
201
        if(auto v = std::get<TriState>(value); v != TriState::Undefined)
×
202
        {
NEW
203
          const auto node = static_cast<uint16_t>(std::get<OutputNodeAddress>(location).node);
×
NEW
204
          const auto address = static_cast<uint16_t>(std::get<OutputNodeAddress>(location).address);
×
NEW
205
          m_kernel->setAccessory(node, address, v == TriState::True);
×
UNCOV
206
          return true;
×
207
        }
208
        break;
×
209

210
      default: [[unlikely]]
×
211
        assert(false);
×
212
        break;
213
    }
214
  }
215
  return false;
×
216
}
217

218
void CBUSInterface::addToWorld()
×
219
{
220
  Interface::addToWorld();
×
221
  DecoderController::addToWorld();
×
222
  OutputController::addToWorld(outputListColumns);
×
223
}
×
224

225
void CBUSInterface::loaded()
×
226
{
227
  Interface::loaded();
×
228

229
  updateVisible();
×
230
}
×
231

232
void CBUSInterface::destroying()
×
233
{
234
  m_cbusPropertyChanged.disconnect();
×
235
  OutputController::destroying();
×
236
  DecoderController::destroying();
×
237
  Interface::destroying();
×
238
}
×
239

240
void CBUSInterface::worldEvent(WorldState state, WorldEvent event)
×
241
{
242
  Interface::worldEvent(state, event);
×
243

244
  switch(event)
×
245
  {
246
    case WorldEvent::PowerOff:
×
247
      if(m_kernel)
×
248
      {
249
        m_kernel->trackOff();
×
250
      }
251
      break;
×
252

253
    case WorldEvent::PowerOn:
×
254
      if(m_kernel)
×
255
      {
256
        m_kernel->trackOn();
×
257
      }
258
      break;
×
259

260
    case WorldEvent::Stop:
×
261
      if(m_kernel)
×
262
      {
263
        m_kernel->requestEmergencyStop();
×
264
      }
265
      break;
×
266

267
    case WorldEvent::Run:
×
268
      if(m_kernel)
×
269
      {
270
        // TODO: send all known speed values
271
      }
272
      break;
×
273

274
    default:
×
275
      break;
×
276
  }
277
}
×
278

279
bool CBUSInterface::setOnline(bool& value, bool simulation)
×
280
{
281
  if(!m_kernel && value)
×
282
  {
283
    try
284
    {
285
      if(simulation)
×
286
      {
287
        m_simulator = std::make_unique<CBUS::Simulator>();
×
288
        m_simulator->addModule(std::make_unique<CBUS::Module::CANCMD>(*m_simulator));
×
289
        m_kernel = CBUS::Kernel::create<CBUS::SimulationIOHandler>(id.value(), cbus->config(), std::ref(*m_simulator));
×
290
      }
291
      else
292
      {
293
        switch(type)
×
294
        {
295
          case CBUSInterfaceType::CANUSB:
×
296
            m_kernel = CBUS::Kernel::create<CBUS::CANUSBIOHandler>(id.value(), cbus->config(), device.value());
×
297
            break;
×
298

299
          case CBUSInterfaceType::CANEther:
×
300
            m_kernel = CBUS::Kernel::create<CBUS::CANEtherIOHandler>(id.value(), cbus->config(), hostname.value(), port.value());
×
301
            break;
×
302
        }
303
      }
304

305
      setState(InterfaceState::Initializing);
×
306

307
      m_kernel->setOnStarted(
×
308
        [this]()
×
309
        {
310
          setState(InterfaceState::Online);
×
311
        });
×
312
      m_kernel->setOnError(
×
313
        [this]()
×
314
        {
315
          setState(InterfaceState::Error);
×
316
          online = false; // communication no longer possible
×
317
        });
×
318
      m_kernel->onTrackOff =
×
319
        [this]()
×
320
        {
321
          m_world.powerOff();
×
322
        };
×
323
      m_kernel->onTrackOn =
×
324
        [this]()
×
325
        {
326
          m_world.powerOn();
×
327
        };
×
328
      m_kernel->onEmergencyStop =
×
329
        [this]()
×
330
        {
331
          m_world.stop();
×
332
        };
×
333

334
      m_kernel->start();
×
335
    }
336
    catch(const LogMessageException& e)
×
337
    {
338
      setState(InterfaceState::Offline);
×
339
      Log::log(*this, e.message(), e.args());
×
340
      return false;
×
341
    }
×
342
  }
343
  else if(m_kernel && !value)
×
344
  {
345
    m_kernel->stop();
×
346
    EventLoop::deleteLater(m_kernel.release());
×
347
    EventLoop::deleteLater(m_simulator.release());
×
348

349
    if(status->state != InterfaceState::Error)
×
350
    {
351
      setState(InterfaceState::Offline);
×
352
    }
353
  }
354
  return true;
×
355
}
356

357
void CBUSInterface::updateVisible()
×
358
{
359
  const bool isSerial = (type == CBUSInterfaceType::CANUSB);
×
360
  Attributes::setVisible(device, isSerial);
×
361

362
  const bool isNetwork = (type == CBUSInterfaceType::CANEther);
×
363
  Attributes::setVisible(hostname, isNetwork);
×
364
  Attributes::setVisible(port, isNetwork);
×
365
}
×
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