renamed li100 to xpressnetserial and added serialcommandstation base class
Dieser Commit ist enthalten in:
Ursprung
71ba4a87fb
Commit
6afb6eb66d
46
server/src/enum/xpressnetserialinterface.hpp
Normale Datei
46
server/src/enum/xpressnetserialinterface.hpp
Normale Datei
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* server/src/enum/xpressnetserialinterface.hpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2020 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_ENUM_XPRESSNETSERIALINTERFACE_HPP
|
||||
#define TRAINTASTIC_SERVER_ENUM_XPRESSNETSERIALINTERFACE_HPP
|
||||
|
||||
#include <traintastic/enum/xpressnetserialinterface.hpp>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
inline constexpr std::array<XpressNetSerialInterface, 5> XpressNetSerialInterfaceValues{{
|
||||
XpressNetSerialInterface::Custom,
|
||||
XpressNetSerialInterface::LenzLI100,
|
||||
XpressNetSerialInterface::LenzLI100F,
|
||||
XpressNetSerialInterface::LenzLI101F,
|
||||
XpressNetSerialInterface::RoSoftS88XPressNetLI,
|
||||
}};
|
||||
|
||||
NLOHMANN_JSON_SERIALIZE_ENUM(XpressNetSerialInterface,
|
||||
{
|
||||
{XpressNetSerialInterface::Custom, "custom"},
|
||||
{XpressNetSerialInterface::LenzLI100, "lenz_li100"},
|
||||
{XpressNetSerialInterface::LenzLI100F, "lenz_li100f"},
|
||||
{XpressNetSerialInterface::LenzLI101F, "lenz_li101f"},
|
||||
{XpressNetSerialInterface::RoSoftS88XPressNetLI, "rosoft_s88xpressnetli"},
|
||||
})
|
||||
|
||||
#endif
|
||||
@ -1,5 +1,5 @@
|
||||
/**
|
||||
* server/src/hardware/commandstation/create.cpp
|
||||
* server/src/hardware/commandstation/commandstations.cpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
@ -21,21 +21,21 @@
|
||||
*/
|
||||
|
||||
#include "commandstations.hpp"
|
||||
#include "li10x.hpp"
|
||||
#include "loconetserial.hpp"
|
||||
#ifndef DISABLE_USB_XPRESSNET_INTERFACE
|
||||
#include "usbxpressnetinterface.hpp"
|
||||
#endif
|
||||
#include "xpressnetserial.hpp"
|
||||
#include "rocoz21.hpp"
|
||||
|
||||
const std::vector<std::string_view>& CommandStations::classList()
|
||||
{
|
||||
static std::vector<std::string_view> list({
|
||||
LI10x::classId,
|
||||
LocoNetSerial::classId,
|
||||
#ifndef DISABLE_USB_XPRESSNET_INTERFACE
|
||||
USBXpressNetInterface::classId,
|
||||
#endif
|
||||
XpressNetSerial::classId,
|
||||
RocoZ21::classId,
|
||||
});
|
||||
return list;
|
||||
@ -43,14 +43,14 @@ const std::vector<std::string_view>& CommandStations::classList()
|
||||
|
||||
std::shared_ptr<CommandStation> CommandStations::create(const std::weak_ptr<World>& world, std::string_view classId, std::string_view id)
|
||||
{
|
||||
if(classId == LI10x::classId)
|
||||
return LI10x::create(world, id);
|
||||
else if(classId == LocoNetSerial::classId)
|
||||
if(classId == LocoNetSerial::classId)
|
||||
return LocoNetSerial::create(world, id);
|
||||
#ifndef DISABLE_USB_XPRESSNET_INTERFACE
|
||||
else if(classId == USBXpressNetInterface::classId)
|
||||
return USBXpressNetInterface::create(world, id);
|
||||
#endif
|
||||
else if(classId == XpressNetSerial::classId)
|
||||
return XpressNetSerial::create(world, id);
|
||||
else if(classId == RocoZ21::classId)
|
||||
return RocoZ21::create(world, id);
|
||||
else
|
||||
|
||||
@ -1,217 +0,0 @@
|
||||
/**
|
||||
* hardware/commandstation/li10x.cpp
|
||||
*
|
||||
* This file is part of the traintastic source code
|
||||
*
|
||||
* Copyright (C) 2019-2020 Reinder Feenstra <reinderfeenstra@gmail.com>
|
||||
*
|
||||
* 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 "li10x.hpp"
|
||||
#include "../../world/world.hpp"
|
||||
#include "../../core/traintastic.hpp"
|
||||
#include "../../core/eventloop.hpp"
|
||||
|
||||
LI10x::LI10x(const std::weak_ptr<World>& world, std::string_view _id) :
|
||||
CommandStation(world, _id),
|
||||
m_serialPort{Traintastic::instance->ioContext()},
|
||||
port{this, "port", "", PropertyFlags::ReadWrite},
|
||||
baudrate{this, "baudrate", 9600, PropertyFlags::ReadWrite},
|
||||
xpressnet{this, "xpressnet", nullptr, PropertyFlags::ReadOnly | PropertyFlags::Store | PropertyFlags::SubObject}
|
||||
{
|
||||
name = "LI10x";
|
||||
xpressnet.setValueInternal(std::make_shared<XpressNet>(*this, xpressnet.name(), std::bind(&LI10x::send, this, std::placeholders::_1)));
|
||||
|
||||
m_interfaceItems.insertBefore(port, notes);
|
||||
m_interfaceItems.insertBefore(baudrate, notes);
|
||||
m_interfaceItems.insertBefore(xpressnet, notes);
|
||||
}
|
||||
|
||||
bool LI10x::setOnline(bool& value)
|
||||
{
|
||||
if(!m_serialPort.is_open() && value)
|
||||
{
|
||||
if(!start())
|
||||
{
|
||||
value = false;
|
||||
return false;
|
||||
}
|
||||
read();
|
||||
}
|
||||
else if(m_serialPort.is_open() && !value)
|
||||
stop();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void LI10x::emergencyStopChanged(bool value)
|
||||
{
|
||||
CommandStation::emergencyStopChanged(value);
|
||||
|
||||
if(value)
|
||||
send(XpressNet::EmergencyStop());
|
||||
else if(!trackVoltageOff)
|
||||
send(XpressNet::NormalOperationResumed());
|
||||
}
|
||||
|
||||
void LI10x::trackVoltageOffChanged(bool value)
|
||||
{
|
||||
CommandStation::trackVoltageOffChanged(value);
|
||||
|
||||
if(!value)
|
||||
send(XpressNet::NormalOperationResumed());
|
||||
else
|
||||
send(XpressNet::TrackPowerOff());
|
||||
|
||||
}
|
||||
|
||||
void LI10x::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber)
|
||||
{
|
||||
CommandStation::decoderChanged(decoder, changes, functionNumber);
|
||||
|
||||
if(online)
|
||||
xpressnet->decoderChanged(decoder, changes, functionNumber);
|
||||
}
|
||||
|
||||
bool LI10x::start()
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
m_serialPort.open(port, ec);
|
||||
if(ec)
|
||||
{
|
||||
logError("open: " + ec.message());
|
||||
return false;
|
||||
}
|
||||
|
||||
m_serialPort.set_option(boost::asio::serial_port_base::baud_rate(baudrate));
|
||||
m_serialPort.set_option(boost::asio::serial_port_base::character_size(8));
|
||||
m_serialPort.set_option(boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one));
|
||||
m_serialPort.set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::none));
|
||||
m_serialPort.set_option(boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::hardware));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void LI10x::stop()
|
||||
{
|
||||
// send bus off cmd
|
||||
m_serialPort.close();
|
||||
}
|
||||
|
||||
bool LI10x::send(const XpressNet::Message& msg)
|
||||
{
|
||||
assert(XpressNet::isChecksumValid(msg));
|
||||
if(!m_serialPort.is_open())
|
||||
return false;
|
||||
boost::system::error_code ec;
|
||||
m_serialPort.write_some(boost::asio::buffer(static_cast<const void*>(&msg), msg.size()), ec); // TODO async
|
||||
if(ec)
|
||||
{
|
||||
logError("write_some: " + ec.message());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void LI10x::receive(std::unique_ptr<uint8_t[]> message)
|
||||
{
|
||||
// NOTE: this runs outside the event loop !!!
|
||||
|
||||
//const uint8_t dataLen = message[0] & 0x0f;
|
||||
|
||||
// TODO: verify checksum
|
||||
|
||||
switch(message[0] & 0xf0)
|
||||
{
|
||||
case 0x60:
|
||||
if(message[1] == 0x01) // Normal operation resumed
|
||||
{
|
||||
EventLoop::call(
|
||||
[this]()
|
||||
{
|
||||
logDebug("receive: Normal operation resumed");
|
||||
emergencyStop.setValueInternal(false);
|
||||
trackVoltageOff.setValueInternal(false);
|
||||
});
|
||||
}
|
||||
else if(message[1] == 0x00) // Track power Off
|
||||
{
|
||||
EventLoop::call(
|
||||
[this]()
|
||||
{
|
||||
logDebug("receive: Track power Off");
|
||||
trackVoltageOff.setValueInternal(true);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x80:
|
||||
if(message[1] == 0x00) // Emergency Stop
|
||||
{
|
||||
EventLoop::call(
|
||||
[this]()
|
||||
{
|
||||
logDebug("receive: Emergency Stop");
|
||||
emergencyStop.setValueInternal(true);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void LI10x::read()
|
||||
{
|
||||
m_serialPort.async_read_some(boost::asio::buffer(m_readBuffer),
|
||||
[this](const boost::system::error_code& ec, std::size_t bytesTransferred)
|
||||
{
|
||||
if(!ec)
|
||||
{
|
||||
std::size_t i = 0;
|
||||
while(bytesTransferred != 0)
|
||||
{
|
||||
if(!m_readMessage) // read header byte
|
||||
{
|
||||
m_readMessageTodo = (m_readBuffer[i] & 0x0f) + 1;
|
||||
m_readMessage = std::make_unique<uint8_t[]>(1 + m_readMessageTodo);
|
||||
m_readMessage[0] = m_readBuffer[i++];
|
||||
m_readMessagePos = 1;
|
||||
bytesTransferred--;
|
||||
}
|
||||
else
|
||||
{
|
||||
const uint8_t size = static_cast<uint8_t>(std::min<std::size_t>(m_readMessageTodo, bytesTransferred));
|
||||
memcpy(m_readMessage.get() + m_readMessagePos, m_readBuffer.data() + i, size);
|
||||
i += size;
|
||||
if((m_readMessageTodo -= size) == 0)
|
||||
receive(std::move(m_readMessage));
|
||||
else
|
||||
m_readMessagePos += size;
|
||||
}
|
||||
}
|
||||
read();
|
||||
}
|
||||
else
|
||||
EventLoop::call([this, ec](){ logError("async_read_some: " + ec.message()); });
|
||||
});
|
||||
}
|
||||
@ -26,10 +26,7 @@
|
||||
#include "../../core/eventloop.hpp"
|
||||
|
||||
LocoNetSerial::LocoNetSerial(const std::weak_ptr<World>& world, std::string_view _id) :
|
||||
CommandStation(world, _id),
|
||||
m_serialPort{Traintastic::instance->ioContext()},
|
||||
m_readBufferOffset{0},
|
||||
port{this, "port", "/dev/ttyUSB0", PropertyFlags::ReadWrite | PropertyFlags::Store},
|
||||
SerialCommandStation(world, _id),
|
||||
interface{this, "interface", LocoNetSerialInterface::Custom, PropertyFlags::ReadWrite | PropertyFlags::Store,
|
||||
[this](LocoNetSerialInterface value)
|
||||
{
|
||||
@ -49,33 +46,17 @@ LocoNetSerial::LocoNetSerial(const std::weak_ptr<World>& world, std::string_view
|
||||
break;
|
||||
}
|
||||
}},
|
||||
baudrate{this, "baudrate", 19200, PropertyFlags::ReadWrite | PropertyFlags::Store,
|
||||
[this](uint32_t)
|
||||
{
|
||||
interface = LocoNetSerialInterface::Custom;
|
||||
}},
|
||||
flowControl{this, "flow_control", SerialFlowControl::None, PropertyFlags::ReadWrite | PropertyFlags::Store,
|
||||
[this](SerialFlowControl)
|
||||
{
|
||||
interface = LocoNetSerialInterface::Custom;
|
||||
}},
|
||||
loconet{this, "loconet", nullptr, PropertyFlags::ReadOnly | PropertyFlags::Store | PropertyFlags::SubObject}
|
||||
{
|
||||
name = "LocoNet (serial)";
|
||||
loconet.setValueInternal(std::make_shared<LocoNet::LocoNet>(*this, loconet.name(), std::bind(&LocoNetSerial::send, this, std::placeholders::_1)));
|
||||
|
||||
port.addAttributeEnabled(!online);
|
||||
interface.addAttributeEnabled(!online);
|
||||
baudrate.addAttributeEnabled(!online);
|
||||
flowControl.addAttributeEnabled(!online);
|
||||
|
||||
m_interfaceItems.insertBefore(port, notes);
|
||||
m_interfaceItems.insertBefore(interface, notes);
|
||||
m_interfaceItems.insertBefore(baudrate, notes);
|
||||
m_interfaceItems.insertBefore(flowControl, notes);
|
||||
m_interfaceItems.insertBefore(interface, baudrate);
|
||||
m_interfaceItems.insertBefore(loconet, notes);
|
||||
}
|
||||
|
||||
/*
|
||||
bool LocoNetSerial::setOnline(bool& value)
|
||||
{
|
||||
if(!m_serialPort.is_open() && value)
|
||||
@ -94,7 +75,7 @@ bool LocoNetSerial::setOnline(bool& value)
|
||||
stop();
|
||||
|
||||
return true;
|
||||
}
|
||||
}*/
|
||||
|
||||
void LocoNetSerial::emergencyStopChanged(bool value)
|
||||
{
|
||||
@ -120,39 +101,6 @@ void LocoNetSerial::decoderChanged(const Decoder& decoder, DecoderChangeFlags ch
|
||||
loconet->decoderChanged(decoder, changes, functionNumber);
|
||||
}
|
||||
|
||||
bool LocoNetSerial::start()
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
m_serialPort.open(port, ec);
|
||||
if(ec)
|
||||
{
|
||||
logError("open: " + ec.message());
|
||||
return false;
|
||||
}
|
||||
|
||||
m_serialPort.set_option(boost::asio::serial_port_base::baud_rate(baudrate));
|
||||
m_serialPort.set_option(boost::asio::serial_port_base::character_size(8));
|
||||
m_serialPort.set_option(boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one));
|
||||
m_serialPort.set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::none));
|
||||
switch(flowControl)
|
||||
{
|
||||
case SerialFlowControl::None:
|
||||
m_serialPort.set_option(boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::none));
|
||||
break;
|
||||
|
||||
case SerialFlowControl::Hardware:
|
||||
m_serialPort.set_option(boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::hardware));
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void LocoNetSerial::stop()
|
||||
{
|
||||
// TODO: send power off cmd??
|
||||
m_serialPort.close();
|
||||
}
|
||||
|
||||
bool LocoNetSerial::send(const LocoNet::Message& message)
|
||||
{
|
||||
if(!m_serialPort.is_open())
|
||||
|
||||
@ -23,20 +23,15 @@
|
||||
#ifndef TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_LOCONETSERIAL_HPP
|
||||
#define TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_LOCONETSERIAL_HPP
|
||||
|
||||
#include "commandstation.hpp"
|
||||
#include "serialcommandstation.hpp"
|
||||
#include "../protocol/loconet/loconet.hpp"
|
||||
#include "../../enum/loconetserialinterface.hpp"
|
||||
#include "../../enum/serialflowcontrol.hpp"
|
||||
#include <boost/asio/serial_port.hpp>
|
||||
//#include "../../enum/serialflowcontrol.hpp"
|
||||
//#include <boost/asio/serial_port.hpp>
|
||||
|
||||
class LocoNetSerial : public CommandStation
|
||||
class LocoNetSerial : public SerialCommandStation
|
||||
{
|
||||
protected:
|
||||
boost::asio::serial_port m_serialPort;
|
||||
std::array<uint8_t, 1024> m_readBuffer;
|
||||
uint16_t m_readBufferOffset;
|
||||
|
||||
bool setOnline(bool& value) final;
|
||||
void emergencyStopChanged(bool value) final;
|
||||
void trackVoltageOffChanged(bool value) final;
|
||||
void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber) final;
|
||||
@ -44,20 +39,16 @@ class LocoNetSerial : public CommandStation
|
||||
bool start();
|
||||
void stop();
|
||||
bool send(const LocoNet::Message& msg);
|
||||
void read();
|
||||
void read() final;
|
||||
|
||||
public:
|
||||
CLASS_ID("command_station.loconet_serial")
|
||||
CREATE(LocoNetSerial)
|
||||
|
||||
Property<std::string> port;
|
||||
Property<LocoNetSerialInterface> interface;
|
||||
Property<uint32_t> baudrate;
|
||||
Property<SerialFlowControl> flowControl;
|
||||
ObjectProperty<LocoNet::LocoNet> loconet;
|
||||
|
||||
LocoNetSerial(const std::weak_ptr<World>& world, std::string_view _id);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
103
server/src/hardware/commandstation/serialcommandstation.cpp
Normale Datei
103
server/src/hardware/commandstation/serialcommandstation.cpp
Normale Datei
@ -0,0 +1,103 @@
|
||||
/**
|
||||
* server/src/hardware/commandstation/serialcommandstation.hpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2020 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 "serialcommandstation.hpp"
|
||||
#include "../../core/traintastic.hpp"
|
||||
#include "../../core/eventloop.hpp"
|
||||
|
||||
SerialCommandStation::SerialCommandStation(const std::weak_ptr<World>& world, std::string_view _id) :
|
||||
CommandStation(world, _id),
|
||||
m_serialPort{Traintastic::instance->ioContext()},
|
||||
m_readBufferOffset{0},
|
||||
port{this, "port", "/dev/ttyUSB0", PropertyFlags::ReadWrite | PropertyFlags::Store},
|
||||
baudrate{this, "baudrate", 19200, PropertyFlags::ReadWrite | PropertyFlags::Store,
|
||||
[this](uint32_t)
|
||||
{
|
||||
//interface = LocoNetSerialInterface::Custom;
|
||||
}},
|
||||
flowControl{this, "flow_control", SerialFlowControl::None, PropertyFlags::ReadWrite | PropertyFlags::Store,
|
||||
[this](SerialFlowControl)
|
||||
{
|
||||
//interface = LocoNetSerialInterface::Custom;
|
||||
}}
|
||||
{
|
||||
port.addAttributeEnabled(!online);
|
||||
baudrate.addAttributeEnabled(!online);
|
||||
flowControl.addAttributeEnabled(!online);
|
||||
|
||||
m_interfaceItems.insertBefore(port, notes);
|
||||
m_interfaceItems.insertBefore(baudrate, notes);
|
||||
m_interfaceItems.insertBefore(flowControl, notes);
|
||||
}
|
||||
|
||||
bool SerialCommandStation::setOnline(bool& value)
|
||||
{
|
||||
if(!m_serialPort.is_open() && value)
|
||||
{
|
||||
if(!start())
|
||||
{
|
||||
value = false;
|
||||
return false;
|
||||
}
|
||||
m_readBufferOffset = 0;
|
||||
read();
|
||||
|
||||
//loconet->queryLocoSlots();
|
||||
}
|
||||
else if(m_serialPort.is_open() && !value)
|
||||
stop();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SerialCommandStation::start()
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
m_serialPort.open(port, ec);
|
||||
if(ec)
|
||||
{
|
||||
logError("open: " + ec.message());
|
||||
return false;
|
||||
}
|
||||
|
||||
m_serialPort.set_option(boost::asio::serial_port_base::baud_rate(baudrate));
|
||||
m_serialPort.set_option(boost::asio::serial_port_base::character_size(8));
|
||||
m_serialPort.set_option(boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one));
|
||||
m_serialPort.set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::none));
|
||||
switch(flowControl)
|
||||
{
|
||||
case SerialFlowControl::None:
|
||||
m_serialPort.set_option(boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::none));
|
||||
break;
|
||||
|
||||
case SerialFlowControl::Hardware:
|
||||
m_serialPort.set_option(boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::hardware));
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void SerialCommandStation::stop()
|
||||
{
|
||||
// TODO: send power off cmd??
|
||||
m_serialPort.close();
|
||||
}
|
||||
51
server/src/hardware/commandstation/serialcommandstation.hpp
Normale Datei
51
server/src/hardware/commandstation/serialcommandstation.hpp
Normale Datei
@ -0,0 +1,51 @@
|
||||
/**
|
||||
* server/src/hardware/commandstation/serialcommandstation.hpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2020 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_COMMANDSTATION_SERIALCOMMANDSTATION_HPP
|
||||
#define TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_SERIALCOMMANDSTATION_HPP
|
||||
|
||||
#include "commandstation.hpp"
|
||||
#include "../../enum/serialflowcontrol.hpp"
|
||||
#include <boost/asio/serial_port.hpp>
|
||||
|
||||
class SerialCommandStation : public CommandStation
|
||||
{
|
||||
protected:
|
||||
boost::asio::serial_port m_serialPort;
|
||||
std::array<uint8_t, 1024> m_readBuffer;
|
||||
uint16_t m_readBufferOffset;
|
||||
|
||||
bool setOnline(bool& value) final;
|
||||
|
||||
bool start();
|
||||
void stop();
|
||||
virtual void read() = 0;
|
||||
|
||||
public:
|
||||
Property<std::string> port;
|
||||
Property<uint32_t> baudrate;
|
||||
Property<SerialFlowControl> flowControl;
|
||||
|
||||
SerialCommandStation(const std::weak_ptr<World>& world, std::string_view _id);
|
||||
};
|
||||
|
||||
#endif
|
||||
200
server/src/hardware/commandstation/xpressnetserial.cpp
Normale Datei
200
server/src/hardware/commandstation/xpressnetserial.cpp
Normale Datei
@ -0,0 +1,200 @@
|
||||
/**
|
||||
* hardware/commandstation/xpressnetserial.cpp
|
||||
*
|
||||
* This file is part of the traintastic source code
|
||||
*
|
||||
* Copyright (C) 2019-2020 Reinder Feenstra <reinderfeenstra@gmail.com>
|
||||
*
|
||||
* 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 "xpressnetserial.hpp"
|
||||
#include "../../world/world.hpp"
|
||||
#include "../../core/traintastic.hpp"
|
||||
#include "../../core/eventloop.hpp"
|
||||
|
||||
XpressNetSerial::XpressNetSerial(const std::weak_ptr<World>& world, std::string_view _id) :
|
||||
SerialCommandStation(world, _id),
|
||||
interface{this, "interface", XpressNetSerialInterface::LenzLI100, PropertyFlags::ReadWrite | PropertyFlags::Store,
|
||||
[this](XpressNetSerialInterface value)
|
||||
{
|
||||
switch(value)
|
||||
{
|
||||
case XpressNetSerialInterface::Custom:
|
||||
break;
|
||||
|
||||
case XpressNetSerialInterface::LenzLI100:
|
||||
case XpressNetSerialInterface::RoSoftS88XPressNetLI:
|
||||
baudrate.setValueInternal(9600);
|
||||
flowControl.setValueInternal(SerialFlowControl::Hardware);
|
||||
break;
|
||||
|
||||
case XpressNetSerialInterface::LenzLI100F:
|
||||
baudrate.setValueInternal(19200);
|
||||
flowControl.setValueInternal(SerialFlowControl::Hardware);
|
||||
break;
|
||||
|
||||
case XpressNetSerialInterface::LenzLI101F:
|
||||
baudrate.setValueInternal(19200);
|
||||
flowControl.setValueInternal(SerialFlowControl::Hardware);
|
||||
break;
|
||||
}
|
||||
}},
|
||||
xpressnet{this, "xpressnet", nullptr, PropertyFlags::ReadOnly | PropertyFlags::Store | PropertyFlags::SubObject}
|
||||
{
|
||||
name = "XpressNet (serial)";
|
||||
xpressnet.setValueInternal(std::make_shared<XpressNet>(*this, xpressnet.name(), std::bind(&XpressNetSerial::send, this, std::placeholders::_1)));
|
||||
|
||||
m_interfaceItems.insertBefore(interface, baudrate);
|
||||
m_interfaceItems.insertBefore(xpressnet, notes);
|
||||
}
|
||||
/*
|
||||
bool XpressNetSerial::setOnline(bool& value)
|
||||
{
|
||||
if(!m_serialPort.is_open() && value)
|
||||
{
|
||||
if(!start())
|
||||
{
|
||||
value = false;
|
||||
return false;
|
||||
}
|
||||
read();
|
||||
}
|
||||
else if(m_serialPort.is_open() && !value)
|
||||
stop();
|
||||
|
||||
return true;
|
||||
}
|
||||
*/
|
||||
void XpressNetSerial::emergencyStopChanged(bool value)
|
||||
{
|
||||
CommandStation::emergencyStopChanged(value);
|
||||
|
||||
if(online)
|
||||
xpressnet->emergencyStopChanged(value);
|
||||
}
|
||||
|
||||
void XpressNetSerial::trackVoltageOffChanged(bool value)
|
||||
{
|
||||
CommandStation::trackVoltageOffChanged(value);
|
||||
|
||||
if(online)
|
||||
xpressnet->trackVoltageOffChanged(value);
|
||||
}
|
||||
|
||||
void XpressNetSerial::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber)
|
||||
{
|
||||
CommandStation::decoderChanged(decoder, changes, functionNumber);
|
||||
|
||||
if(online)
|
||||
xpressnet->decoderChanged(decoder, changes, functionNumber);
|
||||
}
|
||||
/*
|
||||
bool XpressNetSerial::start()
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
m_serialPort.open(port, ec);
|
||||
if(ec)
|
||||
{
|
||||
logError("open: " + ec.message());
|
||||
return false;
|
||||
}
|
||||
|
||||
m_serialPort.set_option(boost::asio::serial_port_base::baud_rate(baudrate));
|
||||
m_serialPort.set_option(boost::asio::serial_port_base::character_size(8));
|
||||
m_serialPort.set_option(boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one));
|
||||
m_serialPort.set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::none));
|
||||
m_serialPort.set_option(boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::hardware));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void XpressNetSerial::stop()
|
||||
{
|
||||
// send bus off cmd
|
||||
m_serialPort.close();
|
||||
}
|
||||
*/
|
||||
bool XpressNetSerial::send(const XpressNet::Message& msg)
|
||||
{
|
||||
assert(XpressNet::isChecksumValid(msg));
|
||||
if(!m_serialPort.is_open())
|
||||
return false;
|
||||
boost::system::error_code ec;
|
||||
m_serialPort.write_some(boost::asio::buffer(static_cast<const void*>(&msg), msg.size()), ec); // TODO async
|
||||
if(ec)
|
||||
{
|
||||
logError("write_some: " + ec.message());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void XpressNetSerial::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)
|
||||
{
|
||||
const uint8_t* pos = m_readBuffer.data();
|
||||
bytesTransferred += m_readBufferOffset;
|
||||
|
||||
while(bytesTransferred > 1)
|
||||
{
|
||||
const XpressNet::Message* message = reinterpret_cast<const XpressNet::Message*>(pos);
|
||||
|
||||
size_t drop = 0;
|
||||
while(message->size() <= bytesTransferred && !XpressNet::isChecksumValid(*message) && drop < bytesTransferred)
|
||||
{
|
||||
drop++;
|
||||
pos++;
|
||||
bytesTransferred--;
|
||||
message = reinterpret_cast<const XpressNet::Message*>(pos);
|
||||
}
|
||||
|
||||
if(drop != 0)
|
||||
{
|
||||
EventLoop::call(
|
||||
[this, drop]()
|
||||
{
|
||||
logWarning("received malformed data, dropped " + std::to_string(drop) + " byte(s)");
|
||||
});
|
||||
}
|
||||
else if(message->size() <= bytesTransferred)
|
||||
{
|
||||
xpressnet->receive(*message);
|
||||
pos += message->size();
|
||||
bytesTransferred -= message->size();
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if(bytesTransferred != 0)
|
||||
memmove(m_readBuffer.data(), pos, bytesTransferred);
|
||||
m_readBufferOffset = bytesTransferred;
|
||||
|
||||
read();
|
||||
}
|
||||
else
|
||||
EventLoop::call(
|
||||
[this, ec]()
|
||||
{
|
||||
logError("async_read_some: " + ec.message());
|
||||
online = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
/**
|
||||
* server/src/hardware/commandstation/li10x.hpp
|
||||
* server/src/hardware/commandstation/xpressnetserial.hpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
@ -23,22 +23,23 @@
|
||||
#ifndef TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_LI10X_HPP
|
||||
#define TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_LI10X_HPP
|
||||
|
||||
#include "commandstation.hpp"
|
||||
#include "serialcommandstation.hpp"
|
||||
#include "../../enum/xpressnetserialinterface.hpp"
|
||||
#include "../protocol/xpressnet.hpp"
|
||||
#include <boost/asio/serial_port.hpp>
|
||||
//#include <boost/asio/serial_port.hpp>
|
||||
//#include "../../core/objectproperty.hpp"
|
||||
//#include "protocol/xpressnet.hpp"
|
||||
|
||||
class LI10x : public CommandStation
|
||||
class XpressNetSerial : public SerialCommandStation
|
||||
{
|
||||
protected:
|
||||
boost::asio::serial_port m_serialPort;
|
||||
std::array<uint8_t, 32> m_readBuffer;
|
||||
std::unique_ptr<uint8_t[]> m_readMessage;
|
||||
uint8_t m_readMessageTodo;
|
||||
uint8_t m_readMessagePos;
|
||||
//boost::asio::serial_port m_serialPort;
|
||||
//std::array<uint8_t, 32> m_readBuffer;
|
||||
//std::unique_ptr<uint8_t[]> m_readMessage;
|
||||
//uint8_t m_readMessageTodo;
|
||||
//uint8_t m_readMessagePos;
|
||||
|
||||
bool setOnline(bool& value) final;
|
||||
//bool setOnline(bool& value) final;
|
||||
void emergencyStopChanged(bool value) final;
|
||||
void trackVoltageOffChanged(bool value) final;
|
||||
void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber) final;
|
||||
@ -50,15 +51,13 @@ class LI10x : public CommandStation
|
||||
void read();
|
||||
|
||||
public:
|
||||
CLASS_ID("command_station.li10x")
|
||||
CREATE(LI10x)
|
||||
CLASS_ID("command_station.xpressnet_serial")
|
||||
CREATE(XpressNetSerial)
|
||||
|
||||
Property<std::string> port;
|
||||
Property<uint32_t> baudrate;
|
||||
//Property<bool> useCTS;
|
||||
Property<XpressNetSerialInterface> interface;
|
||||
ObjectProperty<XpressNet> xpressnet;
|
||||
|
||||
LI10x(const std::weak_ptr<World>& world, std::string_view _id);
|
||||
XpressNetSerial(const std::weak_ptr<World>& world, std::string_view _id);
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -90,7 +90,7 @@ class LocoNet : public SubObject
|
||||
}
|
||||
};
|
||||
|
||||
CommandStation* m_commandStation; // valid if parent is command station, else nullptr
|
||||
CommandStation* const m_commandStation; // valid if parent is command station, else nullptr
|
||||
std::function<bool(const Message&)> m_send;
|
||||
std::atomic_bool m_debugLog;
|
||||
Slots m_slots;
|
||||
|
||||
@ -22,7 +22,9 @@
|
||||
|
||||
#include "xpressnet.hpp"
|
||||
#include "../../core/traintastic.hpp"
|
||||
#include "../../core/eventloop.hpp"
|
||||
#include "../decoder/decoder.hpp"
|
||||
#include "../../utils/to_hex.hpp"
|
||||
|
||||
uint8_t XpressNet::calcChecksum(const void* msg)
|
||||
{
|
||||
@ -47,8 +49,10 @@ bool XpressNet::isChecksumValid(const Message& msg)
|
||||
|
||||
XpressNet::XpressNet(Object& _parent, const std::string& parentPropertyName, std::function<bool(const Message&)> send) :
|
||||
SubObject(_parent, parentPropertyName),
|
||||
m_commandStation{dynamic_cast<CommandStation*>(&_parent)},
|
||||
m_send{std::move(send)},
|
||||
commandStation{this, "command_station", XpressNetCommandStation::Custom, PropertyFlags::ReadWrite,
|
||||
m_debugLog{true},
|
||||
commandStation{this, "command_station", XpressNetCommandStation::Custom, PropertyFlags::ReadWrite | PropertyFlags::Store,
|
||||
[this](XpressNetCommandStation value)
|
||||
{
|
||||
switch(value)
|
||||
@ -63,20 +67,25 @@ XpressNet::XpressNet(Object& _parent, const std::string& parentPropertyName, std
|
||||
break;
|
||||
}
|
||||
}},
|
||||
useEmergencyStopLocomotiveCommand{this, "use_emergency_stop_locomotive_command", false, PropertyFlags::ReadWrite,
|
||||
useEmergencyStopLocomotiveCommand{this, "use_emergency_stop_locomotive_command", false, PropertyFlags::ReadWrite | PropertyFlags::Store,
|
||||
[this](bool)
|
||||
{
|
||||
commandStation = XpressNetCommandStation::Custom;
|
||||
}},
|
||||
useFunctionStateCommands{this, "use_function_state_commands", false, PropertyFlags::ReadWrite,
|
||||
useFunctionStateCommands{this, "use_function_state_commands", false, PropertyFlags::ReadWrite | PropertyFlags::Store,
|
||||
[this](bool)
|
||||
{
|
||||
commandStation = XpressNetCommandStation::Custom;
|
||||
}},
|
||||
useRocoF13F20Command{this, "use_roco_f13_f20_command", false, PropertyFlags::ReadWrite,
|
||||
useRocoF13F20Command{this, "use_roco_f13_f20_command", false, PropertyFlags::ReadWrite | PropertyFlags::Store,
|
||||
[this](bool)
|
||||
{
|
||||
commandStation = XpressNetCommandStation::Custom;
|
||||
}},
|
||||
debugLog{this, "debug_log", m_debugLog, PropertyFlags::ReadWrite | PropertyFlags::Store,
|
||||
[this](bool value)
|
||||
{
|
||||
m_debugLog = value;
|
||||
}}
|
||||
{
|
||||
m_interfaceItems.add(commandStation)
|
||||
@ -101,6 +110,69 @@ void XpressNet::worldEvent(WorldState state, WorldEvent event)
|
||||
useRocoF13F20Command.setAttributeEnabled(editable);
|
||||
}
|
||||
|
||||
void XpressNet::receive(const Message& message)
|
||||
{
|
||||
// NOTE: this runs outside the event loop !!!
|
||||
|
||||
assert(isChecksumValid(message));
|
||||
|
||||
if(m_debugLog)
|
||||
EventLoop::call([this, log="rx: " + to_string(message)](){ logDebug(log); });
|
||||
|
||||
if(m_commandStation)
|
||||
{
|
||||
switch(message.identification())
|
||||
{
|
||||
case 0x60:
|
||||
if(message == NormalOperationResumed())
|
||||
{
|
||||
EventLoop::call(
|
||||
[cs=m_commandStation->shared_ptr<CommandStation>()]()
|
||||
{
|
||||
cs->emergencyStop.setValueInternal(false);
|
||||
cs->trackVoltageOff.setValueInternal(false);
|
||||
});
|
||||
}
|
||||
else if(message == TrackPowerOff())
|
||||
{
|
||||
EventLoop::call(
|
||||
[cs=m_commandStation->shared_ptr<CommandStation>()]()
|
||||
{
|
||||
cs->trackVoltageOff.setValueInternal(true);
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x80:
|
||||
if(message == EmergencyStop())
|
||||
{
|
||||
EventLoop::call(
|
||||
[cs=m_commandStation->shared_ptr<CommandStation>()]()
|
||||
{
|
||||
cs->emergencyStop.setValueInternal(true);
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void XpressNet::emergencyStopChanged(bool value)
|
||||
{
|
||||
if(value)
|
||||
send(EmergencyStop());
|
||||
else if(m_commandStation && !m_commandStation->trackVoltageOff)
|
||||
send(NormalOperationResumed());
|
||||
}
|
||||
|
||||
void XpressNet::trackVoltageOffChanged(bool value)
|
||||
{
|
||||
if(!value)
|
||||
send(NormalOperationResumed());
|
||||
else
|
||||
send(TrackPowerOff());
|
||||
}
|
||||
|
||||
void XpressNet::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber)
|
||||
{
|
||||
logDebug("XpressNet::decoderChanged");
|
||||
@ -199,3 +271,31 @@ void XpressNet::decoderChanged(const Decoder& decoder, DecoderChangeFlags change
|
||||
logWarning("Function F" + std::to_string(functionNumber) + " not supported");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::string to_string(const XpressNet::Message& message, bool raw)
|
||||
{
|
||||
std::string s;
|
||||
|
||||
switch(message.identification())
|
||||
{
|
||||
default:
|
||||
raw = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if(raw)
|
||||
{
|
||||
s.append("[");
|
||||
const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&message);
|
||||
for(int i = 0; i < message.size(); i++)
|
||||
{
|
||||
if(i != 0)
|
||||
s.append(" ");
|
||||
s.append(to_hex(bytes[i]));
|
||||
}
|
||||
s.append("]");
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
@ -29,6 +29,7 @@
|
||||
#include "../../enum/xpressnetcommandstation.hpp"
|
||||
#include "../../hardware/decoder/decoderchangeflags.hpp"
|
||||
|
||||
class CommandStation;
|
||||
class Decoder;
|
||||
|
||||
class XpressNet : public SubObject
|
||||
@ -44,9 +45,14 @@ class XpressNet : public SubObject
|
||||
{
|
||||
uint8_t header;
|
||||
|
||||
uint8_t identification() const
|
||||
{
|
||||
return header & 0xF0;
|
||||
}
|
||||
|
||||
uint8_t dataSize() const
|
||||
{
|
||||
return header & 0x0f;
|
||||
return header & 0x0F;
|
||||
}
|
||||
|
||||
uint8_t size() const
|
||||
@ -329,7 +335,9 @@ class XpressNet : public SubObject
|
||||
protected:
|
||||
static bool getFunctionValue(const Decoder& decoder, uint32_t number);
|
||||
|
||||
CommandStation* const m_commandStation; // valid if parent is command station, else nullptr
|
||||
std::function<bool(const Message&)> m_send;
|
||||
std::atomic_bool m_debugLog;
|
||||
|
||||
void worldEvent(WorldState state, WorldEvent event) final;
|
||||
|
||||
@ -340,13 +348,23 @@ class XpressNet : public SubObject
|
||||
Property<bool> useEmergencyStopLocomotiveCommand;
|
||||
Property<bool> useFunctionStateCommands;
|
||||
Property<bool> useRocoF13F20Command;
|
||||
Property<bool> debugLog;
|
||||
|
||||
XpressNet(Object& _parent, const std::string& parentPropertyName, std::function<bool(const Message&)> send);
|
||||
|
||||
bool send(const Message& msg) { return m_send(msg); }
|
||||
void receive(const Message& msg);
|
||||
|
||||
void emergencyStopChanged(bool value);
|
||||
void trackVoltageOffChanged(bool value);
|
||||
void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber);
|
||||
};
|
||||
|
||||
inline bool operator ==(const XpressNet::Message& lhs, const XpressNet::Message& rhs)
|
||||
{
|
||||
return lhs.size() == rhs.size() && std::memcmp(&lhs, &rhs, lhs.size()) == 0;
|
||||
}
|
||||
|
||||
std::string to_string(const XpressNet::Message& message, bool raw = false);
|
||||
|
||||
#endif
|
||||
|
||||
44
shared/src/traintastic/enum/xpressnetserialinterface.hpp
Normale Datei
44
shared/src/traintastic/enum/xpressnetserialinterface.hpp
Normale Datei
@ -0,0 +1,44 @@
|
||||
/**
|
||||
* shared/src/enum/xpressnetserialinterface.hpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2020 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_XPRESSNETSERIALINTERFACE_HPP
|
||||
#define TRAINTASTIC_SHARED_TRAINTASTIC_ENUM_XPRESSNETSERIALINTERFACE_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include "enum.hpp"
|
||||
|
||||
enum class XpressNetSerialInterface : uint16_t
|
||||
{
|
||||
Custom = 0,
|
||||
LenzLI100 = 1,
|
||||
LenzLI100F = 2,
|
||||
LenzLI101F = 3,
|
||||
RoSoftS88XPressNetLI = 4,
|
||||
};
|
||||
|
||||
template<>
|
||||
struct EnumName<XpressNetSerialInterface>
|
||||
{
|
||||
static constexpr char const* value = "xpressnet_serial_interface";
|
||||
};
|
||||
|
||||
#endif
|
||||
Laden…
x
In neuem Issue referenzieren
Einen Benutzer sperren