renamed li100 to xpressnetserial and added serialcommandstation base class

Dieser Commit ist enthalten in:
Reinder Feenstra 2020-08-04 17:00:09 +02:00
Ursprung 71ba4a87fb
Commit 6afb6eb66d
13 geänderte Dateien mit 598 neuen und 315 gelöschten Zeilen

Datei anzeigen

@ -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

Datei anzeigen

@ -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

Datei anzeigen

@ -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()); });
});
}

Datei anzeigen

@ -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())

Datei anzeigen

@ -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

Datei anzeigen

@ -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();
}

Datei anzeigen

@ -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

Datei anzeigen

@ -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;
});
});
}

Datei anzeigen

@ -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

Datei anzeigen

@ -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;

Datei anzeigen

@ -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;
}

Datei anzeigen

@ -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

Datei anzeigen

@ -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