• 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/protocol/cbus/cbuskernel.hpp
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
#ifndef TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_CBUS_CBUSKERNEL_HPP
23
#define TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_CBUS_CBUSKERNEL_HPP
24

25
#include "../kernelbase.hpp"
26
#include <map>
27
#include <span>
28
#include <set>
29
#include <queue>
30
#include <optional>
31
#include <boost/asio/steady_timer.hpp>
32
#include <traintastic/enum/direction.hpp>
33
#include "cbusconfig.hpp"
34
#include "iohandler/cbusiohandler.hpp"
35
#include "messages/cbusenginemessages.hpp"
36
#include "messages/cbusnodeparametermessages.hpp"
37
#include "../dcc/messages.hpp"
38

39
namespace CBUS {
40

41
class IOHub;
42

43
class Kernel : public ::KernelBase
44
{
45
public:
46
  std::function<void(uint8_t canId, uint16_t nodeNumber, uint8_t manufacturerId, uint8_t moduleId, bool flimMode, bool supportsServiceDiscovery)> onPresenceOfNode;
47
  std::function<void(uint8_t canId, uint16_t nodeNumber, NodeParameter parameter, uint8_t value)> onNodeParameterResponse;
48
  std::function<void()> onTrackOff;
49
  std::function<void()> onTrackOn;
50
  std::function<void()> onEmergencyStop;
51
  std::function<void(uint8_t session, bool external, uint16_t address, bool isLongAddress)> onEngineSessionAcquire;
52
  std::function<void(uint8_t session, uint8_t speed, bool forward)> onEngineSpeedDirectionChanged;
53
  std::function<void(uint8_t session, uint8_t number, bool on)> onEngineFunctionChanged;
54
  std::function<void(uint8_t session)> onEngineSessionReleased;
55
  std::function<void(uint16_t address, bool isLongAddress)> onEngineSessionCancelled;
56
  std::function<void(uint16_t eventNumber, bool on)> onShortEvent;
57
  std::function<void(uint16_t nodeNumber, uint16_t eventNumber, bool on)> onLongEvent;
58

59
  /**
60
   * @brief Create kernel and IO handler
61
   *
62
   * @param[in] config CBUS configuration
63
   * @param[in] args IO handler arguments
64
   * @return The kernel instance
65
   */
66
  template<class IOHandlerType, class... Args>
NEW
67
  static std::unique_ptr<Kernel> create(std::string logId_, const Config& config, Args... args)
×
68
  {
69
    static_assert(std::is_base_of_v<IOHandler, IOHandlerType>);
NEW
70
    std::unique_ptr<Kernel> kernel{new Kernel(std::move(logId_), config, isSimulation<IOHandlerType>())};
×
71
    kernel->setIOHandler(std::make_unique<IOHandlerType>(*kernel, std::forward<Args>(args)...));
×
72
    return kernel;
×
73
  }
×
74

75
#ifndef NDEBUG
76
  bool isKernelThread() const
×
77
  {
78
    return std::this_thread::get_id() == m_thread.get_id();
×
79
  }
80
#endif
81

82
  /**
83
   * @brief Set CBUS configuration
84
   *
85
   * @param[in] config The CBUS configuration
86
   */
87
  void setConfig(const Config& config);
88

89
  void setRequestEventsDuringInitialize(std::vector<uint16_t> shortEvents, std::vector<std::pair<uint16_t,uint16_t>> longEvents);
90

91
  /**
92
   * @brief Start the kernel and IO handler
93
   */
94
  void start();
95

96
  /**
97
   * @brief Stop the kernel and IO handler
98
   */
99
  void stop();
100

101
  /**
102
   * \brief Notify kernel the IO handler is started.
103
   * \note This function must run in the kernel's IO context
104
   */
105
  void started() final;
106

107
  size_t registerOnReceive(OpCode opCode, std::function<void(uint8_t, const Message&)> callback);
108
  void unregisterOnReceive(size_t handle);
109

110
  void trackOff();
111
  void trackOn();
112
  void requestEmergencyStop();
113

114
  void setEngineSpeedDirection(uint16_t address, bool longAddress, uint8_t speedStep, uint8_t speedSteps, bool eStop, bool directionForward);
115
  void setEngineFunction(uint16_t address, bool longAddress, uint8_t number, bool value);
116

117
  void setAccessoryShort(uint16_t deviceNumber, bool on);
118
  void setAccessory(uint16_t nodeNumber, uint16_t eventNumber, bool on);
119

120
  void setDccAccessory(uint16_t address, bool secondOutput);
121
  void setDccAdvancedAccessoryValue(uint16_t address, uint8_t aspect);
122

123
  bool send(std::vector<uint8_t> message);
124
  bool sendDCC(std::vector<uint8_t> dccPacket, uint8_t repeat);
125

126
private:
127
  //! Startup states, executed in order.
128
  enum class State
129
  {
130
    Initial, // must be first
131
    QueryNodes,
132
    ReadNodeParameters,
133
    GetCommandStationStatus,
134
    RequestShortEvents,
135
    RequestLongEvents,
136
    Started // must be last
137
  };
138

139
  enum class Owner
140
  {
141
    Traintastic = 1,
142
    CBUS = 2,
143
  };
144

145
  struct Engine
146
  {
147
    std::optional<uint8_t> session;
148
    Owner owner;
149
    uint8_t speed = 0;
150
    uint8_t speedSteps = 126;
151
    bool directionForward = true;
152
    std::map<uint8_t, bool> functions;
153
    std::chrono::steady_clock::time_point lastCommand;
154
  };
155

156
  std::unique_ptr<IOHandler> m_ioHandler;
157
  std::shared_ptr<IOHub> m_hub;
158
  const bool m_simulation;
159
  State m_state = State::Initial;
160
  boost::asio::steady_timer m_initializationTimer;
161
  std::queue<ReadNodeParameter> m_readNodeParameters;
162
  std::vector<uint16_t> m_initializationRequestShortEvents;
163
  std::vector<std::pair<uint16_t,uint16_t>> m_initializationRequestLongEvents;
164
  Config m_config;
165

166
  size_t m_onReceiveHandle = 0; // owned by eventloop thread
167
  std::unordered_map<size_t, std::function<void(uint8_t, const Message&)>> m_onReceiveCallbacks; // owned by eventloop thread
168
  std::unordered_map<size_t, OpCode> m_onReceiveFilters; // owned by kernel thread
169

170
  bool m_trackOn = false;
171
  uint8_t m_engineKeepAliveSession;
172
  bool m_engineKeepAliveTimerActive = false;
173
  boost::asio::steady_timer m_engineKeepAliveTimer;
174
  std::map<uint16_t, Engine> m_engines;
175
  std::map<uint16_t, Owner> m_engineGLOCs;
176
  std::queue<std::pair<std::chrono::steady_clock::time_point, DCC::SetSimpleAccessory>> m_dccAccessoryQueue;
177
  boost::asio::steady_timer m_dccAccessoryTimer;
178

179
  Kernel(std::string logId_, const Config& config, bool simulation);
180

181
  Kernel(const Kernel&) = delete;
182
  Kernel& operator =(const Kernel&) = delete;
183

184
  void setIOHandler(std::unique_ptr<IOHandler> handler);
185

186
  void send(const Message& message);
187
  void sendGetEngineSession(uint16_t address, bool longAddress);
188
  void sendSetEngineSessionMode(uint8_t session, uint8_t speedSteps);
189
  void sendSetEngineSpeedDirection(uint8_t session, uint8_t speed, bool directionForward);
190
  void sendSetEngineFunction(uint8_t session, uint8_t number, bool value);
191

192
  void receive(const CAN::Message& canMessage);
193
  void receiveGLOC(uint16_t address, bool longAddress, GetEngineSession::Mode mode);
194
  void receiveDFUN(const SetEngineFunctions& message);
195
  void receiveDFNOx(const SetEngineFunction& message);
196
  void receiveDSPD(const SetEngineSpeedDirection& message);
197
  void receiveKLOC(const ReleaseEngine& message);
198
  void receiveShortEvent(uint16_t eventNumber, bool on);
199
  void receiveLongEvent(uint16_t nodeNumber, uint16_t eventNumber, bool on);
200

201
  inline void nextState()
×
202
  {
203
    assert(m_state != State::Started);
×
204
    changeState(static_cast<State>(static_cast<std::underlying_type_t<State>>(m_state) + 1));
×
205
  }
×
206

207
  void changeState(State value);
208

209
  void readNodeParameter();
210
  void requestShortEvent();
211
  void requestLongEvent();
212

213
  void restartInitializationTimer(std::chrono::milliseconds timeout);
214
  void restartEngineKeepAliveTimer();
215
  void startDccAccessoryTimer();
216
};
217

218
}
219

220
#endif
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