[cbus] added interface/kernel/iohandlers (untested due to leak of hardware)

Dieser Commit ist enthalten in:
Reinder Feenstra 2026-02-20 23:52:03 +01:00
Ursprung 2bfbbb267f
Commit ff784fcf49
24 geänderte Dateien mit 2051 neuen und 5 gelöschten Zeilen

Datei anzeigen

@ -105,6 +105,7 @@ file(GLOB SOURCES
"src/hardware/input/monitor/*.cpp"
"src/hardware/interface/*.hpp"
"src/hardware/interface/*.cpp"
"src/hardware/interface/cbus/*.cpp"
"src/hardware/interface/marklincan/*.hpp"
"src/hardware/interface/marklincan/*.cpp"
"src/hardware/output/*.hpp"
@ -119,6 +120,8 @@ file(GLOB SOURCES
"src/hardware/programming/lncv/*.cpp"
"src/hardware/protocol/*.hpp"
"src/hardware/protocol/*.cpp"
"src/hardware/protocol/cbus/*.cpp"
"src/hardware/protocol/cbus/iohandler/*.cpp"
"src/hardware/protocol/dccex/*.hpp"
"src/hardware/protocol/dccex/*.cpp"
"src/hardware/protocol/dccex/iohandler/*.hpp"

Datei anzeigen

@ -0,0 +1,40 @@
/**
* This file is part of Traintastic,
* see <https://github.com/traintastic/traintastic>.
*
* Copyright (C) 2026 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "cbussettings.hpp"
#include "../../../core/attributes.hpp"
#include "../../../utils/displayname.hpp"
CBUSSettings::CBUSSettings(Object& _parent, std::string_view parentPropertyName)
: SubObject(_parent, parentPropertyName)
, debugLogRXTX{this, "debug_log_rx_tx", false, PropertyFlags::ReadWrite | PropertyFlags::Store}
{
Attributes::addDisplayName(debugLogRXTX, DisplayName::Hardware::debugLogRXTX);
//Attributes::addGroup(debugLogRXTX, Group::debug);
m_interfaceItems.add(debugLogRXTX);
}
CBUS::Config CBUSSettings::config() const
{
return CBUS::Config{
.debugLogRXTX = debugLogRXTX,
};
}

Datei anzeigen

@ -0,0 +1,41 @@
/**
* This file is part of Traintastic,
* see <https://github.com/traintastic/traintastic>.
*
* Copyright (C) 2026 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef TRAINTASTIC_SERVER_HARDWARE_INTERFACE_CBUS_CBUSSETTINGS_HPP
#define TRAINTASTIC_SERVER_HARDWARE_INTERFACE_CBUS_CBUSSETTINGS_HPP
#include "../../../core/subobject.hpp"
#include "../../../core/property.hpp"
#include "../../../hardware/protocol/cbus/cbusconfig.hpp"
class CBUSSettings final : public SubObject
{
CLASS_ID("cbus_settings")
public:
Property<bool> debugLogRXTX;
CBUSSettings(Object& _parent, std::string_view parentPropertyName);
CBUS::Config config() const;
};
#endif

Datei anzeigen

@ -0,0 +1,238 @@
/**
* This file is part of Traintastic,
* see <https://github.com/traintastic/traintastic>.
*
* Copyright (C) 2026 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "cbusinterface.hpp"
#include "cbus/cbussettings.hpp"
#include "../protocol/cbus/cbuskernel.hpp"
#include "../protocol/cbus/iohandler/cbuscanusbiohandler.hpp"
#include "../protocol/cbus/iohandler/cbuscanetheriohandler.hpp"
/*
#include "../protocol/cbus/simulator/cbussimulator.hpp"
*/
#include "../../core/attributes.hpp"
#include "../../core/eventloop.hpp"
#include "../../core/method.tpp"
#include "../../core/objectproperty.tpp"
#include "../../log/log.hpp"
#include "../../log/logmessageexception.hpp"
#include "../../utils/displayname.hpp"
#include "../../world/world.hpp"
CREATE_IMPL(CBUSInterface)
CBUSInterface::CBUSInterface(World& world, std::string_view _id)
: Interface(world, _id)
, type{this, "type", CBUSInterfaceType::CANUSB, PropertyFlags::ReadWrite | PropertyFlags::Store,
[this](CBUSInterfaceType /*value*/)
{
updateVisible();
}}
, device{this, "device", "", PropertyFlags::ReadWrite | PropertyFlags::Store}
, hostname{this, "hostname", "", PropertyFlags::ReadWrite | PropertyFlags::Store}
, port{this, "port", 0, PropertyFlags::ReadWrite | PropertyFlags::Store}
, cbus{this, "cbus", nullptr, PropertyFlags::ReadOnly | PropertyFlags::Store | PropertyFlags::SubObject}
{
name = "CBUS/VLCB";
cbus.setValueInternal(std::make_shared<CBUSSettings>(*this, cbus.name()));
Attributes::addDisplayName(type, DisplayName::Interface::type);
Attributes::addEnabled(type, !online);
Attributes::addValues(type, CBUSInterfaceTypeValues);
m_interfaceItems.insertBefore(type, notes);
Attributes::addEnabled(device, !online);
Attributes::addVisible(device, false);
m_interfaceItems.insertBefore(device, notes);
Attributes::addDisplayName(hostname, DisplayName::IP::hostname);
Attributes::addEnabled(hostname, !online);
Attributes::addVisible(hostname, false);
m_interfaceItems.insertBefore(hostname, notes);
Attributes::addDisplayName(port, DisplayName::IP::port);
Attributes::addEnabled(port, !online);
Attributes::addVisible(port, false);
m_interfaceItems.insertBefore(port, notes);
m_interfaceItems.insertBefore(cbus, notes);
m_cbusPropertyChanged = cbus->propertyChanged.connect(
[this](BaseProperty& /*property*/)
{
if(m_kernel)
{
m_kernel->setConfig(cbus->config());
}
});
updateVisible();
}
CBUSInterface::~CBUSInterface() = default;
void CBUSInterface::addToWorld()
{
Interface::addToWorld();
}
void CBUSInterface::loaded()
{
Interface::loaded();
updateVisible();
}
void CBUSInterface::destroying()
{
m_cbusPropertyChanged.disconnect();
Interface::destroying();
}
void CBUSInterface::worldEvent(WorldState state, WorldEvent event)
{
Interface::worldEvent(state, event);
switch(event)
{
case WorldEvent::PowerOff:
if(m_kernel)
{
m_kernel->trackOff();
}
break;
case WorldEvent::PowerOn:
if(m_kernel)
{
m_kernel->trackOn();
}
break;
case WorldEvent::Stop:
if(m_kernel)
{
m_kernel->requestEmergencyStop();
}
break;
case WorldEvent::Run:
if(m_kernel)
{
// TODO: send all known speed values
}
break;
default:
break;
}
}
bool CBUSInterface::setOnline(bool& value, bool simulation)
{
if(!m_kernel && value)
{
try
{
(void)simulation; // silence warning until simulation is added
/* TODO: simulation support
if(simulation)
{
m_simulator = std::make_unique<CBUS::Simulator>();
m_kernel = CBUS::Kernel::create<CBUS::SimulationIOHandler>(id.value(), cbus->config(), std::ref(*m_simulator));
}
else
*/
{
switch(type)
{
case CBUSInterfaceType::CANUSB:
m_kernel = CBUS::Kernel::create<CBUS::CANUSBIOHandler>(id.value(), cbus->config(), device.value());
break;
case CBUSInterfaceType::CANEther:
m_kernel = CBUS::Kernel::create<CBUS::CANEtherIOHandler>(id.value(), cbus->config(), hostname.value(), port.value());
break;
}
}
setState(InterfaceState::Initializing);
m_kernel->setOnStarted(
[this]()
{
setState(InterfaceState::Online);
});
m_kernel->setOnError(
[this]()
{
setState(InterfaceState::Error);
online = false; // communication no longer possible
});
m_kernel->onTrackOff =
[this]()
{
m_world.powerOff();
};
m_kernel->onTrackOn =
[this]()
{
m_world.powerOn();
};
m_kernel->onEmergencyStop =
[this]()
{
m_world.stop();
};
m_kernel->start();
}
catch(const LogMessageException& e)
{
setState(InterfaceState::Offline);
Log::log(*this, e.message(), e.args());
return false;
}
}
else if(m_kernel && !value)
{
m_kernel->stop();
EventLoop::deleteLater(m_kernel.release());
/*
EventLoop::deleteLater(m_simulator.release());
*/
if(status->state != InterfaceState::Error)
{
setState(InterfaceState::Offline);
}
}
return true;
}
void CBUSInterface::updateVisible()
{
const bool isSerial = (type == CBUSInterfaceType::CANUSB);
Attributes::setVisible(device, isSerial);
const bool isNetwork = (type == CBUSInterfaceType::CANEther);
Attributes::setVisible(hostname, isNetwork);
Attributes::setVisible(port, isNetwork);
}

Datei anzeigen

@ -0,0 +1,72 @@
/**
* This file is part of Traintastic,
* see <https://github.com/traintastic/traintastic>.
*
* Copyright (C) 2026 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef TRAINTASTIC_SERVER_HARDWARE_INTERFACE_CBUSINTERFACE_HPP
#define TRAINTASTIC_SERVER_HARDWARE_INTERFACE_CBUSINTERFACE_HPP
#include "interface.hpp"
#include "../../core/serialdeviceproperty.hpp"
#include <traintastic/enum/cbusinterfacetype.hpp>
class CBUSSettings;
namespace CBUS {
class Kernel;
class Simulator {};
}
/**
* @brief CBUS hardware interface
*/
class CBUSInterface final
: public Interface
{
CLASS_ID("interface.cbus")
DEFAULT_ID("cbus")
CREATE_DEF(CBUSInterface)
public:
Property<CBUSInterfaceType> type;
SerialDeviceProperty device;
Property<std::string> hostname;
Property<uint16_t> port;
ObjectProperty<CBUSSettings> cbus;
CBUSInterface(World& world, std::string_view _id);
~CBUSInterface() final;
protected:
void addToWorld() final;
void loaded() final;
void destroying() final;
void worldEvent(WorldState state, WorldEvent event) final;
bool setOnline(bool& value, bool simulation) final;
private:
std::unique_ptr<CBUS::Kernel> m_kernel;
std::unique_ptr<CBUS::Simulator> m_simulator;
boost::signals2::connection m_cbusPropertyChanged;
void updateVisible();
};
#endif

Datei anzeigen

@ -1,9 +1,8 @@
/**
* server/src/hardware/interface/interfaces.cpp
* This file is part of Traintastic,
* see <https://github.com/traintastic/traintastic>.
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021-2025 Reinder Feenstra
* Copyright (C) 2021-2026 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -25,6 +24,7 @@
#include "../../world/world.hpp"
#include "../../utils/makearray.hpp"
#include "cbusinterface.hpp"
#include "dccexinterface.hpp"
#include "ecosinterface.hpp"
#include "hsi88.hpp"
@ -39,6 +39,7 @@
std::span<const std::string_view> Interfaces::classList()
{
static constexpr auto classes = makeArray(
CBUSInterface::classId,
DCCEXInterface::classId,
ECoSInterface::classId,
HSI88Interface::classId,
@ -55,6 +56,7 @@ std::span<const std::string_view> Interfaces::classList()
std::shared_ptr<Interface> Interfaces::create(World& world, std::string_view classId, std::string_view id)
{
IF_CLASSID_CREATE(CBUSInterface)
IF_CLASSID_CREATE(DCCEXInterface)
IF_CLASSID_CREATE(ECoSInterface)
IF_CLASSID_CREATE(HSI88Interface)

Datei anzeigen

@ -0,0 +1,168 @@
/**
* This file is part of Traintastic,
* see <https://github.com/traintastic/traintastic>.
*
* Copyright (C) 2026 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_CBUS_CBUSGETMINORPRIORITY_HPP
#define TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_CBUS_CBUSGETMINORPRIORITY_HPP
#include "cbusopcode.hpp"
#include "cbuspriority.hpp"
namespace CBUS {
constexpr MinorPriority getMinorPriority(OpCode opCode)
{
switch(opCode)
{
using enum OpCode;
case HLT:
case ARST:
case RESTP:
return MinorPriority::High;
case BON:
case TOF:
case TON:
case ESTOP:
case RTOF:
case RTON:
return MinorPriority::AboveNormal;
case ACK:
case NAK:
case RSTAT:
case RQMN:
case KLOC:
case QLOC:
case DKEEP:
case DBG1:
case RLOC:
case QCON:
case ALOC:
case STMOD:
case PCON:
case KCON:
case DSPD:
case DFLG:
case DFNON:
case DFNOF:
case DFUN:
case GLOC:
case ERR:
case RDCC3:
case WCVO:
case WCVB:
case QCVS:
case PCVS:
case RDCC4:
case WCVS:
case RDCC5:
case WCVOA:
case CABDAT:
case FCLK:
case RDCC6:
case PLOC:
case STAT:
return MinorPriority::Normal;
case QNN:
case RQNP:
case EXTC:
case SNN:
case SSTAT:
case NNRSM:
case RQNN:
case NNREL:
case NNACK:
case NNLRN:
case NNULN:
case NNCLR:
case NNEVN:
case NERD:
case RQEVN:
case WRACK:
case RQDAT:
case RQDDS:
case BOOTM:
case ENUM:
case NNRST:
case EXTC1:
case CMDERR:
case EVNLF:
case NVRD:
case NENRD:
case RQNPN:
case NUMEV:
case CANID:
case EXTC2:
case ACON:
case ACOF:
case AREQ:
case ARON:
case AROF:
case EVULN:
case NVSET:
case NVANS:
case ASON:
case ASOF:
case ASRQ:
case PARAN:
case REVAL:
case ARSON:
case ARSOF:
case EXTC3:
case ACON1:
case ACOF1:
case REQEV:
case ARON1:
case AROF1:
case NEVAL:
case PNN:
case ASON1:
case ASOF1:
case ARSON1:
case ARSOF1:
case EXTC4:
case NAME:
case PARAMS:
case ACON3:
case ACOF3:
case ENRSP:
case ARON3:
case AROF3:
case EVLRNI:
case ACDAT:
case ARDAT:
case ASON3:
case ASOF3:
case DDES:
case DDRS:
case ARSON3:
case ARSOF3:
case EXTC6:
return MinorPriority::Low;
}
return MinorPriority::Low;
}
}
#endif

Datei anzeigen

@ -0,0 +1,221 @@
/**
* This file is part of Traintastic,
* see <https://github.com/traintastic/traintastic>.
*
* Copyright (C) 2026 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "cbuskernel.hpp"
#include "cbusmessages.hpp"
#include "cbustostring.hpp"
/*
#include "simulator/cbussimulator.hpp"
*/
#include "../../../core/eventloop.hpp"
#include "../../../log/log.hpp"
#include "../../../log/logmessageexception.hpp"
#include "../../../utils/setthreadname.hpp"
namespace CBUS {
Kernel::Kernel(std::string logId_, const Config& config, bool simulation)
: KernelBase(std::move(logId_))
, m_simulation{simulation}
, m_config{config}
{
assert(isEventLoopThread());
}
void Kernel::setConfig(const Config& config)
{
assert(isEventLoopThread());
m_ioContext.post(
[this, newConfig=config]()
{
m_config = newConfig;
});
}
void Kernel::start()
{
assert(isEventLoopThread());
assert(m_ioHandler);
m_thread = std::thread(
[this]()
{
setThreadName("cbus");
auto work = std::make_shared<boost::asio::io_context::work>(m_ioContext);
m_ioContext.run();
});
m_ioContext.post(
[this]()
{
try
{
m_ioHandler->start();
}
catch(const LogMessageException& e)
{
EventLoop::call(
[this, e]()
{
Log::log(logId, e.message(), e.args());
error();
});
return;
}
});
}
void Kernel::stop()
{
assert(isEventLoopThread());
m_ioContext.post(
[this]()
{
m_ioHandler->stop();
});
m_ioContext.stop();
m_thread.join();
}
void Kernel::started()
{
assert(isKernelThread());
send(RequestCommandStationStatus());
send(QueryNodeNumber());
::KernelBase::started();
}
void Kernel::receive(uint8_t /*canId*/, const Message& message)
{
assert(isKernelThread());
if(m_config.debugLogRXTX)
{
EventLoop::call(
[this, msg=toString(message)]()
{
Log::log(logId, LogMessage::D2002_RX_X, msg);
});
}
switch(message.opCode)
{
case OpCode::TOF:
m_trackOn = false;
if(onTrackOff) [[likely]]
{
EventLoop::call(onTrackOff);
}
break;
case OpCode::TON:
m_trackOn = true;
if(onTrackOn) [[likely]]
{
EventLoop::call(onTrackOn);
}
break;
case OpCode::ESTOP:
if(onEmergencyStop) [[likely]]
{
EventLoop::call(onEmergencyStop);
}
break;
default:
break;
}
}
void Kernel::trackOff()
{
assert(isEventLoopThread());
m_ioContext.post(
[this]()
{
if(m_trackOn)
{
send(RequestTrackOff());
}
});
}
void Kernel::trackOn()
{
assert(isEventLoopThread());
m_ioContext.post(
[this]()
{
if(!m_trackOn)
{
send(RequestTrackOn());
}
});
}
void Kernel::requestEmergencyStop()
{
assert(isEventLoopThread());
m_ioContext.post(
[this]()
{
send(RequestEmergencyStop());
});
}
void Kernel::setIOHandler(std::unique_ptr<IOHandler> handler)
{
assert(isEventLoopThread());
assert(handler);
assert(!m_ioHandler);
m_ioHandler = std::move(handler);
}
void Kernel::send(const Message& message)
{
assert(isKernelThread());
if(m_config.debugLogRXTX)
{
EventLoop::call(
[this, msg=toString(message)]()
{
Log::log(logId, LogMessage::D2001_TX_X, msg);
});
}
if(auto ec = m_ioHandler->send(message); ec)
{
(void)ec; // FIXME: handle error
}
}
}

Datei anzeigen

@ -0,0 +1,110 @@
/**
* This file is part of Traintastic,
* see <https://github.com/traintastic/traintastic>.
*
* Copyright (C) 2026 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_CBUS_CBUSKERNEL_HPP
#define TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_CBUS_CBUSKERNEL_HPP
#include "../kernelbase.hpp"
#include <span>
#include <traintastic/enum/direction.hpp>
#include "cbusconfig.hpp"
#include "iohandler/cbusiohandler.hpp"
namespace CBUS {
class Kernel : public ::KernelBase
{
public:
std::function<void()> onTrackOff;
std::function<void()> onTrackOn;
std::function<void()> onEmergencyStop;
/**
* @brief Create kernel and IO handler
*
* @param[in] config CBUS configuration
* @param[in] args IO handler arguments
* @return The kernel instance
*/
template<class IOHandlerType, class... Args>
static std::unique_ptr<Kernel> create(std::string logId_, const Config& config, Args... args)
{
static_assert(std::is_base_of_v<IOHandler, IOHandlerType>);
std::unique_ptr<Kernel> kernel{new Kernel(std::move(logId_), config, isSimulation<IOHandlerType>())};
kernel->setIOHandler(std::make_unique<IOHandlerType>(*kernel, std::forward<Args>(args)...));
return kernel;
}
#ifndef NDEBUG
bool isKernelThread() const
{
return std::this_thread::get_id() == m_thread.get_id();
}
#endif
/**
* @brief Set CBUS configuration
*
* @param[in] config The CBUS configuration
*/
void setConfig(const Config& config);
/**
* @brief Start the kernel and IO handler
*/
void start();
/**
* @brief Stop the kernel and IO handler
*/
void stop();
/**
* \brief Notify kernel the IO handler is started.
* \note This function must run in the kernel's IO context
*/
void started() final;
void receive(uint8_t canId, const Message& message);
void trackOff();
void trackOn();
void requestEmergencyStop();
private:
std::unique_ptr<IOHandler> m_ioHandler;
const bool m_simulation;
Config m_config;
bool m_trackOn = false;
Kernel(std::string logId_, const Config& config, bool simulation);
Kernel(const Kernel&) = delete;
Kernel& operator =(const Kernel&) = delete;
void setIOHandler(std::unique_ptr<IOHandler> handler);
void send(const Message& message);
};
}
#endif

Datei anzeigen

@ -167,6 +167,11 @@ enum class OpCode : uint8_t
EXTC6 = 0xFF, //!< Extended op-code with 6 data bytes
};
constexpr uint8_t dataSize(OpCode opc)
{
return static_cast<uint8_t>(opc) >> 5; // highest 3 bits determine data length
}
}
#endif

Datei anzeigen

@ -0,0 +1,46 @@
/**
* This file is part of Traintastic,
* see <https://github.com/traintastic/traintastic>.
*
* Copyright (C) 2026 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_CBUS_CBUSPRIORITY_HPP
#define TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_CBUS_CBUSPRIORITY_HPP
#include <cstdint>
namespace CBUS {
enum class MajorPriority : uint8_t
{
Highest = 0b00,
Next = 0b01,
Lowest = 0b10,
};
enum class MinorPriority : uint8_t
{
High = 0b00,
AboveNormal = 0b01,
Normal = 0b10,
Low = 0b11,
};
}
#endif

Datei anzeigen

@ -0,0 +1,381 @@
/**
* This file is part of Traintastic,
* see <https://github.com/traintastic/traintastic>.
*
* Copyright (C) 2026 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "cbustostring.hpp"
#include <format>
#include "cbusmessages.hpp"
#include "../../../utils/tohex.hpp"
namespace CBUS {
std::string toString(const Message& message)
{
std::string s(toString(message.opCode));
switch (message.opCode)
{
using enum OpCode;
// 00-1F – 0 data byte packets:
case ACK:
case NAK:
case HLT:
case BON:
case TOF:
case TON:
case ESTOP:
case ARST:
case RTOF:
case RTON:
case RESTP:
case RSTAT:
case QNN:
case RQNP:
case RQMN:
break; // no data
// 20–3F - 1 data byte packets:
case KLOC:
case QLOC:
case DKEEP:
{
const auto& m = static_cast<const EngineMessage&>(message);
s.append(std::format(" session={}", m.session));
break;
}
case DBG1:
break;
case EXTC:
break;
// 40–5F - 2 data byte packets:
case RLOC:
break;
case QCON:
break;
case SNN:
break;
case ALOC:
break;
case STMOD:
break;
case PCON:
break;
case KCON:
break;
case DSPD:
break;
case DFLG:
break;
case DFNON:
break;
case DFNOF:
break;
case SSTAT:
break;
case NNRSM:
break;
case RQNN:
break;
case NNREL:
break;
case NNACK:
break;
case NNLRN:
break;
case NNULN:
break;
case NNCLR:
break;
case NNEVN:
break;
case NERD:
break;
case RQEVN:
break;
case WRACK:
break;
case RQDAT:
break;
case RQDDS:
break;
case BOOTM:
break;
case ENUM:
break;
case NNRST:
break;
case EXTC1:
break;
// 60-7F - 3 data byte packets:
case DFUN:
break;
case GLOC:
break;
case ERR:
break;
case CMDERR:
break;
case EVNLF:
break;
case NVRD:
break;
case NENRD:
break;
case RQNPN:
break;
case NUMEV:
break;
case CANID:
break;
case EXTC2:
break;
// 80-9F - 4 data byte packets:
case RDCC3:
break;
case WCVO:
break;
case WCVB:
break;
case QCVS:
break;
case PCVS:
break;
case ACON:
break;
case ACOF:
break;
case AREQ:
break;
case ARON:
break;
case AROF:
break;
case EVULN:
break;
case NVSET:
break;
case NVANS:
break;
case ASON:
break;
case ASOF:
break;
case ASRQ:
break;
case PARAN:
break;
case REVAL:
break;
case ARSON:
break;
case ARSOF:
break;
case EXTC3:
break;
// A0-BF - 5 data byte packets:
case RDCC4:
break;
case WCVS:
break;
case ACON1:
break;
case ACOF1:
break;
case REQEV:
break;
case ARON1:
break;
case AROF1:
break;
case NEVAL:
break;
case PNN:
break;
case ASON1:
break;
case ASOF1:
break;
case ARSON1:
break;
case ARSOF1:
break;
case EXTC4:
break;
// C0-DF - 6 data byte packets:
case RDCC5:
break;
case WCVOA:
break;
case CABDAT:
break;
case FCLK:
break;
// E0-FF - 7 data byte packets:
case RDCC6:
break;
case PLOC:
break;
case NAME:
break;
case STAT:
break;
case PARAMS:
break;
case ACON3:
break;
case ACOF3:
break;
case ENRSP:
break;
case ARON3:
break;
case AROF3:
break;
case EVLRNI:
break;
case ACDAT:
break;
case ARDAT:
break;
case ASON3:
break;
case ASOF3:
break;
case DDES:
break;
case DDRS:
break;
case ARSON3:
break;
case ARSOF3:
break;
case EXTC6:
break;
}
s.append(" [");
s.append(toHex(&message, message.size()));
s.append("]");
return s;
}
}

Datei anzeigen

@ -27,13 +27,15 @@
namespace CBUS {
struct Message;
constexpr std::string_view toString(OpCode opCode)
{
switch(opCode)
{
using enum OpCode;
// 00-1F – 0 data bytes packets:
// 00-1F – 0 data byte packets:
case ACK: return "ACK";
case NAK: return "NAK";
case HLT: return "HLT";
@ -171,6 +173,8 @@ constexpr std::string_view toString(OpCode opCode)
return {};
}
std::string toString(const Message& message);
}
#endif

Datei anzeigen

@ -0,0 +1,165 @@
/**
* This file is part of Traintastic,
* see <https://github.com/traintastic/traintastic>.
*
* Copyright (C) 2026 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "cbusasciiiohandler.hpp"
#include "../cbusgetminorpriority.hpp"
#include "../cbuskernel.hpp"
#include "../messages/cbusmessage.hpp"
#include "../../../../core/eventloop.hpp"
#include "../../../../log/log.hpp"
#include "../../../../utils/tohex.hpp"
#include "../../../../utils/fromchars.hpp"
namespace {
std::string buildFrame(CBUS::MajorPriority majorPriority, CBUS::MinorPriority minorPriority, uint8_t canId, const CBUS::Message& message)
{
const uint16_t sid =
(static_cast<uint16_t>(majorPriority) << 14) |
(static_cast<uint16_t>(minorPriority) << 12) |
(static_cast<uint16_t>(canId) << 5);
std::string frame(":S");
frame.append(toHex(sid));
frame.append("N");
frame.append(toHex(&message, message.size()));
frame.append(";");
return frame;
}
}
namespace CBUS {
ASCIIIOHandler::ASCIIIOHandler(Kernel& kernel, uint8_t canId)
: IOHandler(kernel, canId)
{
}
std::error_code ASCIIIOHandler::send(const Message& message)
{
const bool wasEmpty = m_writeQueue.empty();
// FIXME: handle priority queueing
// FIXME: what to do with MajorPriority?
m_writeQueue.emplace(buildFrame(MajorPriority::Lowest, getMinorPriority(message.opCode), m_canId, message));
if(wasEmpty)
{
write();
}
return {};
}
void ASCIIIOHandler::logDropIfNonZeroAndReset(size_t& drop)
{
if(drop != 0)
{
EventLoop::call(
[this, drop]()
{
Log::log(m_kernel.logId, LogMessage::W2001_RECEIVED_MALFORMED_DATA_DROPPED_X_BYTES, drop);
});
drop = 0;
}
}
void ASCIIIOHandler::processRead(std::size_t bytesTransferred)
{
constexpr size_t maxFrameSize = 24; // :SXXXXNXXXXXXXXXXXXXXXX;
std::string_view buffer{m_readBuffer.data(), m_readBufferOffset + bytesTransferred};
size_t drop = 0;
while(!buffer.empty())
{
logDropIfNonZeroAndReset(drop);
if(auto pos = buffer.find(':'); pos != 0)
{
if(pos == std::string_view::npos)
{
// no start marker drop all bytes:
drop += buffer.size();
buffer = {};
break;
}
// drop bytes before start marker:
drop += pos;
buffer.remove_prefix(pos);
}
auto end = buffer.find(';');
if(end == std::string_view::npos)
{
// no end marker yet, wait for more data
break;
}
std::string_view frame{buffer.data(), end + 1};
buffer.remove_prefix(frame.size()); // consume frame
if(frame.size() > maxFrameSize)
{
drop += frame.size();
}
else if(frame[1] == 'S' && frame[6] == 'N') // CBUS only uses standard non RTR CAN frames
{
uint16_t sid;
if(fromChars(frame.substr(2, sizeof(sid) * 2), sid, 16).ec != std::errc())
{
continue; // error reading, ignore frame
}
const auto dataLength = (frame.size() - 7) / 2;
std::array<uint8_t, 8> data;
std::errc ec = std::errc();
for(size_t i = 0; i < dataLength; ++i)
{
ec = fromChars(frame.substr(7 + i * 2, 2), data[i], 16).ec;
if(ec != std::errc())
{
break;
}
}
if(ec != std::errc())
{
continue; // error reading, ignore frame
}
m_kernel.receive((sid >> 5) & 0x7F, *reinterpret_cast<const Message*>(data.data()));
}
}
logDropIfNonZeroAndReset(drop);
if(buffer.size() != 0)
{
memmove(m_readBuffer.data(), buffer.data(), buffer.size());
}
m_readBufferOffset = buffer.size();
}
}

Datei anzeigen

@ -0,0 +1,53 @@
/**
* This file is part of Traintastic,
* see <https://github.com/traintastic/traintastic>.
*
* Copyright (C) 2026 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_CBUS_IOHANDLER_CBUSASCIIIOHANDLER_HPP
#define TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_CBUS_IOHANDLER_CBUSASCIIIOHANDLER_HPP
#include "cbusiohandler.hpp"
#include <array>
#include <string>
#include <queue>
namespace CBUS {
class ASCIIIOHandler : public IOHandler
{
public:
[[nodiscard]] std::error_code send(const Message& message) override;
protected:
std::array<char, 1024> m_readBuffer;
size_t m_readBufferOffset;
std::queue<std::string> m_writeQueue;
ASCIIIOHandler(Kernel& kernel, uint8_t canId);
void logDropIfNonZeroAndReset(size_t& drop);
void processRead(std::size_t bytesTransferred);
virtual void read() = 0;
virtual void write() = 0;
};
}
#endif

Datei anzeigen

@ -0,0 +1,135 @@
/**
* This file is part of Traintastic,
* see <https://github.com/traintastic/traintastic>.
*
* Copyright (C) 2026 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "cbuscanetheriohandler.hpp"
#include <boost/asio/write.hpp>
#include "../cbuskernel.hpp"
#include "../../../../core/eventloop.hpp"
#include "../../../../log/log.hpp"
#include "../../../../log/logmessageexception.hpp"
namespace CBUS {
CANEtherIOHandler::CANEtherIOHandler(Kernel& kernel, std::string hostname, uint16_t port)
: ASCIIIOHandler(kernel, canId)
, m_hostname{std::move(hostname)}
, m_port{port}
, m_socket{m_kernel.ioContext()}
{
}
void CANEtherIOHandler::start()
{
boost::system::error_code ec;
m_endpoint.port(m_port);
m_endpoint.address(boost::asio::ip::make_address(m_hostname, ec));
if(ec)
{
throw LogMessageException(LogMessage::E2003_MAKE_ADDRESS_FAILED_X, ec);
}
m_socket.async_connect(m_endpoint,
[this](const boost::system::error_code& err)
{
if(!err)
{
m_socket.set_option(boost::asio::socket_base::linger(true, 0));
m_socket.set_option(boost::asio::ip::tcp::no_delay(true));
m_connected = true;
read();
write();
m_kernel.started();
}
else if(err != boost::asio::error::operation_aborted)
{
EventLoop::call(
[this, err]()
{
Log::log(m_kernel.logId, LogMessage::E2005_SOCKET_CONNECT_FAILED_X, err);
m_kernel.error();
});
}
});
}
void CANEtherIOHandler::stop()
{
boost::system::error_code ec;
m_socket.cancel(ec);
m_socket.close(ec);
// ignore errors
m_connected = false;
}
void CANEtherIOHandler::read()
{
m_socket.async_read_some(boost::asio::buffer(m_readBuffer.data() + m_readBufferOffset, m_readBuffer.size() - m_readBufferOffset),
[this](const boost::system::error_code& ec, std::size_t bytesTransferred)
{
if(!ec)
{
processRead(bytesTransferred);
read();
}
else if(ec != boost::asio::error::operation_aborted)
{
EventLoop::call(
[this, ec]()
{
Log::log(m_kernel.logId, LogMessage::E1007_SOCKET_READ_FAILED_X, ec);
m_kernel.error();
});
}
});
}
void CANEtherIOHandler::write()
{
assert(!m_writeQueue.empty());
const auto& message = m_writeQueue.front();
boost::asio::async_write(m_socket, boost::asio::buffer(message.data(), message.size()),
[this](const boost::system::error_code& ec, std::size_t /*bytesTransferred*/)
{
if(!ec)
{
m_writeQueue.pop();
if(!m_writeQueue.empty())
{
write();
}
}
else if(ec != boost::asio::error::operation_aborted)
{
EventLoop::call(
[this, ec]()
{
Log::log(m_kernel.logId, LogMessage::E1006_SOCKET_WRITE_FAILED_X, ec);
m_kernel.error();
});
}
});
}
}

Datei anzeigen

@ -0,0 +1,54 @@
/**
* This file is part of Traintastic,
* see <https://github.com/traintastic/traintastic>.
*
* Copyright (C) 2026 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_CBUS_IOHANDLER_CBUSCANETHERIOHANDLER_HPP
#define TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_CBUS_IOHANDLER_CBUSCANETHERIOHANDLER_HPP
#include "cbusasciiiohandler.hpp"
#include <boost/asio/ip/tcp.hpp>
namespace CBUS {
class CANEtherIOHandler final : public ASCIIIOHandler
{
public:
CANEtherIOHandler(Kernel& kernel, std::string hostname, uint16_t port);
void start() final;
void stop() final;
protected:
void read() final;
void write() final;
private:
static constexpr uint8_t canId = 0x7D; //!< CANEther fixed CAN_ID
const std::string m_hostname;
const uint16_t m_port;
boost::asio::ip::tcp::socket m_socket;
boost::asio::ip::tcp::endpoint m_endpoint;
bool m_connected = false;
};
}
#endif

Datei anzeigen

@ -0,0 +1,109 @@
/**
* This file is part of Traintastic,
* see <https://github.com/traintastic/traintastic>.
*
* Copyright (C) 2026 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "cbuscanusbiohandler.hpp"
#include <boost/asio/write.hpp>
#include "../cbuskernel.hpp"
#include "../../../../core/eventloop.hpp"
#include "../../../../log/log.hpp"
#include "../../../../utils/serialport.hpp"
namespace CBUS {
CANUSBIOHandler::CANUSBIOHandler(Kernel& kernel, const std::string& device)
: ASCIIIOHandler(kernel, canId)
, m_serialPort{m_kernel.ioContext()}
{
// FIXME: check serial settings, just a guess, if more settings are needed add them to the interface
SerialPort::open(m_serialPort, device, 115'200, 8, SerialParity::None, SerialStopBits::One, SerialFlowControl::None);
}
CANUSBIOHandler::~CANUSBIOHandler()
{
if(m_serialPort.is_open())
{
boost::system::error_code ec;
m_serialPort.close(ec);
// ignore the error
}
}
void CANUSBIOHandler::start()
{
read();
m_kernel.started();
}
void CANUSBIOHandler::stop()
{
m_serialPort.close();
}
void CANUSBIOHandler::read()
{
m_serialPort.async_read_some(boost::asio::buffer(m_readBuffer.data() + m_readBufferOffset, m_readBuffer.size() - m_readBufferOffset),
[this](const boost::system::error_code& ec, std::size_t bytesTransferred)
{
if(!ec)
{
processRead(bytesTransferred);
read();
}
else if(ec != boost::asio::error::operation_aborted)
{
EventLoop::call(
[this, ec]()
{
Log::log(m_kernel.logId, LogMessage::E2002_SERIAL_READ_FAILED_X, ec);
m_kernel.error();
});
}
});
}
void CANUSBIOHandler::write()
{
assert(!m_writeQueue.empty());
const auto& message = m_writeQueue.front();
boost::asio::async_write(m_serialPort, boost::asio::buffer(message.data(), message.size()),
[this](const boost::system::error_code& ec, std::size_t /*bytesTransferred*/)
{
if(!ec)
{
m_writeQueue.pop();
if(!m_writeQueue.empty())
{
write();
}
}
else if(ec != boost::asio::error::operation_aborted)
{
EventLoop::call(
[this, ec]()
{
Log::log(m_kernel.logId, LogMessage::E2001_SERIAL_WRITE_FAILED_X, ec);
m_kernel.error();
});
}
});
}
}

Datei anzeigen

@ -0,0 +1,51 @@
/**
* This file is part of Traintastic,
* see <https://github.com/traintastic/traintastic>.
*
* Copyright (C) 2026 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_CBUS_IOHANDLER_CBUSCANUSBIOHANDLER_HPP
#define TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_CBUS_IOHANDLER_CBUSCANUSBIOHANDLER_HPP
#include "cbusasciiiohandler.hpp"
#include <boost/asio/serial_port.hpp>
namespace CBUS {
class CANUSBIOHandler final : public ASCIIIOHandler
{
public:
CANUSBIOHandler(Kernel& kernel, const std::string& device);
~CANUSBIOHandler() final;
void start() final;
void stop() final;
protected:
void read() final;
void write() final;
private:
static constexpr uint8_t canId = 0x7C; //!< CANUSB fixed CAN_ID
boost::asio::serial_port m_serialPort;
};
}
#endif

Datei anzeigen

@ -0,0 +1,32 @@
/**
* This file is part of Traintastic,
* see <https://github.com/traintastic/traintastic>.
*
* Copyright (C) 2026 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "cbusiohandler.hpp"
namespace CBUS {
IOHandler::IOHandler(Kernel& kernel, uint8_t canId)
: m_kernel{kernel}
, m_canId{canId}
{
}
}

Datei anzeigen

@ -0,0 +1,61 @@
/**
* This file is part of Traintastic,
* see <https://github.com/traintastic/traintastic>.
*
* Copyright (C) 2026 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_CBUS_IOHANDLER_CBUSIOHANDLER_HPP
#define TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_CBUS_IOHANDLER_CBUSIOHANDLER_HPP
#include <cstdint>
#include <system_error>
namespace CBUS {
class Kernel;
struct Message;
class IOHandler
{
public:
IOHandler(const IOHandler&) = delete;
IOHandler& operator =(const IOHandler&) = delete;
virtual ~IOHandler() = default;
virtual void start() = 0;
virtual void stop() = 0;
[[nodiscard]] virtual std::error_code send(const Message& message) = 0;
protected:
Kernel& m_kernel;
const uint8_t m_canId;
IOHandler(Kernel& kernel, uint8_t canId);
};
template<class T>
constexpr bool isSimulation()
{
return false;
}
}
#endif

Datei anzeigen

@ -30,6 +30,11 @@ struct Message
{
OpCode opCode;
constexpr uint8_t size() const
{
return sizeof(OpCode) + dataSize(opCode);
}
protected:
Message(OpCode opc)
: opCode{opc}

Datei anzeigen

@ -0,0 +1,46 @@
/**
* This file is part of Traintastic,
* see <https://github.com/traintastic/traintastic>.
*
* Copyright (C) 2026 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef TRAINTASTIC_SHARED_TRAINTASTIC_ENUM_CBUSINTERFACETYPE_HPP
#define TRAINTASTIC_SHARED_TRAINTASTIC_ENUM_CBUSINTERFACETYPE_HPP
#include <cstdint>
#include <array>
#include "enum.hpp"
enum class CBUSInterfaceType : uint8_t
{
CANUSB = 0,
CANEther = 1,
};
TRAINTASTIC_ENUM(CBUSInterfaceType, "cbus_interface_type", 2,
{
{CBUSInterfaceType::CANUSB, "canusb"},
{CBUSInterfaceType::CANEther, "canether"},
});
inline constexpr std::array<CBUSInterfaceType, 2> CBUSInterfaceTypeValues{{
CBUSInterfaceType::CANUSB,
CBUSInterfaceType::CANEther,
}};
#endif

Datei anzeigen

@ -1,4 +1,8 @@
[
{
"term": "class_id:interface.cbus",
"definition": "CBUS/VLCB"
},
{
"term": "class_id:interface.dccex",
"definition": "DCC-EX"