wlanmaus: rewrite -> new kernel/iohandler model
Dieser Commit ist enthalten in:
Ursprung
be63db9378
Commit
35869a7fd8
@ -115,8 +115,10 @@ file(GLOB SOURCES
|
||||
"src/hardware/protocol/xpressnet/*.cpp"
|
||||
"src/hardware/protocol/xpressnet/iohandler/*.hpp"
|
||||
"src/hardware/protocol/xpressnet/iohandler/*.cpp"
|
||||
# "src/hardware/protocol/z21/*.hpp"
|
||||
# "src/hardware/protocol/z21/*.cpp"
|
||||
"src/hardware/protocol/z21/*.hpp"
|
||||
"src/hardware/protocol/z21/*.cpp"
|
||||
"src/hardware/protocol/z21/iohandler/*.hpp"
|
||||
"src/hardware/protocol/z21/iohandler/*.cpp"
|
||||
"src/log/*.hpp"
|
||||
"src/log/*.cpp"
|
||||
"src/train/*.hpp"
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2021 Reinder Feenstra
|
||||
* Copyright (C) 2019-2022 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -289,4 +289,5 @@ void Decoder::changed(DecoderChangeFlags changes, uint32_t functionNumber)
|
||||
{
|
||||
if(interface)
|
||||
interface->decoderChanged(*this, changes, functionNumber);
|
||||
decoderChanged(*this, changes, functionNumber);
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2021 Reinder Feenstra
|
||||
* Copyright (C) 2019-2022 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -90,6 +90,8 @@ class Decoder : public IdObject
|
||||
ObjectProperty<DecoderFunctions> functions;
|
||||
Property<std::string> notes;
|
||||
|
||||
boost::signals2::signal<void (Decoder&, DecoderChangeFlags, uint32_t)> decoderChanged;
|
||||
|
||||
Decoder(const std::weak_ptr<World>& world, std::string_view _id);
|
||||
|
||||
void addToWorld() final;
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2020 Reinder Feenstra
|
||||
* Copyright (C) 2019-2022 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -69,6 +69,33 @@ TableModelPtr DecoderList::getModel()
|
||||
return std::make_shared<DecoderListTableModel>(*this);
|
||||
}
|
||||
|
||||
std::shared_ptr<Decoder> DecoderList::getDecoder(uint16_t address) const
|
||||
{
|
||||
auto it = std::find_if(begin(), end(),
|
||||
[address](const auto& decoder)
|
||||
{
|
||||
return decoder->address.value() == address;
|
||||
});
|
||||
if(it != end())
|
||||
return *it;
|
||||
return {};
|
||||
}
|
||||
|
||||
std::shared_ptr<Decoder> DecoderList::getDecoder(DecoderProtocol protocol, uint16_t address, bool longAddress) const
|
||||
{
|
||||
auto it = std::find_if(begin(), end(),
|
||||
[protocol, address, longAddress](const auto& decoder)
|
||||
{
|
||||
return
|
||||
decoder->protocol.value() == protocol &&
|
||||
decoder->address.value() == address &&
|
||||
(protocol != DecoderProtocol::DCC || decoder->longAddress == longAddress);
|
||||
});
|
||||
if(it != end())
|
||||
return *it;
|
||||
return {};
|
||||
}
|
||||
|
||||
void DecoderList::worldEvent(WorldState state, WorldEvent event)
|
||||
{
|
||||
ObjectList<Decoder>::worldEvent(state, event);
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2020 Reinder Feenstra
|
||||
* Copyright (C) 2019-2022 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -42,6 +42,9 @@ class DecoderList : public ObjectList<Decoder>
|
||||
DecoderList(Object& _parent, const std::string& parentPropertyName);
|
||||
|
||||
TableModelPtr getModel() final;
|
||||
|
||||
std::shared_ptr<Decoder> getDecoder(uint16_t address) const;
|
||||
std::shared_ptr<Decoder> getDecoder(DecoderProtocol protocol, uint16_t address, bool longAddress = false) const;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2021 Reinder Feenstra
|
||||
* Copyright (C) 2021-2022 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -29,6 +29,7 @@ std::shared_ptr<Interface> Interfaces::create(const std::shared_ptr<World>& worl
|
||||
IF_CLASSID_CREATE(DCCPlusPlusInterface)
|
||||
IF_CLASSID_CREATE(ECoSInterface)
|
||||
IF_CLASSID_CREATE(LocoNetInterface)
|
||||
IF_CLASSID_CREATE(WlanMausInterface)
|
||||
IF_CLASSID_CREATE(XpressNetInterface)
|
||||
return std::shared_ptr<Interface>();
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2021 Reinder Feenstra
|
||||
* Copyright (C) 2021-2022 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -29,6 +29,7 @@
|
||||
#include "dccplusplusinterface.hpp"
|
||||
#include "ecosinterface.hpp"
|
||||
#include "loconetinterface.hpp"
|
||||
#include "wlanmausinterface.hpp"
|
||||
#include "xpressnetinterface.hpp"
|
||||
|
||||
struct Interfaces
|
||||
@ -39,6 +40,7 @@ struct Interfaces
|
||||
DCCPlusPlusInterface::classId,
|
||||
ECoSInterface::classId,
|
||||
LocoNetInterface::classId,
|
||||
WlanMausInterface::classId,
|
||||
XpressNetInterface::classId
|
||||
);
|
||||
|
||||
|
||||
137
server/src/hardware/interface/wlanmausinterface.cpp
Normale Datei
137
server/src/hardware/interface/wlanmausinterface.cpp
Normale Datei
@ -0,0 +1,137 @@
|
||||
/**
|
||||
* server/src/hardware/interface/wlanmausinterface.cpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2022 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 "wlanmausinterface.hpp"
|
||||
#include "../protocol/z21/iohandler/udpserveriohandler.hpp"
|
||||
#include "../../core/attributes.hpp"
|
||||
#include "../../log/log.hpp"
|
||||
#include "../../log/logmessageexception.hpp"
|
||||
#include "../../utils/displayname.hpp"
|
||||
#include "../../world/world.hpp"
|
||||
|
||||
WlanMausInterface::WlanMausInterface(const std::weak_ptr<World>& world, std::string_view _id)
|
||||
: Interface(world, _id)
|
||||
, z21{this, "z21", nullptr, PropertyFlags::ReadOnly | PropertyFlags::Store | PropertyFlags::SubObject}
|
||||
{
|
||||
z21.setValueInternal(std::make_shared<Z21::ServerSettings>(*this, z21.name()));
|
||||
|
||||
Attributes::addDisplayName(z21, DisplayName::Hardware::z21);
|
||||
m_interfaceItems.insertBefore(z21, notes);
|
||||
}
|
||||
|
||||
void WlanMausInterface::worldEvent(WorldState state, WorldEvent event)
|
||||
{
|
||||
Interface::worldEvent(state, event);
|
||||
|
||||
if(m_kernel)
|
||||
{
|
||||
switch(event)
|
||||
{
|
||||
case WorldEvent::PowerOff:
|
||||
case WorldEvent::PowerOn:
|
||||
case WorldEvent::Stop:
|
||||
case WorldEvent::Run:
|
||||
m_kernel->setState(contains(state, WorldState::PowerOn), !contains(state, WorldState::Run));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WlanMausInterface::idChanged(const std::string& newId)
|
||||
{
|
||||
if(m_kernel)
|
||||
m_kernel->setLogId(newId);
|
||||
}
|
||||
|
||||
bool WlanMausInterface::setOnline(bool& value)
|
||||
{
|
||||
if(!m_kernel && value)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto world = m_world.lock();
|
||||
assert(world);
|
||||
m_kernel = Z21::ServerKernel::create<Z21::UDPServerIOHandler>(z21->config(), world->decoders.value());
|
||||
|
||||
status.setValueInternal(InterfaceStatus::Initializing);
|
||||
|
||||
m_kernel->setLogId(id.value());
|
||||
m_kernel->setOnStarted(
|
||||
[this]()
|
||||
{
|
||||
status.setValueInternal(InterfaceStatus::Online);
|
||||
});
|
||||
|
||||
m_kernel->setOnTrackPowerOff(
|
||||
[this]()
|
||||
{
|
||||
if(auto w = m_world.lock(); w && contains(w->state.value(), WorldState::PowerOn))
|
||||
w->powerOff();
|
||||
});
|
||||
m_kernel->setOnTrackPowerOn(
|
||||
[this]()
|
||||
{
|
||||
if(auto w = m_world.lock())
|
||||
{
|
||||
if(!contains(w->state.value(), WorldState::PowerOn))
|
||||
w->powerOn();
|
||||
if(!contains(w->state.value(), WorldState::Run))
|
||||
w->run();
|
||||
}
|
||||
});
|
||||
m_kernel->setOnEmergencyStop(
|
||||
[this]()
|
||||
{
|
||||
if(auto w = m_world.lock(); w && contains(w->state.value(), WorldState::Run))
|
||||
w->stop();
|
||||
});
|
||||
|
||||
m_kernel->start();
|
||||
|
||||
m_z21PropertyChanged = z21->propertyChanged.connect(
|
||||
[this](BaseProperty&)
|
||||
{
|
||||
m_kernel->setConfig(z21->config());
|
||||
});
|
||||
|
||||
if(auto w = m_world.lock())
|
||||
m_kernel->setState(contains(w->state.value(), WorldState::PowerOn), !contains(w->state.value(), WorldState::Run));
|
||||
}
|
||||
catch(const LogMessageException& e)
|
||||
{
|
||||
status.setValueInternal(InterfaceStatus::Offline);
|
||||
Log::log(*this, e.message(), e.args());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if(m_kernel && !value)
|
||||
{
|
||||
m_kernel->stop();
|
||||
m_kernel.reset();
|
||||
|
||||
status.setValueInternal(InterfaceStatus::Offline);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
56
server/src/hardware/interface/wlanmausinterface.hpp
Normale Datei
56
server/src/hardware/interface/wlanmausinterface.hpp
Normale Datei
@ -0,0 +1,56 @@
|
||||
/**
|
||||
* server/src/hardware/interface/wlanmausinterface.hpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2022 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef TRAINTASTIC_SERVER_HARDWARE_INTERFACE_WLANMAUSINTERFACE_HPP
|
||||
#define TRAINTASTIC_SERVER_HARDWARE_INTERFACE_WLANMAUSINTERFACE_HPP
|
||||
|
||||
#include "interface.hpp"
|
||||
#include "../protocol/z21/serverkernel.hpp"
|
||||
#include "../protocol/z21/serversettings.hpp"
|
||||
#include "../../core/objectproperty.hpp"
|
||||
|
||||
/**
|
||||
* @brief WLANmaus/Z21 app hardware interface
|
||||
*/
|
||||
class WlanMausInterface : public Interface
|
||||
{
|
||||
CLASS_ID("interface.wlanmaus")
|
||||
DEFAULT_ID("wlanmaus")
|
||||
CREATE(WlanMausInterface)
|
||||
|
||||
private:
|
||||
std::unique_ptr<Z21::ServerKernel> m_kernel;
|
||||
boost::signals2::connection m_z21PropertyChanged;
|
||||
|
||||
protected:
|
||||
void worldEvent(WorldState state, WorldEvent event) final;
|
||||
void idChanged(const std::string& newId) final;
|
||||
bool setOnline(bool& value) final;
|
||||
|
||||
public:
|
||||
ObjectProperty<Z21::ServerSettings> z21;
|
||||
|
||||
WlanMausInterface(const std::weak_ptr<World>& world, std::string_view _id);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
53
server/src/hardware/protocol/z21/config.hpp
Normale Datei
53
server/src/hardware/protocol/z21/config.hpp
Normale Datei
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* server/src/hardware/protocol/z21/config.hpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2021-2022 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_Z21_CONFIG_HPP
|
||||
#define TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_Z21_CONFIG_HPP
|
||||
|
||||
#include "messages.hpp"
|
||||
|
||||
namespace Z21 {
|
||||
|
||||
struct Config
|
||||
{
|
||||
bool debugLogRXTX;
|
||||
};
|
||||
|
||||
struct ServerConfig : Config
|
||||
{
|
||||
static constexpr CommandStationId commandStationId = CommandStationId::Z21;
|
||||
static constexpr uint8_t firmwareVersionMajor = 1;
|
||||
static constexpr uint8_t firmwareVersionMinor = 30;
|
||||
static constexpr HardwareType hardwareType = HWT_Z21_START;
|
||||
static constexpr uint16_t inactiveClientPurgeTime = 60; ///< sec
|
||||
static constexpr uint32_t serialNumber = 123456789;
|
||||
static constexpr size_t subscriptionMax = 16;
|
||||
static constexpr uint8_t xBusVersion = 30;
|
||||
|
||||
bool allowEmergencyStop;
|
||||
bool allowTrackPowerOff;
|
||||
bool allowTrackPowerOnReleaseEmergencyStop;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
63
server/src/hardware/protocol/z21/iohandler/iohandler.hpp
Normale Datei
63
server/src/hardware/protocol/z21/iohandler/iohandler.hpp
Normale Datei
@ -0,0 +1,63 @@
|
||||
/**
|
||||
* server/src/hardware/protocol/z21/iohandler/iohandler.hpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2021-2022 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_Z21_IOHANDLER_IOHANDLER_HPP
|
||||
#define TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_Z21_IOHANDLER_IOHANDLER_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <limits>
|
||||
|
||||
namespace Z21 {
|
||||
|
||||
class Kernel;
|
||||
struct Message;
|
||||
|
||||
class IOHandler
|
||||
{
|
||||
protected:
|
||||
Kernel& m_kernel;
|
||||
|
||||
IOHandler(Kernel& kernel)
|
||||
: m_kernel{kernel}
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
using ClientId = size_t;
|
||||
|
||||
IOHandler(const IOHandler&) = delete;
|
||||
IOHandler& operator =(const IOHandler&) = delete;
|
||||
|
||||
virtual ~IOHandler() = default;
|
||||
|
||||
virtual void start() = 0;
|
||||
virtual void stop() = 0;
|
||||
|
||||
virtual bool send(const Message& /*message*/) { return false; }
|
||||
virtual bool sendTo(const Message& /*message*/, ClientId /*id*/) { return false; }
|
||||
|
||||
virtual void purgeClient(ClientId /*id*/) {}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
77
server/src/hardware/protocol/z21/iohandler/udpiohandler.cpp
Normale Datei
77
server/src/hardware/protocol/z21/iohandler/udpiohandler.cpp
Normale Datei
@ -0,0 +1,77 @@
|
||||
/**
|
||||
* server/src/hardware/protocol/z21/iohandler/udpiohandler.cpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2021-2022 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 "udpiohandler.hpp"
|
||||
#include "../kernel.hpp"
|
||||
#include "../messages.hpp"
|
||||
#include "../../../../core/eventloop.hpp"
|
||||
#include "../../../../log/log.hpp"
|
||||
#include "../../../../log/logmessageexception.hpp"
|
||||
|
||||
namespace Z21 {
|
||||
|
||||
UDPIOHandler::UDPIOHandler(Kernel& kernel)
|
||||
: IOHandler(kernel)
|
||||
, m_socket{m_kernel.ioContext()}
|
||||
{
|
||||
}
|
||||
|
||||
void UDPIOHandler::start()
|
||||
{
|
||||
receive();
|
||||
}
|
||||
|
||||
void UDPIOHandler::stop()
|
||||
{
|
||||
}
|
||||
|
||||
void UDPIOHandler::receive()
|
||||
{
|
||||
m_socket.async_receive_from(boost::asio::buffer(m_receiveBuffer), m_receiveEndpoint,
|
||||
[this](const boost::system::error_code& ec, std::size_t bytesReceived)
|
||||
{
|
||||
if(!ec)
|
||||
{
|
||||
const std::byte* pos = m_receiveBuffer.data();
|
||||
while(bytesReceived >= sizeof(Message))
|
||||
{
|
||||
const Message& message = *reinterpret_cast<const Message*>(pos);
|
||||
if(message.dataLen() < sizeof(Message))
|
||||
break;
|
||||
receive(message, m_receiveEndpoint);
|
||||
pos += message.dataLen();
|
||||
bytesReceived -= message.dataLen();
|
||||
}
|
||||
receive();
|
||||
}
|
||||
else
|
||||
{
|
||||
EventLoop::call(
|
||||
[this, ec]()
|
||||
{
|
||||
Log::log(m_kernel.logId(), LogMessage::E2009_SOCKET_RECEIVE_FAILED_X, ec);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
58
server/src/hardware/protocol/z21/iohandler/udpiohandler.hpp
Normale Datei
58
server/src/hardware/protocol/z21/iohandler/udpiohandler.hpp
Normale Datei
@ -0,0 +1,58 @@
|
||||
/**
|
||||
* server/src/hardware/protocol/z21/iohandler/udpiohandler.hpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2021-2022 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_Z21_IOHANDLER_UDPIOHANDLER_HPP
|
||||
#define TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_Z21_IOHANDLER_UDPIOHANDLER_HPP
|
||||
|
||||
#include "iohandler.hpp"
|
||||
#include <boost/asio/ip/udp.hpp>
|
||||
|
||||
namespace Z21 {
|
||||
|
||||
class UDPIOHandler : public IOHandler
|
||||
{
|
||||
public:
|
||||
static constexpr size_t payloadSizeMax = 1500 - 20 - 8; ///< Ethernet MTU - IPv4 header - UDP header
|
||||
|
||||
private:
|
||||
boost::asio::ip::udp::endpoint m_receiveEndpoint;
|
||||
std::array<std::byte, payloadSizeMax> m_receiveBuffer;
|
||||
|
||||
void receive();
|
||||
|
||||
protected:
|
||||
boost::asio::ip::udp::socket m_socket;
|
||||
|
||||
virtual void receive(const Message& message, const boost::asio::ip::udp::endpoint& remoteEndpoint) = 0;
|
||||
|
||||
public:
|
||||
static constexpr uint16_t defaultPort = 21105;
|
||||
|
||||
UDPIOHandler(Kernel& kernel);
|
||||
|
||||
void start() override;
|
||||
void stop() override;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
87
server/src/hardware/protocol/z21/iohandler/udpserveriohandler.cpp
Normale Datei
87
server/src/hardware/protocol/z21/iohandler/udpserveriohandler.cpp
Normale Datei
@ -0,0 +1,87 @@
|
||||
/**
|
||||
* server/src/hardware/protocol/z21/iohandler/udpclientiohandler.cpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2021-2022 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 "udpserveriohandler.hpp"
|
||||
//#include <boost/asio/buffer.hpp>
|
||||
#include "../serverkernel.hpp"
|
||||
#include "../messages.hpp"
|
||||
#include "../../../../log/logmessageexception.hpp"
|
||||
|
||||
namespace Z21 {
|
||||
|
||||
UDPServerIOHandler::UDPServerIOHandler(ServerKernel& kernel)
|
||||
: UDPIOHandler(kernel)
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
|
||||
if(m_socket.open(boost::asio::ip::udp::v4(), ec))
|
||||
throw LogMessageException(LogMessage::E2004_SOCKET_OPEN_FAILED_X, ec);
|
||||
|
||||
if(m_socket.bind(boost::asio::ip::udp::endpoint(boost::asio::ip::address_v4::any(), defaultPort), ec))
|
||||
{
|
||||
m_socket.close();
|
||||
throw LogMessageException(LogMessage::E2006_SOCKET_BIND_FAILED_X, ec);
|
||||
}
|
||||
}
|
||||
|
||||
bool UDPServerIOHandler::sendTo(const Message& message, ClientId id)
|
||||
{
|
||||
//! \todo use async_send_to()
|
||||
if(auto it = m_clients.find(id); it != m_clients.end())
|
||||
{
|
||||
m_socket.send_to(boost::asio::buffer(&message, message.dataLen()), it->second);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void UDPServerIOHandler::purgeClient(ClientId id)
|
||||
{
|
||||
m_clients.erase(id);
|
||||
}
|
||||
|
||||
void UDPServerIOHandler::receive(const Message& message, const boost::asio::ip::udp::endpoint& remoteEndpoint)
|
||||
{
|
||||
ClientId clientId;
|
||||
|
||||
if(auto it = std::find_if(m_clients.begin(), m_clients.end(), [&remoteEndpoint](auto n) { return n.second == remoteEndpoint; }); it != m_clients.end())
|
||||
{
|
||||
clientId = it->first;
|
||||
}
|
||||
else // new client
|
||||
{
|
||||
do
|
||||
{
|
||||
m_lastClientId++;
|
||||
}
|
||||
while(m_clients.find(m_lastClientId) != m_clients.end());
|
||||
|
||||
m_clients.emplace(m_lastClientId, remoteEndpoint);
|
||||
|
||||
clientId = m_lastClientId;
|
||||
}
|
||||
|
||||
static_cast<ServerKernel&>(m_kernel).receiveFrom(message, clientId);
|
||||
}
|
||||
|
||||
}
|
||||
52
server/src/hardware/protocol/z21/iohandler/udpserveriohandler.hpp
Normale Datei
52
server/src/hardware/protocol/z21/iohandler/udpserveriohandler.hpp
Normale Datei
@ -0,0 +1,52 @@
|
||||
/**
|
||||
* server/src/hardware/protocol/z21/iohandler/udpserveriohandler.hpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2021-2022 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_Z21_IOHANDLER_UDPSERVERIOHANDLER_HPP
|
||||
#define TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_Z21_IOHANDLER_UDPSERVERIOHANDLER_HPP
|
||||
|
||||
#include "udpiohandler.hpp"
|
||||
#include <unordered_map>
|
||||
|
||||
namespace Z21 {
|
||||
|
||||
class ServerKernel;
|
||||
|
||||
class UDPServerIOHandler final : public UDPIOHandler
|
||||
{
|
||||
private:
|
||||
ClientId m_lastClientId = 0;
|
||||
std::unordered_map<ClientId, boost::asio::ip::udp::endpoint> m_clients;
|
||||
|
||||
protected:
|
||||
void receive(const Message& message, const boost::asio::ip::udp::endpoint& remoteEndpoint) final;
|
||||
|
||||
public:
|
||||
UDPServerIOHandler(ServerKernel& kernel);
|
||||
|
||||
bool sendTo(const Message& message, ClientId id) final;
|
||||
|
||||
void purgeClient(ClientId id) final;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
102
server/src/hardware/protocol/z21/kernel.cpp
Normale Datei
102
server/src/hardware/protocol/z21/kernel.cpp
Normale Datei
@ -0,0 +1,102 @@
|
||||
/**
|
||||
* server/src/hardware/protocol/z21/kernel.cpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2022 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 "kernel.hpp"
|
||||
#include "messages.hpp"
|
||||
#include "../xpressnet/messages.hpp"
|
||||
#include "../../decoder/decoder.hpp"
|
||||
#include "../../decoder/decoderchangeflags.hpp"
|
||||
#include "../../input/inputcontroller.hpp"
|
||||
#include "../../../utils/setthreadname.hpp"
|
||||
#include "../../../core/eventloop.hpp"
|
||||
#include "../../../log/log.hpp"
|
||||
|
||||
namespace Z21 {
|
||||
|
||||
Kernel::Kernel()
|
||||
: m_ioContext{1}
|
||||
#ifndef NDEBUG
|
||||
, m_started{false}
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
void Kernel::start()
|
||||
{
|
||||
assert(m_ioHandler);
|
||||
assert(!m_started);
|
||||
|
||||
m_thread = std::thread(
|
||||
[this]()
|
||||
{
|
||||
setThreadName("z21");
|
||||
auto work = std::make_shared<boost::asio::io_context::work>(m_ioContext);
|
||||
m_ioContext.run();
|
||||
});
|
||||
|
||||
m_ioContext.post(
|
||||
[this]()
|
||||
{
|
||||
m_ioHandler->start();
|
||||
|
||||
onStart();
|
||||
|
||||
if(m_onStarted)
|
||||
EventLoop::call(
|
||||
[this]()
|
||||
{
|
||||
m_onStarted();
|
||||
});
|
||||
});
|
||||
|
||||
#ifndef NDEBUG
|
||||
m_started = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void Kernel::stop()
|
||||
{
|
||||
m_ioContext.post(
|
||||
[this]()
|
||||
{
|
||||
onStop();
|
||||
|
||||
m_ioHandler->stop();
|
||||
});
|
||||
|
||||
m_ioContext.stop();
|
||||
|
||||
m_thread.join();
|
||||
|
||||
#ifndef NDEBUG
|
||||
m_started = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void Kernel::setIOHandler(std::unique_ptr<IOHandler> handler)
|
||||
{
|
||||
assert(handler);
|
||||
assert(!m_ioHandler);
|
||||
m_ioHandler = std::move(handler);
|
||||
}
|
||||
|
||||
}
|
||||
118
server/src/hardware/protocol/z21/kernel.hpp
Normale Datei
118
server/src/hardware/protocol/z21/kernel.hpp
Normale Datei
@ -0,0 +1,118 @@
|
||||
/**
|
||||
* server/src/hardware/protocol/z21/kernel.hpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2022 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_Z21_KERNEL_HPP
|
||||
#define TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_Z21_KERNEL_HPP
|
||||
|
||||
#include <array>
|
||||
#include <thread>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
|
||||
#include <traintastic/enum/tristate.hpp>
|
||||
#include "config.hpp"
|
||||
#include "iohandler/iohandler.hpp"
|
||||
|
||||
class Decoder;
|
||||
enum class DecoderChangeFlags;
|
||||
class DecoderController;
|
||||
|
||||
namespace Z21 {
|
||||
|
||||
struct Message;
|
||||
enum HardwareType : uint32_t;
|
||||
|
||||
class Kernel
|
||||
{
|
||||
private:
|
||||
std::thread m_thread;
|
||||
std::function<void()> m_onStarted;
|
||||
|
||||
protected:
|
||||
boost::asio::io_context m_ioContext;
|
||||
std::unique_ptr<IOHandler> m_ioHandler;
|
||||
std::string m_logId;
|
||||
#ifndef NDEBUG
|
||||
bool m_started;
|
||||
#endif
|
||||
|
||||
Kernel();
|
||||
virtual ~Kernel() = default;
|
||||
|
||||
void setIOHandler(std::unique_ptr<IOHandler> handler);
|
||||
|
||||
virtual void onStart() {}
|
||||
virtual void onStop() {}
|
||||
|
||||
public:
|
||||
Kernel(const Kernel&) = delete;
|
||||
Kernel& operator =(const Kernel&) = delete;
|
||||
|
||||
/**
|
||||
* @brief IO context for Z21 kernel and IO handler
|
||||
*
|
||||
* @return The IO context
|
||||
*/
|
||||
boost::asio::io_context& ioContext() { return m_ioContext; }
|
||||
|
||||
/**
|
||||
* @brief Get object id used for log messages
|
||||
* @return The object id
|
||||
*/
|
||||
inline const std::string& logId()
|
||||
{
|
||||
return m_logId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set object id used for log messages
|
||||
* @param[in] value The object id
|
||||
*/
|
||||
inline void setLogId(std::string value)
|
||||
{
|
||||
m_logId = std::move(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief ...
|
||||
* @param[in] callback ...
|
||||
* @note This function may not be called when the kernel is running.
|
||||
*/
|
||||
inline void setOnStarted(std::function<void()> callback)
|
||||
{
|
||||
assert(!m_started);
|
||||
m_onStarted = std::move(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Start the kernel and IO handler
|
||||
*/
|
||||
virtual void start();
|
||||
|
||||
/**
|
||||
* @brief Stop the kernel and IO handler
|
||||
*/
|
||||
virtual void stop();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2020 Reinder Feenstra
|
||||
* Copyright (C) 2019-2022 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -31,6 +31,7 @@ static std::string_view toString(Header header)
|
||||
switch(header)
|
||||
{
|
||||
case LAN_GET_SERIAL_NUMBER: return "LAN_GET_SERIAL_NUMBER";
|
||||
case LAN_GET_CODE: return "LAN_GET_CODE";
|
||||
case LAN_GET_HWINFO: return "LAN_GET_HWINFO";
|
||||
case LAN_LOGOFF: return "LAN_LOGOFF";
|
||||
case LAN_X: return "LAN_X";
|
||||
@ -38,10 +39,21 @@ static std::string_view toString(Header header)
|
||||
case LAN_GET_BROADCASTFLAGS: return "LAN_GET_BROADCASTFLAGS";
|
||||
case LAN_GET_LOCO_MODE: return "LAN_GET_LOCO_MODE";
|
||||
case LAN_SET_LOCO_MODE: return "LAN_SET_LOCO_MODE";
|
||||
case LAN_GET_TURNOUTMODE: return "LAN_GET_TURNOUTMODE";
|
||||
case LAN_SET_TURNOUTMODE: return "LAN_SET_TURNOUTMODE";
|
||||
case LAN_RMBUS_DATACHANGED: return "LAN_RMBUS_DATACHANGED";
|
||||
case LAN_RMBUS_GETDATA: return "LAN_RMBUS_GETDATA";
|
||||
case LAN_RMBUS_PROGRAMMODULE: return "LAN_RMBUS_PROGRAMMODULE";
|
||||
case LAN_SYSTEMSTATE_DATACHANGED: return "LAN_SYSTEMSTATE_DATACHANGED";
|
||||
case LAN_SYSTEMSTATE_GETDATA: return "LAN_SYSTEMSTATE_GETDATA";
|
||||
case LAN_RAILCOM_DATACHANGED: return "LAN_RAILCOM_DATACHANGED";
|
||||
case LAN_RAILCOM_GETDATA: return "LAN_RAILCOM_GETDATA";
|
||||
case LAN_LOCONET_Z21_RX: return "LAN_LOCONET_Z21_RX";
|
||||
case LAN_LOCONET_Z21_TX: return "LAN_LOCONET_Z21_TX";
|
||||
case LAN_LOCONET_FROM_LAN: return "LAN_LOCONET_FROM_LAN";
|
||||
case LAN_LOCONET_DISPATCH_ADDR: return "LAN_LOCONET_DISPATCH_ADDR";
|
||||
case LAN_LOCONET_DETECTOR: return "LAN_LOCONET_DETECTOR";
|
||||
case LAN_CAN_DETECTOR: return "LAN_CAN_DETECTOR";
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@ -84,28 +96,119 @@ std::string toString(const Message& message, bool raw)
|
||||
raw = true;
|
||||
break;
|
||||
|
||||
case 0xE3:
|
||||
if(const auto& getLocoInfo = static_cast<const LanXGetLocoInfo&>(message); getLocoInfo.db0 == 0xF0)
|
||||
{
|
||||
s = "LAN_X_GET_LOCO_INFO";
|
||||
s.append(" address=").append(std::to_string(getLocoInfo.address()));
|
||||
if(getLocoInfo.isLongAddress())
|
||||
s.append(" (long)");
|
||||
}
|
||||
else
|
||||
raw = true;
|
||||
break;
|
||||
|
||||
case 0xE4:
|
||||
if(const auto& setLocoDrive = static_cast<const LanXSetLocoDrive&>(message);
|
||||
setLocoDrive.db0 >= 0x10 && setLocoDrive.db0 <= 0x13)
|
||||
{
|
||||
s = "LAN_X_SET_LOCO_DRIVE";
|
||||
s.append(" address=").append(std::to_string(setLocoDrive.address()));
|
||||
if(setLocoDrive.isLongAddress())
|
||||
s.append("/long");
|
||||
s.append(" direction=").append(setLocoDrive.direction() == Direction::Forward ? "fwd" : "rev");
|
||||
s.append(" speed=");
|
||||
if(setLocoDrive.isEmergencyStop())
|
||||
s.append("estop");
|
||||
else
|
||||
s.append(std::to_string(setLocoDrive.speedStep())).append("/").append(std::to_string(setLocoDrive.speedSteps()));
|
||||
}
|
||||
else if(const auto& setLocoFunction = static_cast<const LanXSetLocoFunction&>(message);
|
||||
setLocoFunction.db0 == 0xF8)
|
||||
{
|
||||
s = "LAN_X_SET_LOCO_FUNCTION";
|
||||
s.append(" address=").append(std::to_string(setLocoFunction.address()));
|
||||
if(setLocoFunction.isLongAddress())
|
||||
s.append("/long");
|
||||
s.append(" function=").append(std::to_string(setLocoFunction.functionIndex()));
|
||||
s.append(" state=").append(toString(setLocoFunction.switchType()));
|
||||
}
|
||||
else
|
||||
raw = true;
|
||||
break;
|
||||
|
||||
case 0xEF:
|
||||
{
|
||||
const auto& locoInfo = static_cast<const LanXLocoInfo&>(message);
|
||||
s = "LAN_X_LOCO_INFO";
|
||||
s.append(" address=").append(std::to_string(locoInfo.address()));
|
||||
if(locoInfo.isLongAddress())
|
||||
s.append("/long");
|
||||
s.append(" direction=").append(locoInfo.direction() == Direction::Forward ? "fwd" : "rev");
|
||||
s.append(" speed=");
|
||||
if(locoInfo.isEmergencyStop())
|
||||
s.append("estop");
|
||||
else
|
||||
s.append(std::to_string(locoInfo.speedStep())).append("/").append(std::to_string(locoInfo.speedSteps()));
|
||||
for(uint8_t i = 0; i <= LanXLocoInfo::functionIndexMax; i++)
|
||||
s.append(" f").append(std::to_string(i)).append("=").append(locoInfo.getFunction(i) ? "1" : "0");
|
||||
s.append(" busy=").append(locoInfo.isBusy() ? "1" : "0");
|
||||
break;
|
||||
}
|
||||
case 0xF1:
|
||||
if(message == LanXGetFirmwareVersion())
|
||||
s = "LAN_X_GET_FIRMWARE_VERSION";
|
||||
else
|
||||
raw = true;
|
||||
break;
|
||||
|
||||
case 0xF3:
|
||||
if(message.dataLen() == sizeof(LanXGetFirmwareVersionReply))
|
||||
{
|
||||
const auto& getFirmwareVersion = static_cast<const LanXGetFirmwareVersionReply&>(message);
|
||||
s = "LAN_X_GET_FIRMWARE_VERSION";
|
||||
s.append(" version=").append(std::to_string(getFirmwareVersion.versionMajor())).append(".").append(std::to_string(getFirmwareVersion.versionMinor()));
|
||||
}
|
||||
else
|
||||
raw = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
raw = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case Z21::LAN_GET_BROADCASTFLAGS:
|
||||
if(message.dataLen() == sizeof(Z21::LanGetBroadcastFlags))
|
||||
case LAN_GET_BROADCASTFLAGS:
|
||||
if(message == LanGetBroadcastFlags())
|
||||
s = "LAN_GET_BROADCASTFLAGS";
|
||||
//else if(message.dataLen() == sizeof(Z21::LanGetBroadcastFlagsReply))
|
||||
// s = "LAN_GET_BROADCASTFLAGS flags=0x" + toHex(static_cast<const LanGetBroadcastFlagsReply&>(message).broadcastFlags()));
|
||||
else
|
||||
raw = true;
|
||||
break;
|
||||
|
||||
case Z21::LAN_SET_BROADCASTFLAGS:
|
||||
case LAN_SET_BROADCASTFLAGS:
|
||||
if(message.dataLen() == sizeof(Z21::LanSetBroadcastFlags))
|
||||
s = "LAN_SET_BROADCASTFLAGS flags=0x" + toHex(static_cast<const LanSetBroadcastFlags&>(message).broadcastFlags());
|
||||
{
|
||||
s = "LAN_SET_BROADCASTFLAGS";
|
||||
s.append(" flags=0x").append(toHex(static_cast<std::underlying_type_t<BroadcastFlags>>(static_cast<const LanSetBroadcastFlags&>(message).broadcastFlags())));
|
||||
}
|
||||
else
|
||||
raw = true;
|
||||
break;
|
||||
|
||||
case LAN_SYSTEMSTATE_DATACHANGED:
|
||||
{
|
||||
const auto& systemState = static_cast<const LanSystemStateDataChanged&>(message);
|
||||
s.append(" mainCurrent=").append(std::to_string(systemState.mainCurrent));
|
||||
s.append(" progCurrent=").append(std::to_string(systemState.progCurrent));
|
||||
s.append(" filteredMainCurrent=").append(std::to_string(systemState.filteredMainCurrent));
|
||||
s.append(" temperature=").append(std::to_string(systemState.temperature));
|
||||
s.append(" supplyVoltage=").append(std::to_string(systemState.supplyVoltage));
|
||||
s.append(" vccVoltage=").append(std::to_string(systemState.vccVoltage));
|
||||
s.append(" centralState=0x").append(toHex(systemState.centralState));
|
||||
s.append(" centralStateEx=0x").append(toHex(systemState.centralStateEx));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
raw = true;
|
||||
break;
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2020 Reinder Feenstra
|
||||
* Copyright (C) 2019-2022 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -67,7 +67,7 @@ enum Header : uint16_t
|
||||
LAN_CAN_DETECTOR = 0xC4,
|
||||
};
|
||||
|
||||
enum BroadcastFlags : uint32_t
|
||||
enum class BroadcastFlags : uint32_t
|
||||
{
|
||||
None = 0,
|
||||
|
||||
@ -157,6 +157,7 @@ static constexpr uint8_t LAN_X_LOCO_INFO = 0xEF;
|
||||
|
||||
enum HardwareType : uint32_t
|
||||
{
|
||||
HWT_UNKNOWN = 0,
|
||||
HWT_Z21_OLD = 0x00000200, //!< „black Z21” (hardware variant from 2012)
|
||||
HWT_Z21_NEW = 0x00000201, //!< „black Z21”(hardware variant from 2013)
|
||||
HWT_SMARTRAIL = 0x00000202, //!< SmartRail (from 2012)
|
||||
@ -164,6 +165,37 @@ enum HardwareType : uint32_t
|
||||
HWT_Z21_START = 0x00000204, //!< „z21 start” starter set variant (from 2016)
|
||||
};
|
||||
|
||||
constexpr std::string_view toString(HardwareType value)
|
||||
{
|
||||
switch(value)
|
||||
{
|
||||
case HWT_Z21_OLD:
|
||||
return "Black Z21 (hardware variant from 2012)";
|
||||
|
||||
case HWT_Z21_NEW:
|
||||
return "Black Z21 (hardware variant from 2013)";
|
||||
|
||||
case HWT_SMARTRAIL:
|
||||
return "SmartRail (from 2012)";
|
||||
|
||||
case HWT_Z21_SMALL:
|
||||
return "White Z21 (starter set variant from 2013)";
|
||||
|
||||
case HWT_Z21_START :
|
||||
return "Z21 start (starter set variant from 2016)";
|
||||
|
||||
case HWT_UNKNOWN:
|
||||
break;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
enum class CommandStationId : uint8_t
|
||||
{
|
||||
Z21 = 0x12,
|
||||
};
|
||||
|
||||
#define Z21_CENTRALSTATE_EMERGENCYSTOP 0x01 //!< The emergency stop is switched on
|
||||
#define Z21_CENTRALSTATE_TRACKVOLTAGEOFF 0x02 //!< The track voltage is switched off
|
||||
#define Z21_CENTRALSTATE_SHORTCIRCUIT 0x04 //!< Short circuit
|
||||
@ -260,6 +292,19 @@ struct LanLogoff : Message
|
||||
static_assert(sizeof(LanLogoff) == 4);
|
||||
|
||||
// LAN_X_GET_VERSION
|
||||
struct LanXGetVersion : LanX
|
||||
{
|
||||
uint8_t db0 = 0x21;
|
||||
uint8_t checksum = 0x00;
|
||||
|
||||
LanXGetVersion() :
|
||||
LanX(sizeof(LanXGetVersion), 0x21)
|
||||
{
|
||||
}
|
||||
} ATTRIBUTE_PACKED;
|
||||
static_assert(sizeof(LanXGetVersion) == 7);
|
||||
|
||||
// LAN_X_GET_FIRMWARE_VERSION
|
||||
struct LanXGetFirmwareVersion : LanX
|
||||
{
|
||||
uint8_t db0 = 0x0A;
|
||||
@ -378,9 +423,6 @@ static_assert(sizeof(LanXGetLocoInfo) == 9);
|
||||
// LAN_X_SET_LOCO_DRIVE
|
||||
struct LanXSetLocoDrive : LanX
|
||||
{
|
||||
//static constexpr uint8_t directionFlag = 0x80;
|
||||
|
||||
//uint8_t xheader = 0xe4;
|
||||
uint8_t db0;
|
||||
uint8_t addressHigh;
|
||||
uint8_t addressLow;
|
||||
@ -402,6 +444,12 @@ struct LanXSetLocoDrive : LanX
|
||||
return (addressHigh & 0xC0) == 0xC0;
|
||||
}
|
||||
|
||||
inline void setAddress(uint16_t address, bool longAddress)
|
||||
{
|
||||
addressHigh = longAddress ? (0xC0 | (address >> 8)) : 0x00;
|
||||
addressLow = longAddress ? address & 0xFF : address & 0x7F;
|
||||
}
|
||||
|
||||
inline uint8_t speedSteps() const
|
||||
{
|
||||
switch(db0 & 0x0F)
|
||||
@ -477,6 +525,12 @@ struct LanXSetLocoFunction : LanX
|
||||
return (addressHigh & 0xC0) == 0xC0;
|
||||
}
|
||||
|
||||
inline void setAddress(uint16_t address, bool longAddress)
|
||||
{
|
||||
addressHigh = longAddress ? (0xC0 | (address >> 8)) : 0x00;
|
||||
addressLow = longAddress ? address & 0xFF : address & 0x7F;
|
||||
}
|
||||
|
||||
inline SwitchType switchType() const
|
||||
{
|
||||
return static_cast<SwitchType>(db3 >> 6);
|
||||
@ -633,6 +687,40 @@ struct LanGetSerialNumberReply : Message
|
||||
} ATTRIBUTE_PACKED;
|
||||
static_assert(sizeof(LanGetSerialNumberReply) == 8);
|
||||
|
||||
// Reply to LAN_X_GET_VERSION
|
||||
struct LanXGetVersionReply : LanX
|
||||
{
|
||||
uint8_t db0 = 0x21;
|
||||
uint8_t xBusVersionBCD;
|
||||
CommandStationId commandStationId;
|
||||
uint8_t checksum;
|
||||
|
||||
LanXGetVersionReply()
|
||||
: LanX(sizeof(LanXGetVersionReply), 0x63)
|
||||
{
|
||||
}
|
||||
|
||||
LanXGetVersionReply(uint8_t _xBusVersion, CommandStationId _commandStationId)
|
||||
: LanXGetVersionReply()
|
||||
{
|
||||
setXBusVersion(_xBusVersion);
|
||||
commandStationId = _commandStationId;
|
||||
calcChecksum();
|
||||
}
|
||||
|
||||
inline uint8_t xBusVersion() const
|
||||
{
|
||||
return Utils::fromBCD(xBusVersionBCD);
|
||||
}
|
||||
|
||||
inline void setXBusVersion(uint8_t value)
|
||||
{
|
||||
assert(value < 100);
|
||||
xBusVersionBCD = Utils::toBCD(value);
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(LanXGetVersionReply) == 9);
|
||||
|
||||
// Reply to LAN_GET_CODE
|
||||
struct LanXGetFirmwareVersionReply : LanX
|
||||
{
|
||||
@ -788,6 +876,7 @@ struct LanXLocoInfo : LanX
|
||||
static constexpr uint8_t directionFlag = 0x80;
|
||||
static constexpr uint8_t speedStepMask = 0x7F;
|
||||
static constexpr uint8_t flagF0 = 0x10;
|
||||
static constexpr uint8_t functionIndexMax = 28;
|
||||
|
||||
uint8_t addressHigh = 0;
|
||||
uint8_t addressLow = 0;
|
||||
@ -889,7 +978,7 @@ struct LanXLocoInfo : LanX
|
||||
Z21::Utils::setSpeedStep(speedAndDirection, speedSteps(), value);
|
||||
}
|
||||
|
||||
bool getFunction(uint8_t index)
|
||||
bool getFunction(uint8_t index) const
|
||||
{
|
||||
if(index == 0)
|
||||
return db4 & flagF0;
|
||||
@ -1029,6 +1118,22 @@ static_assert(sizeof(LanSystemStateDataChanged) == 20);
|
||||
|
||||
PRAGMA_PACK_POP
|
||||
|
||||
constexpr std::string_view toString(LanXSetLocoFunction::SwitchType value)
|
||||
{
|
||||
switch(value)
|
||||
{
|
||||
case LanXSetLocoFunction::SwitchType::Off:
|
||||
return "off";
|
||||
case LanXSetLocoFunction::SwitchType::On:
|
||||
return "on";
|
||||
case LanXSetLocoFunction::SwitchType::Toggle:
|
||||
return "toggle";
|
||||
case LanXSetLocoFunction::SwitchType::Invalid:
|
||||
return "invalid";
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
inline bool operator ==(const Z21::Message& lhs, const Z21::Message& rhs)
|
||||
@ -1043,4 +1148,11 @@ constexpr Z21::BroadcastFlags operator |(Z21::BroadcastFlags lhs, Z21::Broadcast
|
||||
static_cast<std::underlying_type_t<Z21::BroadcastFlags>>(rhs));
|
||||
}
|
||||
|
||||
constexpr Z21::BroadcastFlags operator &(Z21::BroadcastFlags lhs, Z21::BroadcastFlags rhs)
|
||||
{
|
||||
return static_cast<Z21::BroadcastFlags>(
|
||||
static_cast<std::underlying_type_t<Z21::BroadcastFlags>>(lhs) &
|
||||
static_cast<std::underlying_type_t<Z21::BroadcastFlags>>(rhs));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
439
server/src/hardware/protocol/z21/serverkernel.cpp
Normale Datei
439
server/src/hardware/protocol/z21/serverkernel.cpp
Normale Datei
@ -0,0 +1,439 @@
|
||||
/**
|
||||
* server/src/hardware/protocol/z21/serverkernel.cpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2022 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 "serverkernel.hpp"
|
||||
#include "messages.hpp"
|
||||
#include "../xpressnet/messages.hpp"
|
||||
#include "../../decoder/decoderlist.hpp"
|
||||
#include "../../../core/eventloop.hpp"
|
||||
#include "../../../log/log.hpp"
|
||||
|
||||
namespace Z21 {
|
||||
|
||||
ServerKernel::ServerKernel(const ServerConfig& config, std::shared_ptr<DecoderList> decoderList)
|
||||
: Kernel()
|
||||
, m_inactiveClientPurgeTimer{m_ioContext}
|
||||
, m_config{config}
|
||||
, m_decoderList{std::move(decoderList)}
|
||||
{
|
||||
}
|
||||
|
||||
void ServerKernel::setConfig(const ServerConfig& config)
|
||||
{
|
||||
m_ioContext.post(
|
||||
[this, newConfig=config]()
|
||||
{
|
||||
m_config = newConfig;
|
||||
});
|
||||
}
|
||||
|
||||
void ServerKernel::setState(bool trackPowerOn, bool emergencyStop)
|
||||
{
|
||||
m_ioContext.post(
|
||||
[this, trackPowerOn, emergencyStop]()
|
||||
{
|
||||
const auto trackPowerOnTri = toTriState(trackPowerOn);
|
||||
const auto emergencyStopTri = toTriState(emergencyStop);
|
||||
|
||||
const bool trackPowerOnChanged = m_trackPowerOn != trackPowerOnTri;
|
||||
const bool emergencyStopChanged = m_emergencyStop != emergencyStopTri;
|
||||
|
||||
m_trackPowerOn = trackPowerOnTri;
|
||||
m_emergencyStop = emergencyStopTri;
|
||||
|
||||
if(emergencyStopChanged && m_emergencyStop == TriState::True)
|
||||
sendTo(LanXBCStopped(), BroadcastFlags::PowerLocoTurnoutChanges);
|
||||
|
||||
if(trackPowerOnChanged && m_trackPowerOn == TriState::False)
|
||||
sendTo(LanXBCTrackPowerOff(), BroadcastFlags::PowerLocoTurnoutChanges);
|
||||
|
||||
if((trackPowerOnChanged || emergencyStopChanged) && m_trackPowerOn == TriState::True && m_emergencyStop == TriState::False)
|
||||
sendTo(LanXBCTrackPowerOn(), BroadcastFlags::PowerLocoTurnoutChanges);
|
||||
|
||||
if(trackPowerOnChanged || emergencyStopChanged)
|
||||
sendTo(getLanSystemStateDataChanged(), BroadcastFlags::PowerLocoTurnoutChanges);
|
||||
});
|
||||
}
|
||||
|
||||
void ServerKernel::receiveFrom(const Message& message, IOHandler::ClientId clientId)
|
||||
{
|
||||
if(m_config.debugLogRXTX)
|
||||
EventLoop::call(
|
||||
[this, clientId, msg=toString(message)]()
|
||||
{
|
||||
Log::log(m_logId, LogMessage::D2005_X_RX_X, clientId, msg);
|
||||
});
|
||||
|
||||
m_clients[clientId].lastSeen = std::chrono::steady_clock::now();
|
||||
|
||||
switch(message.header())
|
||||
{
|
||||
case LAN_X:
|
||||
{
|
||||
const auto& lanX = static_cast<const LanX&>(message);
|
||||
|
||||
if(!XpressNet::isChecksumValid(*reinterpret_cast<const XpressNet::Message*>(&lanX.xheader)))
|
||||
break;
|
||||
|
||||
switch(lanX.xheader)
|
||||
{
|
||||
case 0x21:
|
||||
if(message == LanXGetVersion())
|
||||
{
|
||||
sendTo(LanXGetVersionReply(ServerConfig::xBusVersion, ServerConfig::commandStationId), clientId);
|
||||
}
|
||||
else if(message == LanXGetStatus())
|
||||
{
|
||||
LanXStatusChanged response;
|
||||
if(m_emergencyStop != TriState::False)
|
||||
response.db1 |= Z21_CENTRALSTATE_EMERGENCYSTOP;
|
||||
if(m_trackPowerOn != TriState::True)
|
||||
response.db1 |= Z21_CENTRALSTATE_TRACKVOLTAGEOFF;
|
||||
response.calcChecksum();
|
||||
sendTo(response, clientId);
|
||||
}
|
||||
else if(message == LanXSetTrackPowerOn())
|
||||
{
|
||||
if(m_config.allowTrackPowerOnReleaseEmergencyStop && (m_trackPowerOn != TriState::True || m_emergencyStop != TriState::False) && m_onTrackPowerOn)
|
||||
{
|
||||
EventLoop::call(
|
||||
[this]()
|
||||
{
|
||||
m_onTrackPowerOn();
|
||||
});
|
||||
}
|
||||
}
|
||||
else if(message == LanXSetTrackPowerOff())
|
||||
{
|
||||
if(m_config.allowTrackPowerOff && m_trackPowerOn != TriState::False && m_onTrackPowerOff)
|
||||
{
|
||||
EventLoop::call(
|
||||
[this]()
|
||||
{
|
||||
m_onTrackPowerOff();
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x80:
|
||||
if(message == LanXSetStop())
|
||||
{
|
||||
if(m_config.allowEmergencyStop && m_emergencyStop != TriState::True && m_onEmergencyStop)
|
||||
{
|
||||
EventLoop::call(
|
||||
[this]()
|
||||
{
|
||||
m_onEmergencyStop();
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xE3:
|
||||
if(const auto& getLocoInfo = static_cast<const LanXGetLocoInfo&>(message);
|
||||
getLocoInfo.db0 == 0xF0)
|
||||
{
|
||||
subscribe(clientId, getLocoInfo.address(), getLocoInfo.isLongAddress());
|
||||
|
||||
EventLoop::call(
|
||||
[this, getLocoInfo, clientId]()
|
||||
{
|
||||
if(auto decoder = getDecoder(getLocoInfo.address(), getLocoInfo.isLongAddress()))
|
||||
postSendTo(LanXLocoInfo(*decoder), clientId);
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xE4:
|
||||
if(const auto& setLocoDrive = static_cast<const LanXSetLocoDrive&>(message);
|
||||
setLocoDrive.db0 >= 0x10 && setLocoDrive.db0 <= 0x13)
|
||||
{
|
||||
subscribe(clientId, setLocoDrive.address(), setLocoDrive.isLongAddress());
|
||||
|
||||
EventLoop::call(
|
||||
[this, setLocoDrive]()
|
||||
{
|
||||
if(auto decoder = getDecoder(setLocoDrive.address(), setLocoDrive.isLongAddress()))
|
||||
{
|
||||
decoder->direction = setLocoDrive.direction();
|
||||
decoder->emergencyStop = setLocoDrive.isEmergencyStop();
|
||||
decoder->throttle = Decoder::speedStepToThrottle(setLocoDrive.speedStep(), setLocoDrive.speedSteps());
|
||||
}
|
||||
//else
|
||||
// Log::log(*this, LogMessage::I2001_UNKNOWN_LOCO_ADDRESS_X, setLocoDrive.address());
|
||||
});
|
||||
}
|
||||
else if(const auto& setLocoFunction = static_cast<const LanXSetLocoFunction&>(message);
|
||||
setLocoFunction.db0 == 0xF8 &&
|
||||
setLocoFunction.switchType() != LanXSetLocoFunction::SwitchType::Invalid)
|
||||
{
|
||||
subscribe(clientId, setLocoFunction.address(), setLocoFunction.isLongAddress());
|
||||
|
||||
EventLoop::call(
|
||||
[this, setLocoFunction]()
|
||||
{
|
||||
if(auto decoder = getDecoder(setLocoFunction.address(), setLocoFunction.isLongAddress()))
|
||||
{
|
||||
if(auto function = decoder->getFunction(setLocoFunction.functionIndex()))
|
||||
{
|
||||
switch(setLocoFunction.switchType())
|
||||
{
|
||||
case LanXSetLocoFunction::SwitchType::Off:
|
||||
function->value = false;
|
||||
break;
|
||||
|
||||
case LanXSetLocoFunction::SwitchType::On:
|
||||
function->value = true;
|
||||
break;
|
||||
|
||||
case LanXSetLocoFunction::SwitchType::Toggle:
|
||||
function->value = !function->value;
|
||||
break;
|
||||
|
||||
case LanXSetLocoFunction::SwitchType::Invalid:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xF1:
|
||||
if(message == LanXGetFirmwareVersion())
|
||||
sendTo(LanXGetFirmwareVersionReply(ServerConfig::firmwareVersionMajor, ServerConfig::firmwareVersionMinor), clientId);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LAN_GET_LOCO_MODE:
|
||||
if(message.dataLen() == sizeof(LanGetLocoMode))
|
||||
sendTo(LanGetLocoModeReply(static_cast<const LanGetLocoMode&>(message).address(), LocoMode::DCC), clientId);
|
||||
break;
|
||||
|
||||
case LAN_SET_LOCO_MODE:
|
||||
// ignore, we always report DCC
|
||||
break;
|
||||
|
||||
case LAN_GET_SERIAL_NUMBER:
|
||||
if(message.dataLen() == sizeof(LanGetSerialNumber))
|
||||
sendTo(LanGetSerialNumberReply(ServerConfig::serialNumber), clientId);
|
||||
break;
|
||||
|
||||
case LAN_GET_HWINFO:
|
||||
if(message.dataLen() == sizeof(LanGetHardwareInfo))
|
||||
sendTo(LanGetHardwareInfoReply(ServerConfig::hardwareType, ServerConfig::firmwareVersionMajor, ServerConfig::firmwareVersionMinor), clientId);
|
||||
break;
|
||||
|
||||
case LAN_GET_BROADCASTFLAGS:
|
||||
if(message == LanGetBroadcastFlags())
|
||||
sendTo(LanSetBroadcastFlags(m_clients[clientId].broadcastFlags), clientId);
|
||||
break;
|
||||
|
||||
case LAN_SET_BROADCASTFLAGS:
|
||||
if(message.dataLen() == sizeof(LanSetBroadcastFlags))
|
||||
m_clients[clientId].broadcastFlags = static_cast<const LanSetBroadcastFlags&>(message).broadcastFlags();
|
||||
break;
|
||||
|
||||
case LAN_SYSTEMSTATE_GETDATA:
|
||||
if(message == LanSystemStateGetData())
|
||||
sendTo(getLanSystemStateDataChanged(), clientId);
|
||||
break;
|
||||
|
||||
case LAN_LOGOFF:
|
||||
if(message == LanLogoff())
|
||||
m_clients.erase(clientId);
|
||||
break;
|
||||
|
||||
case LAN_GET_CODE:
|
||||
case LAN_GET_TURNOUTMODE:
|
||||
case LAN_SET_TURNOUTMODE:
|
||||
case LAN_RMBUS_DATACHANGED:
|
||||
case LAN_RMBUS_GETDATA:
|
||||
case LAN_RMBUS_PROGRAMMODULE:
|
||||
case LAN_SYSTEMSTATE_DATACHANGED:
|
||||
case LAN_RAILCOM_DATACHANGED:
|
||||
case LAN_RAILCOM_GETDATA:
|
||||
case LAN_LOCONET_Z21_RX:
|
||||
case LAN_LOCONET_Z21_TX:
|
||||
case LAN_LOCONET_FROM_LAN:
|
||||
case LAN_LOCONET_DISPATCH_ADDR:
|
||||
case LAN_LOCONET_DETECTOR:
|
||||
case LAN_CAN_DETECTOR:
|
||||
break; // not (yet) supported
|
||||
}
|
||||
}
|
||||
|
||||
void ServerKernel::onStart()
|
||||
{
|
||||
startInactiveClientPurgeTimer();
|
||||
}
|
||||
|
||||
void ServerKernel::onStop()
|
||||
{
|
||||
m_inactiveClientPurgeTimer.cancel();
|
||||
}
|
||||
|
||||
void ServerKernel::sendTo(const Message& message, IOHandler::ClientId clientId)
|
||||
{
|
||||
if(m_ioHandler->sendTo(message, clientId))
|
||||
{
|
||||
if(m_config.debugLogRXTX)
|
||||
EventLoop::call(
|
||||
[this, clientId, msg=toString(message)]()
|
||||
{
|
||||
Log::log(m_logId, LogMessage::D2004_X_TX_X, clientId, msg);
|
||||
});
|
||||
}
|
||||
else
|
||||
{} // log message and go to error state
|
||||
}
|
||||
|
||||
void ServerKernel::sendTo(const Message& message, BroadcastFlags broadcastFlags)
|
||||
{
|
||||
for(const auto& client : m_clients)
|
||||
if((client.second.broadcastFlags & broadcastFlags) != BroadcastFlags::None)
|
||||
sendTo(message, client.first);
|
||||
}
|
||||
|
||||
LanSystemStateDataChanged ServerKernel::getLanSystemStateDataChanged() const
|
||||
{
|
||||
LanSystemStateDataChanged message;
|
||||
|
||||
if(m_emergencyStop != TriState::False)
|
||||
message.centralState |= Z21_CENTRALSTATE_EMERGENCYSTOP;
|
||||
if(m_trackPowerOn != TriState::True)
|
||||
message.centralState |= Z21_CENTRALSTATE_TRACKVOLTAGEOFF;
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
std::shared_ptr<Decoder> ServerKernel::getDecoder(uint16_t address, bool longAddress) const
|
||||
{
|
||||
auto decoder = m_decoderList->getDecoder(DecoderProtocol::DCC, address, longAddress);
|
||||
if(!decoder)
|
||||
decoder = m_decoderList->getDecoder(DecoderProtocol::Auto, address);
|
||||
if(!decoder)
|
||||
decoder = m_decoderList->getDecoder(address);
|
||||
return decoder;
|
||||
}
|
||||
|
||||
void ServerKernel::removeClient(IOHandler::ClientId clientId)
|
||||
{
|
||||
auto& subscriptions = m_clients[clientId].subscriptions;
|
||||
while(!subscriptions.empty())
|
||||
unsubscribe(clientId, *subscriptions.begin());
|
||||
m_clients.erase(clientId);
|
||||
m_ioHandler->purgeClient(clientId);
|
||||
}
|
||||
|
||||
void ServerKernel::subscribe(IOHandler::ClientId clientId, uint16_t address, bool longAddress)
|
||||
{
|
||||
auto& subscriptions = m_clients[clientId].subscriptions;
|
||||
const std::pair<uint16_t, bool> key{address, longAddress};
|
||||
if(std::find(subscriptions.begin(), subscriptions.end(), key) != subscriptions.end())
|
||||
return;
|
||||
subscriptions.emplace_back(address, longAddress);
|
||||
if(subscriptions.size() > ServerConfig::subscriptionMax)
|
||||
unsubscribe(clientId, *subscriptions.begin());
|
||||
|
||||
EventLoop::call(
|
||||
[this, key]()
|
||||
{
|
||||
if(auto it = m_decoderSubscriptions.find(key); it == m_decoderSubscriptions.end())
|
||||
{
|
||||
if(auto decoder = getDecoder(key.first, key.second))
|
||||
m_decoderSubscriptions.emplace(key, DecoderSubscription{decoder->decoderChanged.connect(std::bind(&ServerKernel::decoderChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)), 1});
|
||||
}
|
||||
else
|
||||
{
|
||||
it->second.count++;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ServerKernel::unsubscribe(IOHandler::ClientId clientId, std::pair<uint16_t, bool> key)
|
||||
{
|
||||
{
|
||||
auto& subscriptions = m_clients[clientId].subscriptions;
|
||||
auto it = std::find(subscriptions.begin(), subscriptions.end(), key);
|
||||
if(it != subscriptions.end())
|
||||
subscriptions.erase(it);
|
||||
}
|
||||
|
||||
EventLoop::call(
|
||||
[this, key]()
|
||||
{
|
||||
if(auto it = m_decoderSubscriptions.find(key); it != m_decoderSubscriptions.end())
|
||||
{
|
||||
assert(it->second.count > 0);
|
||||
if(--it->second.count == 0)
|
||||
m_decoderSubscriptions.erase(it);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ServerKernel::decoderChanged(const Decoder& decoder, DecoderChangeFlags /*changes*/, uint32_t /*functionNumber*/)
|
||||
{
|
||||
const std::pair<uint16_t, bool> key(decoder.address, decoder.longAddress);
|
||||
const LanXLocoInfo message(decoder);
|
||||
|
||||
EventLoop::call(
|
||||
[this, key, message]()
|
||||
{
|
||||
for(auto it : m_clients)
|
||||
if((it.second.broadcastFlags & BroadcastFlags::PowerLocoTurnoutChanges) == BroadcastFlags::PowerLocoTurnoutChanges)
|
||||
{
|
||||
auto& subscriptions = it.second.subscriptions;
|
||||
if(std::find(subscriptions.begin(), subscriptions.end(), key) != subscriptions.end())
|
||||
sendTo(message, it.first);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ServerKernel::startInactiveClientPurgeTimer()
|
||||
{
|
||||
assert(m_config.inactiveClientPurgeTime > 0);
|
||||
m_inactiveClientPurgeTimer.expires_after(boost::asio::chrono::seconds(std::max(1, m_config.inactiveClientPurgeTime / 4)));
|
||||
m_inactiveClientPurgeTimer.async_wait(std::bind(&ServerKernel::inactiveClientPurgeTimerExpired, this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
void ServerKernel::inactiveClientPurgeTimerExpired(const boost::system::error_code& ec)
|
||||
{
|
||||
if(ec)
|
||||
return;
|
||||
|
||||
std::vector<IOHandler::ClientId> clientsToRemove;
|
||||
const auto purgeTime = std::chrono::steady_clock::now() - std::chrono::seconds(ServerConfig::inactiveClientPurgeTime);
|
||||
for(const auto& it : m_clients)
|
||||
if(it.second.lastSeen < purgeTime)
|
||||
clientsToRemove.emplace_back(it.first);
|
||||
|
||||
for(const auto& clientId : clientsToRemove)
|
||||
removeClient(clientId);
|
||||
|
||||
startInactiveClientPurgeTimer();
|
||||
}
|
||||
|
||||
}
|
||||
169
server/src/hardware/protocol/z21/serverkernel.hpp
Normale Datei
169
server/src/hardware/protocol/z21/serverkernel.hpp
Normale Datei
@ -0,0 +1,169 @@
|
||||
/**
|
||||
* server/src/hardware/protocol/z21/serverkernel.hpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2022 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_Z21_SERVERKERNEL_HPP
|
||||
#define TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_Z21_SERVERKERNEL_HPP
|
||||
|
||||
#include "kernel.hpp"
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <boost/asio/steady_timer.hpp>
|
||||
#include <boost/signals2/signal.hpp>
|
||||
#include "messages.hpp"
|
||||
|
||||
class DecoderList;
|
||||
|
||||
namespace Z21 {
|
||||
|
||||
class ServerKernel final : public Kernel
|
||||
{
|
||||
private:
|
||||
struct Client
|
||||
{
|
||||
std::chrono::time_point<std::chrono::steady_clock> lastSeen;
|
||||
BroadcastFlags broadcastFlags = BroadcastFlags::None;
|
||||
std::list<std::pair<uint16_t, bool>> subscriptions;
|
||||
};
|
||||
|
||||
struct DecoderSubscription
|
||||
{
|
||||
boost::signals2::connection connection;
|
||||
size_t count; //!< number of clients subscribed to the decoder
|
||||
};
|
||||
|
||||
boost::asio::steady_timer m_inactiveClientPurgeTimer;
|
||||
ServerConfig m_config;
|
||||
std::shared_ptr<DecoderList> m_decoderList;
|
||||
std::unordered_map<IOHandler::ClientId, Client> m_clients;
|
||||
std::map<std::pair<uint16_t, bool>, DecoderSubscription> m_decoderSubscriptions;
|
||||
TriState m_trackPowerOn = TriState::Undefined;
|
||||
std::function<void()> m_onTrackPowerOff;
|
||||
std::function<void()> m_onTrackPowerOn;
|
||||
TriState m_emergencyStop = TriState::Undefined;
|
||||
std::function<void()> m_onEmergencyStop;
|
||||
|
||||
ServerKernel(const ServerConfig& config, std::shared_ptr<DecoderList> decoderList);
|
||||
|
||||
void onStart() final;
|
||||
void onStop() final;
|
||||
|
||||
template<class T>
|
||||
void postSendTo(const T& message, IOHandler::ClientId clientId)
|
||||
{
|
||||
m_ioContext.post(
|
||||
[this, message, clientId]()
|
||||
{
|
||||
sendTo(message, clientId);
|
||||
});
|
||||
}
|
||||
|
||||
void sendTo(const Message& message, IOHandler::ClientId clientId);
|
||||
void sendTo(const Message& message, BroadcastFlags broadcastFlags);
|
||||
|
||||
LanSystemStateDataChanged getLanSystemStateDataChanged() const;
|
||||
|
||||
std::shared_ptr<Decoder> getDecoder(uint16_t address, bool longAddress) const;
|
||||
|
||||
void removeClient(IOHandler::ClientId clientId);
|
||||
void subscribe(IOHandler::ClientId clientId, uint16_t address, bool longAddress);
|
||||
void unsubscribe(IOHandler::ClientId clientId, std::pair<uint16_t, bool> key);
|
||||
void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber);
|
||||
|
||||
void startInactiveClientPurgeTimer();
|
||||
void inactiveClientPurgeTimerExpired(const boost::system::error_code& ec);
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Create kernel and IO handler
|
||||
* @param[in] config Z21 server configuration
|
||||
* @param[in] args IO handler arguments
|
||||
* @return The kernel instance
|
||||
*/
|
||||
template<class IOHandlerType, class... Args>
|
||||
static std::unique_ptr<ServerKernel> create(const ServerConfig& config, std::shared_ptr<DecoderList> decoderList, Args... args)
|
||||
{
|
||||
static_assert(std::is_base_of_v<IOHandler, IOHandlerType>);
|
||||
std::unique_ptr<ServerKernel> kernel{new ServerKernel(config, std::move(decoderList))};
|
||||
kernel->setIOHandler(std::make_unique<IOHandlerType>(*kernel, std::forward<Args>(args)...));
|
||||
return kernel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief ...
|
||||
* @param[in] callback ...
|
||||
* @note This function may not be called when the kernel is running.
|
||||
*/
|
||||
inline void setOnTrackPowerOff(std::function<void()> callback)
|
||||
{
|
||||
assert(!m_started);
|
||||
m_onTrackPowerOff = std::move(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief ...
|
||||
* @param[in] callback ...
|
||||
* @note This function may not be called when the kernel is running.
|
||||
*/
|
||||
inline void setOnTrackPowerOn(std::function<void()> callback)
|
||||
{
|
||||
assert(!m_started);
|
||||
m_onTrackPowerOn = std::move(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief ...
|
||||
* @param[in] callback ...
|
||||
* @note This function may not be called when the kernel is running.
|
||||
*/
|
||||
inline void setOnEmergencyStop(std::function<void()> callback)
|
||||
{
|
||||
assert(!m_started);
|
||||
m_onEmergencyStop = std::move(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set Z21 server configuration
|
||||
* @param[in] config The Z21 server configuration
|
||||
*/
|
||||
void setConfig(const ServerConfig& config);
|
||||
|
||||
/**
|
||||
* @brief Set Z21 state
|
||||
* @param[in] trackPowerOn \c true if track power is on, \c false if track power is off
|
||||
* @param[in] emergencyStop \c true if emergency stop is active, \c false if emergency stop isn't active
|
||||
*/
|
||||
void setState(bool trackPowerOn, bool emergencyStop);
|
||||
|
||||
/**
|
||||
* @brief Incoming message handler
|
||||
* This method must be called by the IO handler whenever a Z21 message is received.
|
||||
* @param[in] message The received Z21 message
|
||||
* @param[in] cliendId The client who sent the message
|
||||
* @note This function must run in the kernel's IO context
|
||||
*/
|
||||
void receiveFrom(const Message& message, IOHandler::ClientId clientId);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
51
server/src/hardware/protocol/z21/serversettings.cpp
Normale Datei
51
server/src/hardware/protocol/z21/serversettings.cpp
Normale Datei
@ -0,0 +1,51 @@
|
||||
/**
|
||||
* server/src/hardware/protocol/z21/serversettings.cpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2021-2022 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 "serversettings.hpp"
|
||||
|
||||
namespace Z21 {
|
||||
|
||||
ServerSettings::ServerSettings(Object& _parent, const std::string& parentPropertyName)
|
||||
: Settings(_parent, parentPropertyName)
|
||||
, allowEmergencyStop{this, "allow_emergency_stop", true, PropertyFlags::ReadWrite | PropertyFlags::Store}
|
||||
, allowTrackPowerOff{this, "allow_track_power_off", true, PropertyFlags::ReadWrite | PropertyFlags::Store}
|
||||
, allowTrackPowerOnReleaseEmergencyStop{this, "allow_track_power_on_release_emergency_stop", false, PropertyFlags::ReadWrite | PropertyFlags::Store}
|
||||
{
|
||||
m_interfaceItems.insertBefore(allowEmergencyStop, debugLogRXTX);
|
||||
m_interfaceItems.insertBefore(allowTrackPowerOff, debugLogRXTX);
|
||||
m_interfaceItems.insertBefore(allowTrackPowerOnReleaseEmergencyStop, debugLogRXTX);
|
||||
}
|
||||
|
||||
ServerConfig ServerSettings::config() const
|
||||
{
|
||||
ServerConfig config;
|
||||
|
||||
getConfig(config);
|
||||
|
||||
config.allowEmergencyStop = allowEmergencyStop;
|
||||
config.allowTrackPowerOff = allowTrackPowerOff;
|
||||
config.allowTrackPowerOnReleaseEmergencyStop = allowTrackPowerOnReleaseEmergencyStop;
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
}
|
||||
46
server/src/hardware/protocol/z21/serversettings.hpp
Normale Datei
46
server/src/hardware/protocol/z21/serversettings.hpp
Normale Datei
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* server/src/hardware/protocol/z21/serversettings.hpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2021-2022 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_Z21_SERVERSETTINGS_HPP
|
||||
#define TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_Z21_SERVERSETTINGS_HPP
|
||||
|
||||
#include "settings.hpp"
|
||||
|
||||
namespace Z21 {
|
||||
|
||||
class ServerSettings final : public Settings
|
||||
{
|
||||
CLASS_ID("z21_settings.server")
|
||||
|
||||
public:
|
||||
Property<bool> allowEmergencyStop;
|
||||
Property<bool> allowTrackPowerOff;
|
||||
Property<bool> allowTrackPowerOnReleaseEmergencyStop;
|
||||
|
||||
ServerSettings(Object& _parent, const std::string& parentPropertyName);
|
||||
|
||||
ServerConfig config() const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
42
server/src/hardware/protocol/z21/settings.cpp
Normale Datei
42
server/src/hardware/protocol/z21/settings.cpp
Normale Datei
@ -0,0 +1,42 @@
|
||||
/**
|
||||
* server/src/hardware/protocol/z21/settings.cpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2022 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 "settings.hpp"
|
||||
#include "../../../core/attributes.hpp"
|
||||
#include "../../../utils/displayname.hpp"
|
||||
|
||||
namespace Z21 {
|
||||
|
||||
Settings::Settings(Object& _parent, const std::string& parentPropertyName)
|
||||
: SubObject(_parent, parentPropertyName)
|
||||
, debugLogRXTX{this, "debug_log_rx_tx", false, PropertyFlags::ReadWrite | PropertyFlags::Store}
|
||||
{
|
||||
Attributes::addDisplayName(debugLogRXTX, DisplayName::Hardware::debugLogRXTX);
|
||||
m_interfaceItems.add(debugLogRXTX);
|
||||
}
|
||||
|
||||
void Settings::getConfig(Config& config) const
|
||||
{
|
||||
config.debugLogRXTX = debugLogRXTX;
|
||||
}
|
||||
|
||||
}
|
||||
45
server/src/hardware/protocol/z21/settings.hpp
Normale Datei
45
server/src/hardware/protocol/z21/settings.hpp
Normale Datei
@ -0,0 +1,45 @@
|
||||
/**
|
||||
* server/src/hardware/protocol/z21/settings.hpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2022 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_Z21_SETTINGS_HPP
|
||||
#define TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_Z21_SETTINGS_HPP
|
||||
|
||||
#include "../../../core/subobject.hpp"
|
||||
#include "../../../core/property.hpp"
|
||||
#include "config.hpp"
|
||||
|
||||
namespace Z21 {
|
||||
|
||||
class Settings : public SubObject
|
||||
{
|
||||
protected:
|
||||
Settings(Object& _parent, const std::string& parentPropertyName);
|
||||
|
||||
void getConfig(Config& config) const;
|
||||
|
||||
public:
|
||||
Property<bool> debugLogRXTX;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2020 Reinder Feenstra
|
||||
* Copyright (C) 2019-2022 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -63,6 +63,9 @@ constexpr void setEmergencyStop(uint8_t& db)
|
||||
|
||||
constexpr uint8_t getSpeedStep(uint8_t db, uint8_t speedSteps)
|
||||
{
|
||||
if(isEmergencyStop(db, speedSteps))
|
||||
return 0;
|
||||
|
||||
switch(speedSteps)
|
||||
{
|
||||
case 126:
|
||||
@ -70,7 +73,7 @@ constexpr uint8_t getSpeedStep(uint8_t db, uint8_t speedSteps)
|
||||
break;
|
||||
|
||||
case 28:
|
||||
db = ((db & 0x0F) << 1) | ((db & 0x10) >> 4);
|
||||
db = ((db & 0x0F) << 1) | ((db & 0x10) >> 4); //! @todo check
|
||||
break;
|
||||
|
||||
case 14:
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2021 Reinder Feenstra
|
||||
* Copyright (C) 2021-2022 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -58,6 +58,7 @@ namespace DisplayName
|
||||
constexpr std::string_view outputs = "hardware:outputs";
|
||||
constexpr std::string_view speedSteps = "hardware:speed_steps";
|
||||
constexpr std::string_view xpressnet = "hardware:xpressnet";
|
||||
constexpr std::string_view z21 = "hardware:z21";
|
||||
}
|
||||
namespace Interface
|
||||
{
|
||||
|
||||
Laden…
x
In neuem Issue referenzieren
Einen Benutzer sperren