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

traintastic / traintastic / 22554474183

01 Mar 2026 09:47PM UTC coverage: 27.1%. First build
22554474183

push

github

reinder
[cbus] added simulator basics

0 of 56 new or added lines in 8 files covered. (0.0%)

8182 of 30192 relevant lines covered (27.1%)

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

40
constexpr auto outputListColumns = OutputListColumn::Channel | OutputListColumn::Address;
41

42
CREATE_IMPL(CBUSInterface)
×
43

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

60
  Attributes::addDisplayName(type, DisplayName::Interface::type);
×
61
  Attributes::addEnabled(type, !online);
×
62
  Attributes::addValues(type, CBUSInterfaceTypeValues);
×
63
  m_interfaceItems.insertBefore(type, notes);
×
64

65
  Attributes::addEnabled(device, !online);
×
66
  Attributes::addVisible(device, false);
×
67
  m_interfaceItems.insertBefore(device, notes);
×
68

69
  Attributes::addDisplayName(hostname, DisplayName::IP::hostname);
×
70
  Attributes::addEnabled(hostname, !online);
×
71
  Attributes::addVisible(hostname, false);
×
72
  m_interfaceItems.insertBefore(hostname, notes);
×
73

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

79
  m_interfaceItems.insertBefore(cbus, notes);
×
80

81
  m_interfaceItems.insertBefore(outputs, notes);
×
82

83
  m_cbusPropertyChanged = cbus->propertyChanged.connect(
×
84
    [this](BaseProperty& /*property*/)
×
85
    {
86
      if(m_kernel)
×
87
      {
88
        m_kernel->setConfig(cbus->config());
×
89
      }
90
    });
×
91

92
  updateVisible();
×
93
}
×
94

95
CBUSInterface::~CBUSInterface() = default;
×
96

97
bool CBUSInterface::send(std::vector<uint8_t> message)
×
98
{
99
  if(m_kernel)
×
100
  {
101
    return m_kernel->send(std::move(message));
×
102
  }
103
  return false;
×
104
}
105

106
bool CBUSInterface::sendDCC(std::vector<uint8_t> dccPacket, uint8_t repeat)
×
107
{
108
  if(m_kernel)
×
109
  {
110
    return m_kernel->sendDCC(std::move(dccPacket), repeat);
×
111
  }
112
  return false;
×
113
}
114

115
std::span<const OutputChannel> CBUSInterface::outputChannels() const
×
116
{
117
  static const std::array<OutputChannel, 2> channels{
118
    OutputChannel::CBUSAccessoryShort,
119
    OutputChannel::CBUSAccessory,
120
    //OutputChannel::AccessoryDCC,
121
    //OutputChannel::DCCext,
122
  };
123
  return channels;
×
124
}
125

126
std::pair<uint32_t, uint32_t> CBUSInterface::outputAddressMinMax(OutputChannel channel) const
×
127
{
128
  switch(channel)
×
129
  {
130
    case OutputChannel::CBUSAccessory:
×
131
    case OutputChannel::CBUSAccessoryShort:
132
      return {std::numeric_limits<uint16_t>::min(), std::numeric_limits<uint16_t>::max()};
×
133

134
    default:
×
135
      return OutputController::outputAddressMinMax(channel);
×
136
  }
137
}
138

139
bool CBUSInterface::setOutputValue(OutputChannel channel, uint32_t address, OutputValue value)
×
140
{
141
  if(m_kernel)
×
142
  {
143
    switch(channel)
×
144
    {
145
      case OutputChannel::CBUSAccessoryShort:
×
146
        if(auto v = std::get<TriState>(value); v != TriState::Undefined)
×
147
        {
148
          m_kernel->setAccessoryShort(static_cast<uint16_t>(address), v == TriState::True);
×
149
          return true;
×
150
        }
151
        break;
×
152

153
      case OutputChannel::CBUSAccessory:
×
154
        if(auto v = std::get<TriState>(value); v != TriState::Undefined)
×
155
        {
156
          m_kernel->setAccessory(static_cast<uint16_t>(address), v == TriState::True);
×
157
          return true;
×
158
        }
159
        break;
×
160

161
      default: [[unlikely]]
×
162
        assert(false);
×
163
        break;
164
    }
165
  }
166
  return false;
×
167
}
168

169
void CBUSInterface::addToWorld()
×
170
{
171
  Interface::addToWorld();
×
172
  OutputController::addToWorld(outputListColumns);
×
173
}
×
174

175
void CBUSInterface::loaded()
×
176
{
177
  Interface::loaded();
×
178

179
  updateVisible();
×
180
}
×
181

182
void CBUSInterface::destroying()
×
183
{
184
  m_cbusPropertyChanged.disconnect();
×
185
  OutputController::destroying();
×
186
  Interface::destroying();
×
187
}
×
188

189
void CBUSInterface::worldEvent(WorldState state, WorldEvent event)
×
190
{
191
  Interface::worldEvent(state, event);
×
192

193
  switch(event)
×
194
  {
195
    case WorldEvent::PowerOff:
×
196
      if(m_kernel)
×
197
      {
198
        m_kernel->trackOff();
×
199
      }
200
      break;
×
201

202
    case WorldEvent::PowerOn:
×
203
      if(m_kernel)
×
204
      {
205
        m_kernel->trackOn();
×
206
      }
207
      break;
×
208

209
    case WorldEvent::Stop:
×
210
      if(m_kernel)
×
211
      {
212
        m_kernel->requestEmergencyStop();
×
213
      }
214
      break;
×
215

216
    case WorldEvent::Run:
×
217
      if(m_kernel)
×
218
      {
219
        // TODO: send all known speed values
220
      }
221
      break;
×
222

223
    default:
×
224
      break;
×
225
  }
226
}
×
227

228
bool CBUSInterface::setOnline(bool& value, bool simulation)
×
229
{
230
  if(!m_kernel && value)
×
231
  {
232
    try
233
    {
234
      if(simulation)
×
235
      {
236
        m_simulator = std::make_unique<CBUS::Simulator>();
×
NEW
237
        m_simulator->addModule(std::make_unique<CBUS::Module::CANCMD>(*m_simulator));
×
238
        m_kernel = CBUS::Kernel::create<CBUS::SimulationIOHandler>(id.value(), cbus->config(), std::ref(*m_simulator));
×
239
      }
240
      else
241
      {
242
        switch(type)
×
243
        {
244
          case CBUSInterfaceType::CANUSB:
×
245
            m_kernel = CBUS::Kernel::create<CBUS::CANUSBIOHandler>(id.value(), cbus->config(), device.value());
×
246
            break;
×
247

248
          case CBUSInterfaceType::CANEther:
×
249
            m_kernel = CBUS::Kernel::create<CBUS::CANEtherIOHandler>(id.value(), cbus->config(), hostname.value(), port.value());
×
250
            break;
×
251
        }
252
      }
253

254
      setState(InterfaceState::Initializing);
×
255

256
      m_kernel->setOnStarted(
×
257
        [this]()
×
258
        {
259
          setState(InterfaceState::Online);
×
260
        });
×
261
      m_kernel->setOnError(
×
262
        [this]()
×
263
        {
264
          setState(InterfaceState::Error);
×
265
          online = false; // communication no longer possible
×
266
        });
×
267
      m_kernel->onTrackOff =
×
268
        [this]()
×
269
        {
270
          m_world.powerOff();
×
271
        };
×
272
      m_kernel->onTrackOn =
×
273
        [this]()
×
274
        {
275
          m_world.powerOn();
×
276
        };
×
277
      m_kernel->onEmergencyStop =
×
278
        [this]()
×
279
        {
280
          m_world.stop();
×
281
        };
×
282

283
      m_kernel->start();
×
284
    }
285
    catch(const LogMessageException& e)
×
286
    {
287
      setState(InterfaceState::Offline);
×
288
      Log::log(*this, e.message(), e.args());
×
289
      return false;
×
290
    }
×
291
  }
292
  else if(m_kernel && !value)
×
293
  {
294
    m_kernel->stop();
×
295
    EventLoop::deleteLater(m_kernel.release());
×
296
    EventLoop::deleteLater(m_simulator.release());
×
297

298
    if(status->state != InterfaceState::Error)
×
299
    {
300
      setState(InterfaceState::Offline);
×
301
    }
302
  }
303
  return true;
×
304
}
305

306
void CBUSInterface::updateVisible()
×
307
{
308
  const bool isSerial = (type == CBUSInterfaceType::CANUSB);
×
309
  Attributes::setVisible(device, isSerial);
×
310

311
  const bool isNetwork = (type == CBUSInterfaceType::CANEther);
×
312
  Attributes::setVisible(hostname, isNetwork);
×
313
  Attributes::setVisible(port, isNetwork);
×
314
}
×
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