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.
|
* This file is part of the traintastic source code.
|
||||||
*
|
*
|
||||||
@ -21,21 +21,21 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "commandstations.hpp"
|
#include "commandstations.hpp"
|
||||||
#include "li10x.hpp"
|
|
||||||
#include "loconetserial.hpp"
|
#include "loconetserial.hpp"
|
||||||
#ifndef DISABLE_USB_XPRESSNET_INTERFACE
|
#ifndef DISABLE_USB_XPRESSNET_INTERFACE
|
||||||
#include "usbxpressnetinterface.hpp"
|
#include "usbxpressnetinterface.hpp"
|
||||||
#endif
|
#endif
|
||||||
|
#include "xpressnetserial.hpp"
|
||||||
#include "rocoz21.hpp"
|
#include "rocoz21.hpp"
|
||||||
|
|
||||||
const std::vector<std::string_view>& CommandStations::classList()
|
const std::vector<std::string_view>& CommandStations::classList()
|
||||||
{
|
{
|
||||||
static std::vector<std::string_view> list({
|
static std::vector<std::string_view> list({
|
||||||
LI10x::classId,
|
|
||||||
LocoNetSerial::classId,
|
LocoNetSerial::classId,
|
||||||
#ifndef DISABLE_USB_XPRESSNET_INTERFACE
|
#ifndef DISABLE_USB_XPRESSNET_INTERFACE
|
||||||
USBXpressNetInterface::classId,
|
USBXpressNetInterface::classId,
|
||||||
#endif
|
#endif
|
||||||
|
XpressNetSerial::classId,
|
||||||
RocoZ21::classId,
|
RocoZ21::classId,
|
||||||
});
|
});
|
||||||
return list;
|
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)
|
std::shared_ptr<CommandStation> CommandStations::create(const std::weak_ptr<World>& world, std::string_view classId, std::string_view id)
|
||||||
{
|
{
|
||||||
if(classId == LI10x::classId)
|
if(classId == LocoNetSerial::classId)
|
||||||
return LI10x::create(world, id);
|
|
||||||
else if(classId == LocoNetSerial::classId)
|
|
||||||
return LocoNetSerial::create(world, id);
|
return LocoNetSerial::create(world, id);
|
||||||
#ifndef DISABLE_USB_XPRESSNET_INTERFACE
|
#ifndef DISABLE_USB_XPRESSNET_INTERFACE
|
||||||
else if(classId == USBXpressNetInterface::classId)
|
else if(classId == USBXpressNetInterface::classId)
|
||||||
return USBXpressNetInterface::create(world, id);
|
return USBXpressNetInterface::create(world, id);
|
||||||
#endif
|
#endif
|
||||||
|
else if(classId == XpressNetSerial::classId)
|
||||||
|
return XpressNetSerial::create(world, id);
|
||||||
else if(classId == RocoZ21::classId)
|
else if(classId == RocoZ21::classId)
|
||||||
return RocoZ21::create(world, id);
|
return RocoZ21::create(world, id);
|
||||||
else
|
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"
|
#include "../../core/eventloop.hpp"
|
||||||
|
|
||||||
LocoNetSerial::LocoNetSerial(const std::weak_ptr<World>& world, std::string_view _id) :
|
LocoNetSerial::LocoNetSerial(const std::weak_ptr<World>& world, std::string_view _id) :
|
||||||
CommandStation(world, _id),
|
SerialCommandStation(world, _id),
|
||||||
m_serialPort{Traintastic::instance->ioContext()},
|
|
||||||
m_readBufferOffset{0},
|
|
||||||
port{this, "port", "/dev/ttyUSB0", PropertyFlags::ReadWrite | PropertyFlags::Store},
|
|
||||||
interface{this, "interface", LocoNetSerialInterface::Custom, PropertyFlags::ReadWrite | PropertyFlags::Store,
|
interface{this, "interface", LocoNetSerialInterface::Custom, PropertyFlags::ReadWrite | PropertyFlags::Store,
|
||||||
[this](LocoNetSerialInterface value)
|
[this](LocoNetSerialInterface value)
|
||||||
{
|
{
|
||||||
@ -49,33 +46,17 @@ LocoNetSerial::LocoNetSerial(const std::weak_ptr<World>& world, std::string_view
|
|||||||
break;
|
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}
|
loconet{this, "loconet", nullptr, PropertyFlags::ReadOnly | PropertyFlags::Store | PropertyFlags::SubObject}
|
||||||
{
|
{
|
||||||
name = "LocoNet (serial)";
|
name = "LocoNet (serial)";
|
||||||
loconet.setValueInternal(std::make_shared<LocoNet::LocoNet>(*this, loconet.name(), std::bind(&LocoNetSerial::send, this, std::placeholders::_1)));
|
loconet.setValueInternal(std::make_shared<LocoNet::LocoNet>(*this, loconet.name(), std::bind(&LocoNetSerial::send, this, std::placeholders::_1)));
|
||||||
|
|
||||||
port.addAttributeEnabled(!online);
|
|
||||||
interface.addAttributeEnabled(!online);
|
interface.addAttributeEnabled(!online);
|
||||||
baudrate.addAttributeEnabled(!online);
|
|
||||||
flowControl.addAttributeEnabled(!online);
|
|
||||||
|
|
||||||
m_interfaceItems.insertBefore(port, notes);
|
m_interfaceItems.insertBefore(interface, baudrate);
|
||||||
m_interfaceItems.insertBefore(interface, notes);
|
|
||||||
m_interfaceItems.insertBefore(baudrate, notes);
|
|
||||||
m_interfaceItems.insertBefore(flowControl, notes);
|
|
||||||
m_interfaceItems.insertBefore(loconet, notes);
|
m_interfaceItems.insertBefore(loconet, notes);
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
bool LocoNetSerial::setOnline(bool& value)
|
bool LocoNetSerial::setOnline(bool& value)
|
||||||
{
|
{
|
||||||
if(!m_serialPort.is_open() && value)
|
if(!m_serialPort.is_open() && value)
|
||||||
@ -94,7 +75,7 @@ bool LocoNetSerial::setOnline(bool& value)
|
|||||||
stop();
|
stop();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
void LocoNetSerial::emergencyStopChanged(bool value)
|
void LocoNetSerial::emergencyStopChanged(bool value)
|
||||||
{
|
{
|
||||||
@ -120,39 +101,6 @@ void LocoNetSerial::decoderChanged(const Decoder& decoder, DecoderChangeFlags ch
|
|||||||
loconet->decoderChanged(decoder, changes, functionNumber);
|
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)
|
bool LocoNetSerial::send(const LocoNet::Message& message)
|
||||||
{
|
{
|
||||||
if(!m_serialPort.is_open())
|
if(!m_serialPort.is_open())
|
||||||
|
|||||||
@ -23,20 +23,15 @@
|
|||||||
#ifndef TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_LOCONETSERIAL_HPP
|
#ifndef TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_LOCONETSERIAL_HPP
|
||||||
#define 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 "../protocol/loconet/loconet.hpp"
|
||||||
#include "../../enum/loconetserialinterface.hpp"
|
#include "../../enum/loconetserialinterface.hpp"
|
||||||
#include "../../enum/serialflowcontrol.hpp"
|
//#include "../../enum/serialflowcontrol.hpp"
|
||||||
#include <boost/asio/serial_port.hpp>
|
//#include <boost/asio/serial_port.hpp>
|
||||||
|
|
||||||
class LocoNetSerial : public CommandStation
|
class LocoNetSerial : public SerialCommandStation
|
||||||
{
|
{
|
||||||
protected:
|
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 emergencyStopChanged(bool value) final;
|
||||||
void trackVoltageOffChanged(bool value) final;
|
void trackVoltageOffChanged(bool value) final;
|
||||||
void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber) final;
|
void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber) final;
|
||||||
@ -44,20 +39,16 @@ class LocoNetSerial : public CommandStation
|
|||||||
bool start();
|
bool start();
|
||||||
void stop();
|
void stop();
|
||||||
bool send(const LocoNet::Message& msg);
|
bool send(const LocoNet::Message& msg);
|
||||||
void read();
|
void read() final;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CLASS_ID("command_station.loconet_serial")
|
CLASS_ID("command_station.loconet_serial")
|
||||||
CREATE(LocoNetSerial)
|
CREATE(LocoNetSerial)
|
||||||
|
|
||||||
Property<std::string> port;
|
|
||||||
Property<LocoNetSerialInterface> interface;
|
Property<LocoNetSerialInterface> interface;
|
||||||
Property<uint32_t> baudrate;
|
|
||||||
Property<SerialFlowControl> flowControl;
|
|
||||||
ObjectProperty<LocoNet::LocoNet> loconet;
|
ObjectProperty<LocoNet::LocoNet> loconet;
|
||||||
|
|
||||||
LocoNetSerial(const std::weak_ptr<World>& world, std::string_view _id);
|
LocoNetSerial(const std::weak_ptr<World>& world, std::string_view _id);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#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.
|
* This file is part of the traintastic source code.
|
||||||
*
|
*
|
||||||
@ -23,22 +23,23 @@
|
|||||||
#ifndef TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_LI10X_HPP
|
#ifndef TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_LI10X_HPP
|
||||||
#define 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 "../protocol/xpressnet.hpp"
|
||||||
#include <boost/asio/serial_port.hpp>
|
//#include <boost/asio/serial_port.hpp>
|
||||||
//#include "../../core/objectproperty.hpp"
|
//#include "../../core/objectproperty.hpp"
|
||||||
//#include "protocol/xpressnet.hpp"
|
//#include "protocol/xpressnet.hpp"
|
||||||
|
|
||||||
class LI10x : public CommandStation
|
class XpressNetSerial : public SerialCommandStation
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
boost::asio::serial_port m_serialPort;
|
//boost::asio::serial_port m_serialPort;
|
||||||
std::array<uint8_t, 32> m_readBuffer;
|
//std::array<uint8_t, 32> m_readBuffer;
|
||||||
std::unique_ptr<uint8_t[]> m_readMessage;
|
//std::unique_ptr<uint8_t[]> m_readMessage;
|
||||||
uint8_t m_readMessageTodo;
|
//uint8_t m_readMessageTodo;
|
||||||
uint8_t m_readMessagePos;
|
//uint8_t m_readMessagePos;
|
||||||
|
|
||||||
bool setOnline(bool& value) final;
|
//bool setOnline(bool& value) final;
|
||||||
void emergencyStopChanged(bool value) final;
|
void emergencyStopChanged(bool value) final;
|
||||||
void trackVoltageOffChanged(bool value) final;
|
void trackVoltageOffChanged(bool value) final;
|
||||||
void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber) final;
|
void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber) final;
|
||||||
@ -50,15 +51,13 @@ class LI10x : public CommandStation
|
|||||||
void read();
|
void read();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CLASS_ID("command_station.li10x")
|
CLASS_ID("command_station.xpressnet_serial")
|
||||||
CREATE(LI10x)
|
CREATE(XpressNetSerial)
|
||||||
|
|
||||||
Property<std::string> port;
|
Property<XpressNetSerialInterface> interface;
|
||||||
Property<uint32_t> baudrate;
|
|
||||||
//Property<bool> useCTS;
|
|
||||||
ObjectProperty<XpressNet> xpressnet;
|
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
|
#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::function<bool(const Message&)> m_send;
|
||||||
std::atomic_bool m_debugLog;
|
std::atomic_bool m_debugLog;
|
||||||
Slots m_slots;
|
Slots m_slots;
|
||||||
|
|||||||
@ -22,7 +22,9 @@
|
|||||||
|
|
||||||
#include "xpressnet.hpp"
|
#include "xpressnet.hpp"
|
||||||
#include "../../core/traintastic.hpp"
|
#include "../../core/traintastic.hpp"
|
||||||
|
#include "../../core/eventloop.hpp"
|
||||||
#include "../decoder/decoder.hpp"
|
#include "../decoder/decoder.hpp"
|
||||||
|
#include "../../utils/to_hex.hpp"
|
||||||
|
|
||||||
uint8_t XpressNet::calcChecksum(const void* msg)
|
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) :
|
XpressNet::XpressNet(Object& _parent, const std::string& parentPropertyName, std::function<bool(const Message&)> send) :
|
||||||
SubObject(_parent, parentPropertyName),
|
SubObject(_parent, parentPropertyName),
|
||||||
|
m_commandStation{dynamic_cast<CommandStation*>(&_parent)},
|
||||||
m_send{std::move(send)},
|
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)
|
[this](XpressNetCommandStation value)
|
||||||
{
|
{
|
||||||
switch(value)
|
switch(value)
|
||||||
@ -63,20 +67,25 @@ XpressNet::XpressNet(Object& _parent, const std::string& parentPropertyName, std
|
|||||||
break;
|
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)
|
[this](bool)
|
||||||
{
|
{
|
||||||
commandStation = XpressNetCommandStation::Custom;
|
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)
|
[this](bool)
|
||||||
{
|
{
|
||||||
commandStation = XpressNetCommandStation::Custom;
|
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)
|
[this](bool)
|
||||||
{
|
{
|
||||||
commandStation = XpressNetCommandStation::Custom;
|
commandStation = XpressNetCommandStation::Custom;
|
||||||
|
}},
|
||||||
|
debugLog{this, "debug_log", m_debugLog, PropertyFlags::ReadWrite | PropertyFlags::Store,
|
||||||
|
[this](bool value)
|
||||||
|
{
|
||||||
|
m_debugLog = value;
|
||||||
}}
|
}}
|
||||||
{
|
{
|
||||||
m_interfaceItems.add(commandStation)
|
m_interfaceItems.add(commandStation)
|
||||||
@ -101,6 +110,69 @@ void XpressNet::worldEvent(WorldState state, WorldEvent event)
|
|||||||
useRocoF13F20Command.setAttributeEnabled(editable);
|
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)
|
void XpressNet::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber)
|
||||||
{
|
{
|
||||||
logDebug("XpressNet::decoderChanged");
|
logDebug("XpressNet::decoderChanged");
|
||||||
@ -199,3 +271,31 @@ void XpressNet::decoderChanged(const Decoder& decoder, DecoderChangeFlags change
|
|||||||
logWarning("Function F" + std::to_string(functionNumber) + " not supported");
|
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 "../../enum/xpressnetcommandstation.hpp"
|
||||||
#include "../../hardware/decoder/decoderchangeflags.hpp"
|
#include "../../hardware/decoder/decoderchangeflags.hpp"
|
||||||
|
|
||||||
|
class CommandStation;
|
||||||
class Decoder;
|
class Decoder;
|
||||||
|
|
||||||
class XpressNet : public SubObject
|
class XpressNet : public SubObject
|
||||||
@ -44,9 +45,14 @@ class XpressNet : public SubObject
|
|||||||
{
|
{
|
||||||
uint8_t header;
|
uint8_t header;
|
||||||
|
|
||||||
|
uint8_t identification() const
|
||||||
|
{
|
||||||
|
return header & 0xF0;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t dataSize() const
|
uint8_t dataSize() const
|
||||||
{
|
{
|
||||||
return header & 0x0f;
|
return header & 0x0F;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t size() const
|
uint8_t size() const
|
||||||
@ -329,7 +335,9 @@ class XpressNet : public SubObject
|
|||||||
protected:
|
protected:
|
||||||
static bool getFunctionValue(const Decoder& decoder, uint32_t number);
|
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::function<bool(const Message&)> m_send;
|
||||||
|
std::atomic_bool m_debugLog;
|
||||||
|
|
||||||
void worldEvent(WorldState state, WorldEvent event) final;
|
void worldEvent(WorldState state, WorldEvent event) final;
|
||||||
|
|
||||||
@ -340,13 +348,23 @@ class XpressNet : public SubObject
|
|||||||
Property<bool> useEmergencyStopLocomotiveCommand;
|
Property<bool> useEmergencyStopLocomotiveCommand;
|
||||||
Property<bool> useFunctionStateCommands;
|
Property<bool> useFunctionStateCommands;
|
||||||
Property<bool> useRocoF13F20Command;
|
Property<bool> useRocoF13F20Command;
|
||||||
|
Property<bool> debugLog;
|
||||||
|
|
||||||
XpressNet(Object& _parent, const std::string& parentPropertyName, std::function<bool(const Message&)> send);
|
XpressNet(Object& _parent, const std::string& parentPropertyName, std::function<bool(const Message&)> send);
|
||||||
|
|
||||||
bool send(const Message& msg) { return m_send(msg); }
|
bool send(const Message& msg) { return m_send(msg); }
|
||||||
void receive(const Message& msg);
|
void receive(const Message& msg);
|
||||||
|
|
||||||
|
void emergencyStopChanged(bool value);
|
||||||
|
void trackVoltageOffChanged(bool value);
|
||||||
void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber);
|
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
|
#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