z21: rewrite -> new kernel/iohandler model
Dieser Commit ist enthalten in:
Ursprung
5c891c2428
Commit
14eb29e95b
@ -31,5 +31,6 @@ std::shared_ptr<Interface> Interfaces::create(const std::shared_ptr<World>& worl
|
||||
IF_CLASSID_CREATE(LocoNetInterface)
|
||||
IF_CLASSID_CREATE(WlanMausInterface)
|
||||
IF_CLASSID_CREATE(XpressNetInterface)
|
||||
IF_CLASSID_CREATE(Z21Interface)
|
||||
return std::shared_ptr<Interface>();
|
||||
}
|
||||
|
||||
@ -31,6 +31,7 @@
|
||||
#include "loconetinterface.hpp"
|
||||
#include "wlanmausinterface.hpp"
|
||||
#include "xpressnetinterface.hpp"
|
||||
#include "z21interface.hpp"
|
||||
|
||||
struct Interfaces
|
||||
{
|
||||
@ -41,7 +42,8 @@ struct Interfaces
|
||||
ECoSInterface::classId,
|
||||
LocoNetInterface::classId,
|
||||
WlanMausInterface::classId,
|
||||
XpressNetInterface::classId
|
||||
XpressNetInterface::classId,
|
||||
Z21Interface::classId
|
||||
);
|
||||
|
||||
static std::shared_ptr<Interface> create(const std::shared_ptr<World>& world, std::string_view classId, std::string_view id = std::string_view{});
|
||||
|
||||
260
server/src/hardware/interface/z21interface.cpp
Normale Datei
260
server/src/hardware/interface/z21interface.cpp
Normale Datei
@ -0,0 +1,260 @@
|
||||
/**
|
||||
* server/src/hardware/interface/z21interface.cpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2021 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 "z21interface.hpp"
|
||||
#include "../decoder/decoderlisttablemodel.hpp"
|
||||
#include "../protocol/z21/messages.hpp"
|
||||
#include "../protocol/z21/iohandler/udpclientiohandler.hpp"
|
||||
#include "../../core/attributes.hpp"
|
||||
#include "../../log/log.hpp"
|
||||
#include "../../log/logmessageexception.hpp"
|
||||
#include "../../utils/category.hpp"
|
||||
#include "../../utils/displayname.hpp"
|
||||
#include "../../utils/inrange.hpp"
|
||||
#include "../../world/world.hpp"
|
||||
|
||||
Z21Interface::Z21Interface(const std::weak_ptr<World>& world, std::string_view _id)
|
||||
: Interface(world, _id)
|
||||
, hostname{this, "hostname", "192.168.1.203", PropertyFlags::ReadWrite | PropertyFlags::Store}
|
||||
, port{this, "port", 21105, PropertyFlags::ReadWrite | PropertyFlags::Store}
|
||||
, z21{this, "z21", nullptr, PropertyFlags::ReadOnly | PropertyFlags::Store | PropertyFlags::SubObject}
|
||||
, decoders{this, "decoders", nullptr, PropertyFlags::ReadOnly | PropertyFlags::NoStore | PropertyFlags::SubObject}
|
||||
, hardwareType{this, "hardware_type", "", PropertyFlags::ReadOnly | PropertyFlags::NoStore}
|
||||
, serialNumber{this, "serial_number", "", PropertyFlags::ReadOnly | PropertyFlags::NoStore}
|
||||
, firmwareVersion{this, "firmware_version", "", PropertyFlags::ReadOnly | PropertyFlags::NoStore}
|
||||
{
|
||||
z21.setValueInternal(std::make_shared<Z21::ClientSettings>(*this, z21.name()));
|
||||
decoders.setValueInternal(std::make_shared<DecoderList>(*this, decoders.name()));
|
||||
|
||||
Attributes::addDisplayName(hostname, DisplayName::IP::hostname);
|
||||
Attributes::addEnabled(hostname, !online);
|
||||
m_interfaceItems.insertBefore(hostname, notes);
|
||||
|
||||
Attributes::addDisplayName(port, DisplayName::IP::port);
|
||||
Attributes::addEnabled(port, !online);
|
||||
m_interfaceItems.insertBefore(port, notes);
|
||||
|
||||
Attributes::addDisplayName(z21, DisplayName::Hardware::z21);
|
||||
m_interfaceItems.insertBefore(z21, notes);
|
||||
|
||||
Attributes::addDisplayName(decoders, DisplayName::Hardware::decoders);
|
||||
m_interfaceItems.insertBefore(decoders, notes);
|
||||
|
||||
Attributes::addCategory(hardwareType, Category::info);
|
||||
m_interfaceItems.insertBefore(hardwareType, notes);
|
||||
|
||||
Attributes::addCategory(serialNumber, Category::info);
|
||||
m_interfaceItems.insertBefore(serialNumber, notes);
|
||||
|
||||
Attributes::addCategory(firmwareVersion, Category::info);
|
||||
m_interfaceItems.insertBefore(firmwareVersion, notes);
|
||||
}
|
||||
|
||||
bool Z21Interface::addDecoder(Decoder& decoder)
|
||||
{
|
||||
const bool success = DecoderController::addDecoder(decoder);
|
||||
if(success)
|
||||
decoders->addObject(decoder.shared_ptr<Decoder>());
|
||||
return success;
|
||||
}
|
||||
|
||||
bool Z21Interface::removeDecoder(Decoder& decoder)
|
||||
{
|
||||
const bool success = DecoderController::removeDecoder(decoder);
|
||||
if(success)
|
||||
decoders->removeObject(decoder.shared_ptr<Decoder>());
|
||||
return success;
|
||||
}
|
||||
|
||||
void Z21Interface::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber)
|
||||
{
|
||||
if(m_kernel)
|
||||
m_kernel->decoderChanged(decoder, changes, functionNumber);
|
||||
}
|
||||
|
||||
bool Z21Interface::setOnline(bool& value)
|
||||
{
|
||||
if(!m_kernel && value)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_kernel = Z21::ClientKernel::create<Z21::UDPClientIOHandler>(z21->config(), hostname.value(), port.value());
|
||||
|
||||
status.setValueInternal(InterfaceStatus::Initializing);
|
||||
|
||||
m_kernel->setLogId(id.value());
|
||||
m_kernel->setOnStarted(
|
||||
[this]()
|
||||
{
|
||||
status.setValueInternal(InterfaceStatus::Online);
|
||||
});
|
||||
m_kernel->setOnSerialNumberChanged(
|
||||
[this](uint32_t newValue)
|
||||
{
|
||||
serialNumber.setValueInternal(std::to_string(newValue));
|
||||
});
|
||||
m_kernel->setOnHardwareInfoChanged(
|
||||
[this](Z21::HardwareType type, uint8_t versionMajor, uint8_t versionMinor)
|
||||
{
|
||||
hardwareType.setValueInternal(std::string(Z21::toString(type)));
|
||||
Log::log(*this, LogMessage::I2002_HARDWARE_TYPE_X, hardwareType.value());
|
||||
|
||||
if(versionMajor != 0 || versionMinor != 0)
|
||||
{
|
||||
firmwareVersion.setValueInternal(std::to_string(versionMajor).append(".").append(std::to_string(versionMinor)));
|
||||
Log::log(*this, LogMessage::I2003_FIRMWARE_VERSION_X, firmwareVersion.value());
|
||||
}
|
||||
else
|
||||
firmwareVersion.setValueInternal("");
|
||||
});
|
||||
m_kernel->setOnTrackPowerOnChanged(
|
||||
[this](bool powerOn)
|
||||
{
|
||||
if(auto w = m_world.lock())
|
||||
{
|
||||
if(powerOn == contains(w->state.value(), WorldState::PowerOn))
|
||||
return;
|
||||
|
||||
if(powerOn)
|
||||
w->powerOn();
|
||||
else
|
||||
w->powerOff();
|
||||
}
|
||||
});
|
||||
m_kernel->setOnEmergencyStop(
|
||||
[this]()
|
||||
{
|
||||
if(auto w = m_world.lock(); w && contains(w->state.value(), WorldState::Run))
|
||||
w->stop();
|
||||
});
|
||||
|
||||
m_kernel->setDecoderController(this);
|
||||
|
||||
m_kernel->start();
|
||||
|
||||
m_z21PropertyChanged = z21->propertyChanged.connect(
|
||||
[this](BaseProperty&)
|
||||
{
|
||||
m_kernel->setConfig(z21->config());
|
||||
});
|
||||
|
||||
if(auto w = m_world.lock())
|
||||
{
|
||||
if(contains(w->state.value(), WorldState::PowerOn))
|
||||
m_kernel->trackPowerOn();
|
||||
else
|
||||
m_kernel->trackPowerOff();
|
||||
|
||||
if(!contains(w->state.value(), WorldState::Run))
|
||||
m_kernel->emergencyStop();
|
||||
}
|
||||
|
||||
Attributes::setEnabled({hostname, port}, false);
|
||||
}
|
||||
catch(const LogMessageException& e)
|
||||
{
|
||||
status.setValueInternal(InterfaceStatus::Offline);
|
||||
Log::log(*this, e.message(), e.args());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if(m_kernel && !value)
|
||||
{
|
||||
Attributes::setEnabled({hostname, port}, true);
|
||||
|
||||
m_z21PropertyChanged.disconnect();
|
||||
|
||||
m_kernel->stop();
|
||||
m_kernel.reset();
|
||||
|
||||
status.setValueInternal(InterfaceStatus::Offline);
|
||||
hardwareType.setValueInternal("");
|
||||
serialNumber.setValueInternal("");
|
||||
firmwareVersion.setValueInternal("");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Z21Interface::addToWorld()
|
||||
{
|
||||
Interface::addToWorld();
|
||||
|
||||
if(auto world = m_world.lock())
|
||||
{
|
||||
world->decoderControllers->add(std::dynamic_pointer_cast<DecoderController>(shared_from_this()));
|
||||
}
|
||||
}
|
||||
|
||||
void Z21Interface::destroying()
|
||||
{
|
||||
for(const auto& decoder : *decoders)
|
||||
{
|
||||
assert(decoder->interface.value() == std::dynamic_pointer_cast<DecoderController>(shared_from_this()));
|
||||
decoder->interface = nullptr;
|
||||
}
|
||||
|
||||
if(auto world = m_world.lock())
|
||||
{
|
||||
world->decoderControllers->remove(std::dynamic_pointer_cast<DecoderController>(shared_from_this()));
|
||||
}
|
||||
|
||||
Interface::destroying();
|
||||
}
|
||||
|
||||
void Z21Interface::worldEvent(WorldState state, WorldEvent event)
|
||||
{
|
||||
Interface::worldEvent(state, event);
|
||||
|
||||
if(m_kernel)
|
||||
{
|
||||
switch(event)
|
||||
{
|
||||
case WorldEvent::PowerOff:
|
||||
m_kernel->trackPowerOff();
|
||||
break;
|
||||
|
||||
case WorldEvent::PowerOn:
|
||||
m_kernel->trackPowerOn();
|
||||
if(!contains(state, WorldState::Run))
|
||||
m_kernel->emergencyStop();
|
||||
break;
|
||||
|
||||
case WorldEvent::Stop:
|
||||
m_kernel->emergencyStop();
|
||||
break;
|
||||
|
||||
case WorldEvent::Run:
|
||||
if(contains(state, WorldState::PowerOn))
|
||||
m_kernel->trackPowerOn();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Z21Interface::idChanged(const std::string& newId)
|
||||
{
|
||||
if(m_kernel)
|
||||
m_kernel->setLogId(newId);
|
||||
}
|
||||
76
server/src/hardware/interface/z21interface.hpp
Normale Datei
76
server/src/hardware/interface/z21interface.hpp
Normale Datei
@ -0,0 +1,76 @@
|
||||
/**
|
||||
* server/src/hardware/interface/z21interface.hpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2021 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_Z21INTERFACE_HPP
|
||||
#define TRAINTASTIC_SERVER_HARDWARE_INTERFACE_Z21INTERFACE_HPP
|
||||
|
||||
#include "interface.hpp"
|
||||
#include "../protocol/z21/clientkernel.hpp"
|
||||
#include "../protocol/z21/clientsettings.hpp"
|
||||
#include "../decoder/decodercontroller.hpp"
|
||||
#include "../decoder/decoderlist.hpp"
|
||||
#include "../../core/objectproperty.hpp"
|
||||
|
||||
/**
|
||||
* @brief Z21 hardware interface
|
||||
*/
|
||||
class Z21Interface final
|
||||
: public Interface
|
||||
, public DecoderController
|
||||
{
|
||||
CLASS_ID("interface.z21")
|
||||
DEFAULT_ID("z21")
|
||||
CREATE(Z21Interface)
|
||||
|
||||
private:
|
||||
std::unique_ptr<Z21::ClientKernel> m_kernel;
|
||||
boost::signals2::connection m_z21PropertyChanged;
|
||||
|
||||
void addToWorld() final;
|
||||
void destroying() final;
|
||||
void worldEvent(WorldState state, WorldEvent event) final;
|
||||
|
||||
void idChanged(const std::string& newId) final;
|
||||
|
||||
void updateVisible();
|
||||
|
||||
protected:
|
||||
bool setOnline(bool& value) final;
|
||||
|
||||
public:
|
||||
Property<std::string> hostname;
|
||||
Property<uint16_t> port;
|
||||
ObjectProperty<Z21::ClientSettings> z21;
|
||||
ObjectProperty<DecoderList> decoders;
|
||||
Property<std::string> hardwareType;
|
||||
Property<std::string> serialNumber;
|
||||
Property<std::string> firmwareVersion;
|
||||
|
||||
Z21Interface(const std::weak_ptr<World>& world, std::string_view _id);
|
||||
|
||||
// DecoderController:
|
||||
[[nodiscard]] bool addDecoder(Decoder& decoder) final;
|
||||
[[nodiscard]] bool removeDecoder(Decoder& decoder) final;
|
||||
void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber) final;
|
||||
};
|
||||
|
||||
#endif
|
||||
345
server/src/hardware/protocol/z21/clientkernel.cpp
Normale Datei
345
server/src/hardware/protocol/z21/clientkernel.cpp
Normale Datei
@ -0,0 +1,345 @@
|
||||
/**
|
||||
* server/src/hardware/protocol/z21/clientkernel.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 "clientkernel.hpp"
|
||||
#include "messages.hpp"
|
||||
#include "../xpressnet/messages.hpp"
|
||||
#include "../../decoder/decoder.hpp"
|
||||
#include "../../decoder/decoderchangeflags.hpp"
|
||||
#include "../../input/inputcontroller.hpp"
|
||||
#include "../../../core/eventloop.hpp"
|
||||
#include "../../../log/log.hpp"
|
||||
|
||||
namespace Z21 {
|
||||
|
||||
ClientKernel::ClientKernel(const ClientConfig& config)
|
||||
: Kernel()
|
||||
, m_keepAliveTimer(m_ioContext)
|
||||
, m_config{config}
|
||||
{
|
||||
}
|
||||
|
||||
void ClientKernel::setConfig(const ClientConfig& config)
|
||||
{
|
||||
m_ioContext.post(
|
||||
[this, newConfig=config]()
|
||||
{
|
||||
m_config = newConfig;
|
||||
});
|
||||
}
|
||||
|
||||
void ClientKernel::receive(const Message& message)
|
||||
{
|
||||
if(m_config.debugLogRXTX)
|
||||
EventLoop::call(
|
||||
[this, msg=toString(message)]()
|
||||
{
|
||||
Log::log(m_logId, LogMessage::D2002_RX_X, msg);
|
||||
});
|
||||
|
||||
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 0x61:
|
||||
if(message == LanXBCTrackPowerOff())
|
||||
{
|
||||
if(m_trackPowerOn != TriState::False)
|
||||
{
|
||||
m_trackPowerOn = TriState::False;
|
||||
|
||||
if(m_onTrackPowerOnChanged)
|
||||
EventLoop::call(
|
||||
[this]()
|
||||
{
|
||||
m_onTrackPowerOnChanged(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
else if(message == LanXBCTrackPowerOn())
|
||||
{
|
||||
if(m_trackPowerOn != TriState::True)
|
||||
{
|
||||
m_trackPowerOn = TriState::True;
|
||||
|
||||
if(m_onTrackPowerOnChanged)
|
||||
EventLoop::call(
|
||||
[this]()
|
||||
{
|
||||
m_onTrackPowerOnChanged(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x81:
|
||||
if(message == LanXBCStopped())
|
||||
{
|
||||
if(m_emergencyStop != TriState::True)
|
||||
{
|
||||
m_emergencyStop = TriState::True;
|
||||
|
||||
if(m_onEmergencyStop)
|
||||
EventLoop::call(
|
||||
[this]()
|
||||
{
|
||||
m_onEmergencyStop();
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LAN_GET_SERIAL_NUMBER:
|
||||
if(message.dataLen() == sizeof(LanGetSerialNumberReply))
|
||||
{
|
||||
const auto& reply = static_cast<const LanGetSerialNumberReply&>(message);
|
||||
|
||||
if(m_serialNumber != reply.serialNumber())
|
||||
{
|
||||
m_serialNumber = reply.serialNumber();
|
||||
if(m_onSerialNumberChanged)
|
||||
{
|
||||
EventLoop::call(
|
||||
[this, serialNumber=m_serialNumber]()
|
||||
{
|
||||
m_onSerialNumberChanged(serialNumber);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case LAN_GET_HWINFO:
|
||||
if(message.dataLen() == sizeof(LanGetHardwareInfoReply))
|
||||
{
|
||||
const auto& reply = static_cast<const LanGetHardwareInfoReply&>(message);
|
||||
|
||||
if(m_hardwareType != reply.hardwareType() ||
|
||||
m_firmwareVersionMajor != reply.firmwareVersionMajor() ||
|
||||
m_firmwareVersionMinor != reply.firmwareVersionMinor())
|
||||
{
|
||||
m_hardwareType = reply.hardwareType();
|
||||
m_firmwareVersionMajor = reply.firmwareVersionMajor();
|
||||
m_firmwareVersionMinor = reply.firmwareVersionMinor();
|
||||
|
||||
if(m_onHardwareInfoChanged)
|
||||
{
|
||||
EventLoop::call(
|
||||
[this, hardwareType=m_hardwareType, firmwareVersionMajor=m_firmwareVersionMajor, firmwareVersionMinor=m_firmwareVersionMinor]()
|
||||
{
|
||||
m_onHardwareInfoChanged(hardwareType, firmwareVersionMajor, firmwareVersionMinor);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case LAN_GET_CODE:
|
||||
case LAN_LOGOFF:
|
||||
case LAN_SET_BROADCASTFLAGS:
|
||||
case LAN_GET_BROADCASTFLAGS:
|
||||
case LAN_GET_LOCO_MODE:
|
||||
case LAN_SET_LOCO_MODE:
|
||||
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_SYSTEMSTATE_GETDATA:
|
||||
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 ClientKernel::trackPowerOn()
|
||||
{
|
||||
m_ioContext.post(
|
||||
[this]()
|
||||
{
|
||||
if(m_trackPowerOn != TriState::True || m_emergencyStop != TriState::False)
|
||||
send(LanXSetTrackPowerOn());
|
||||
});
|
||||
}
|
||||
|
||||
void ClientKernel::trackPowerOff()
|
||||
{
|
||||
m_ioContext.post(
|
||||
[this]()
|
||||
{
|
||||
if(m_trackPowerOn != TriState::False)
|
||||
send(LanXSetTrackPowerOff());
|
||||
});
|
||||
}
|
||||
|
||||
void ClientKernel::emergencyStop()
|
||||
{
|
||||
m_ioContext.post(
|
||||
[this]()
|
||||
{
|
||||
if(m_emergencyStop != TriState::True)
|
||||
send(LanXSetStop());
|
||||
});
|
||||
}
|
||||
|
||||
void ClientKernel::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber)
|
||||
{
|
||||
if(has(changes, DecoderChangeFlags::EmergencyStop | DecoderChangeFlags::Direction | DecoderChangeFlags::Throttle | DecoderChangeFlags::SpeedSteps))
|
||||
{
|
||||
LanXSetLocoDrive cmd;
|
||||
cmd.setAddress(decoder.address, decoder.longAddress);
|
||||
|
||||
switch(decoder.speedSteps)
|
||||
{
|
||||
case 14:
|
||||
{
|
||||
const uint8_t speedStep = Decoder::throttleToSpeedStep(decoder.throttle, 14);
|
||||
cmd.db0 = 0x10;
|
||||
if(decoder.emergencyStop)
|
||||
cmd.speedAndDirection = 0x01;
|
||||
else if(speedStep > 0)
|
||||
cmd.speedAndDirection = speedStep + 1;
|
||||
break;
|
||||
}
|
||||
case 28:
|
||||
{
|
||||
uint8_t speedStep = Decoder::throttleToSpeedStep(decoder.throttle, 28);
|
||||
cmd.db0 = 0x12;
|
||||
if(decoder.emergencyStop)
|
||||
cmd.speedAndDirection = 0x01;
|
||||
else if(speedStep > 0)
|
||||
{
|
||||
speedStep++;
|
||||
cmd.speedAndDirection = ((speedStep & 0x01) << 4) | (speedStep >> 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 126:
|
||||
case 128:
|
||||
default:
|
||||
{
|
||||
const uint8_t speedStep = Decoder::throttleToSpeedStep(decoder.throttle, 126);
|
||||
cmd.db0 = 0x13;
|
||||
if(decoder.emergencyStop)
|
||||
cmd.speedAndDirection = 0x01;
|
||||
else if(speedStep > 0)
|
||||
cmd.speedAndDirection = speedStep + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(decoder.direction.value() == Direction::Forward)
|
||||
cmd.speedAndDirection |= 0x80;
|
||||
|
||||
cmd.checksum = XpressNet::calcChecksum(*reinterpret_cast<const XpressNet::Message*>(&cmd.xheader));
|
||||
postSend(cmd);
|
||||
}
|
||||
else if(has(changes, DecoderChangeFlags::FunctionValue))
|
||||
{
|
||||
if(functionNumber <= LanXSetLocoFunction::functionNumberMax)
|
||||
if(const auto& f = decoder.getFunction(functionNumber))
|
||||
postSend(LanXSetLocoFunction(
|
||||
decoder.address, decoder.longAddress,
|
||||
static_cast<uint8_t>(functionNumber),
|
||||
f->value ? LanXSetLocoFunction::SwitchType::On : LanXSetLocoFunction::SwitchType::Off));
|
||||
}
|
||||
}
|
||||
|
||||
void ClientKernel::onStart()
|
||||
{
|
||||
// reset all state values
|
||||
m_serialNumber = 0;
|
||||
m_hardwareType = HWT_UNKNOWN;
|
||||
m_firmwareVersionMajor = 0;
|
||||
m_firmwareVersionMinor = 0;
|
||||
m_trackPowerOn = TriState::Undefined;
|
||||
m_emergencyStop = TriState::Undefined;
|
||||
|
||||
send(LanGetSerialNumber());
|
||||
send(LanGetHardwareInfo());
|
||||
|
||||
send(LanSetBroadcastFlags(
|
||||
BroadcastFlags::PowerLocoTurnoutChanges |
|
||||
BroadcastFlags::SystemStatusChanges |
|
||||
BroadcastFlags::AllLocoChanges)); // seems not to work with DR5000
|
||||
|
||||
send(LanGetBroadcastFlags());
|
||||
|
||||
send(LanSystemStateGetData());
|
||||
|
||||
startKeepAliveTimer();
|
||||
}
|
||||
|
||||
void ClientKernel::onStop()
|
||||
{
|
||||
send(LanLogoff());
|
||||
}
|
||||
|
||||
void ClientKernel::send(const Message& message)
|
||||
{
|
||||
if(m_ioHandler->send(message))
|
||||
{
|
||||
if(m_config.debugLogRXTX)
|
||||
EventLoop::call(
|
||||
[this, msg=toString(message)]()
|
||||
{
|
||||
Log::log(m_logId, LogMessage::D2001_TX_X, msg);
|
||||
});
|
||||
}
|
||||
else
|
||||
{} // log message and go to error state
|
||||
}
|
||||
|
||||
void ClientKernel::startKeepAliveTimer()
|
||||
{
|
||||
assert(m_config.keepAliveInterval > 0);
|
||||
m_keepAliveTimer.expires_after(boost::asio::chrono::seconds(m_config.keepAliveInterval));
|
||||
m_keepAliveTimer.async_wait(std::bind(&ClientKernel::keepAliveTimerExpired, this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
void ClientKernel::keepAliveTimerExpired(const boost::system::error_code& ec)
|
||||
{
|
||||
if(ec)
|
||||
return;
|
||||
|
||||
send(LanSystemStateGetData());
|
||||
|
||||
startKeepAliveTimer();
|
||||
}
|
||||
|
||||
}
|
||||
178
server/src/hardware/protocol/z21/clientkernel.hpp
Normale Datei
178
server/src/hardware/protocol/z21/clientkernel.hpp
Normale Datei
@ -0,0 +1,178 @@
|
||||
/**
|
||||
* server/src/hardware/protocol/z21/clientkernel.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_CLIENTKERNEL_HPP
|
||||
#define TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_Z21_CLIENTKERNEL_HPP
|
||||
|
||||
#include "kernel.hpp"
|
||||
#include <boost/asio/steady_timer.hpp>
|
||||
#include "../../../enum/tristate.hpp"
|
||||
|
||||
namespace Z21 {
|
||||
|
||||
class ClientKernel final : public Kernel
|
||||
{
|
||||
private:
|
||||
boost::asio::steady_timer m_keepAliveTimer;
|
||||
|
||||
uint32_t m_serialNumber;
|
||||
std::function<void(uint32_t)> m_onSerialNumberChanged;
|
||||
|
||||
HardwareType m_hardwareType;
|
||||
uint8_t m_firmwareVersionMajor;
|
||||
uint8_t m_firmwareVersionMinor;
|
||||
std::function<void(HardwareType, uint8_t, uint8_t)> m_onHardwareInfoChanged;
|
||||
|
||||
TriState m_trackPowerOn;
|
||||
TriState m_emergencyStop;
|
||||
std::function<void(bool)> m_onTrackPowerOnChanged;
|
||||
std::function<void()> m_onEmergencyStop;
|
||||
|
||||
DecoderController* m_decoderController = nullptr;
|
||||
|
||||
ClientConfig m_config;
|
||||
|
||||
ClientKernel(const ClientConfig& config);
|
||||
|
||||
void onStart() final;
|
||||
void onStop() final;
|
||||
|
||||
template<class T>
|
||||
void postSend(const T& message)
|
||||
{
|
||||
m_ioContext.post(
|
||||
[this, message]()
|
||||
{
|
||||
send(message);
|
||||
});
|
||||
}
|
||||
|
||||
void send(const Message& message);
|
||||
|
||||
void startKeepAliveTimer();
|
||||
void keepAliveTimerExpired(const boost::system::error_code& ec);
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Create kernel and IO handler
|
||||
* @param[in] config Z21 client configuration
|
||||
* @param[in] args IO handler arguments
|
||||
* @return The kernel instance
|
||||
*/
|
||||
template<class IOHandlerType, class... Args>
|
||||
static std::unique_ptr<ClientKernel> create(const ClientConfig& config, Args... args)
|
||||
{
|
||||
static_assert(std::is_base_of_v<IOHandler, IOHandlerType>);
|
||||
std::unique_ptr<ClientKernel> kernel{new ClientKernel(config)};
|
||||
kernel->setIOHandler(std::make_unique<IOHandlerType>(*kernel, std::forward<Args>(args)...));
|
||||
return kernel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set Z21 client configuration
|
||||
* @param[in] config The Z21 client configuration
|
||||
*/
|
||||
void setConfig(const ClientConfig& config);
|
||||
|
||||
/**
|
||||
* @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
|
||||
* @note This function must run in the kernel's IO context
|
||||
*/
|
||||
void receive(const Message& message);
|
||||
|
||||
/**
|
||||
* @brief ...
|
||||
* @param[in] callback ...
|
||||
* @note This function may not be called when the kernel is running.
|
||||
*/
|
||||
inline void setOnSerialNumberChanged(std::function<void(uint32_t)> callback)
|
||||
{
|
||||
assert(!m_started);
|
||||
m_onSerialNumberChanged = std::move(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief ...
|
||||
* @param[in] callback ...
|
||||
* @note This function may not be called when the kernel is running.
|
||||
*/
|
||||
inline void setOnHardwareInfoChanged(std::function<void(HardwareType, uint8_t, uint8_t)> callback)
|
||||
{
|
||||
assert(!m_started);
|
||||
m_onHardwareInfoChanged = std::move(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief ...
|
||||
* @param[in] callback ...
|
||||
* @note This function may not be called when the kernel is running.
|
||||
*/
|
||||
inline void setOnTrackPowerOnChanged(std::function<void(bool)> callback)
|
||||
{
|
||||
assert(!m_started);
|
||||
m_onTrackPowerOnChanged = std::move(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
void trackPowerOn();
|
||||
|
||||
/**
|
||||
*/
|
||||
void trackPowerOff();
|
||||
|
||||
/**
|
||||
*/
|
||||
void emergencyStop();
|
||||
|
||||
/**
|
||||
* @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 the decoder controller
|
||||
* @param[in] decoderController The decoder controller
|
||||
* @note This function may not be called when the kernel is running.
|
||||
*/
|
||||
inline void setDecoderController(DecoderController* decoderController)
|
||||
{
|
||||
assert(!m_started);
|
||||
m_decoderController = decoderController;
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
41
server/src/hardware/protocol/z21/clientsettings.cpp
Normale Datei
41
server/src/hardware/protocol/z21/clientsettings.cpp
Normale Datei
@ -0,0 +1,41 @@
|
||||
/**
|
||||
* server/src/hardware/protocol/z21/clientsettings.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 "clientsettings.hpp"
|
||||
|
||||
namespace Z21 {
|
||||
|
||||
ClientSettings::ClientSettings(Object& _parent, const std::string& parentPropertyName)
|
||||
: Settings(_parent, parentPropertyName)
|
||||
{
|
||||
}
|
||||
|
||||
ClientConfig ClientSettings::config() const
|
||||
{
|
||||
ClientConfig config;
|
||||
|
||||
getConfig(config);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
}
|
||||
42
server/src/hardware/protocol/z21/clientsettings.hpp
Normale Datei
42
server/src/hardware/protocol/z21/clientsettings.hpp
Normale Datei
@ -0,0 +1,42 @@
|
||||
/**
|
||||
* server/src/hardware/protocol/z21/clientsettings.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_CLIENTSETTINGS_HPP
|
||||
#define TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_Z21_CLIENTSETTINGS_HPP
|
||||
|
||||
#include "settings.hpp"
|
||||
|
||||
namespace Z21 {
|
||||
|
||||
class ClientSettings final : public Settings
|
||||
{
|
||||
CLASS_ID("z21_settings.client")
|
||||
|
||||
public:
|
||||
ClientSettings(Object& _parent, const std::string& parentPropertyName);
|
||||
|
||||
ClientConfig config() const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -32,6 +32,11 @@ struct Config
|
||||
bool debugLogRXTX;
|
||||
};
|
||||
|
||||
struct ClientConfig : Config
|
||||
{
|
||||
static constexpr uint16_t keepAliveInterval = 15; //!< sec
|
||||
};
|
||||
|
||||
struct ServerConfig : Config
|
||||
{
|
||||
static constexpr CommandStationId commandStationId = CommandStationId::Z21;
|
||||
|
||||
101
server/src/hardware/protocol/z21/iohandler/udpclientiohandler.cpp
Normale Datei
101
server/src/hardware/protocol/z21/iohandler/udpclientiohandler.cpp
Normale Datei
@ -0,0 +1,101 @@
|
||||
/**
|
||||
* 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 "udpclientiohandler.hpp"
|
||||
#include "../clientkernel.hpp"
|
||||
#include "../messages.hpp"
|
||||
#include "../../../../core/eventloop.hpp"
|
||||
#include "../../../../log/log.hpp"
|
||||
#include "../../../../log/logmessageexception.hpp"
|
||||
|
||||
namespace Z21 {
|
||||
|
||||
UDPClientIOHandler::UDPClientIOHandler(ClientKernel& kernel, const std::string& hostname, uint16_t port)
|
||||
: UDPIOHandler(kernel)
|
||||
, m_sendBufferOffset{0}
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
|
||||
m_remoteEndpoint.port(port);
|
||||
m_remoteEndpoint.address(boost::asio::ip::make_address(hostname, ec));
|
||||
if(ec)
|
||||
throw LogMessageException(LogMessage::E2003_MAKE_ADDRESS_FAILED_X, 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(), 0), ec))
|
||||
{
|
||||
m_socket.close();
|
||||
throw LogMessageException(LogMessage::E2006_SOCKET_BIND_FAILED_X, ec);
|
||||
}
|
||||
}
|
||||
|
||||
bool UDPClientIOHandler::send(const Message& message)
|
||||
{
|
||||
if(m_sendBufferOffset + message.dataLen() > m_sendBuffer.size())
|
||||
return false;
|
||||
|
||||
const bool wasEmpty = m_sendBufferOffset == 0;
|
||||
|
||||
memcpy(m_sendBuffer.data() + m_sendBufferOffset, &message, message.dataLen());
|
||||
m_sendBufferOffset += message.dataLen();
|
||||
|
||||
if(wasEmpty)
|
||||
send();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UDPClientIOHandler::receive(const Message& message, const boost::asio::ip::udp::endpoint& /*remoteEndpoint*/)
|
||||
{
|
||||
static_cast<ClientKernel&>(m_kernel).receive(message);
|
||||
}
|
||||
|
||||
void UDPClientIOHandler::send()
|
||||
{
|
||||
m_socket.async_send_to(boost::asio::buffer(m_sendBuffer.data(), m_sendBufferOffset), m_remoteEndpoint,
|
||||
[this](const boost::system::error_code& ec, std::size_t bytesTransferred)
|
||||
{
|
||||
if(!ec)
|
||||
{
|
||||
m_sendBufferOffset -= bytesTransferred;
|
||||
|
||||
if(m_sendBufferOffset > 0)
|
||||
{
|
||||
memmove(m_sendBuffer.data(), m_sendBuffer.data() + bytesTransferred, m_sendBufferOffset);
|
||||
send();
|
||||
}
|
||||
}
|
||||
else if(ec != boost::asio::error::operation_aborted)
|
||||
{
|
||||
EventLoop::call(
|
||||
[this, ec]()
|
||||
{
|
||||
Log::log(m_kernel.logId(), LogMessage::E2011_SOCKET_SEND_FAILED_X, ec);
|
||||
// TODO interface status -> error
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
52
server/src/hardware/protocol/z21/iohandler/udpclientiohandler.hpp
Normale Datei
52
server/src/hardware/protocol/z21/iohandler/udpclientiohandler.hpp
Normale Datei
@ -0,0 +1,52 @@
|
||||
/**
|
||||
* server/src/hardware/protocol/z21/iohandler/udpclientiohandler.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_UDPCLIENTIOHANDLER_HPP
|
||||
#define TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_Z21_IOHANDLER_UDPCLIENTIOHANDLER_HPP
|
||||
|
||||
#include "udpiohandler.hpp"
|
||||
|
||||
namespace Z21 {
|
||||
|
||||
class ClientKernel;
|
||||
|
||||
class UDPClientIOHandler final : public UDPIOHandler
|
||||
{
|
||||
private:
|
||||
boost::asio::ip::udp::endpoint m_remoteEndpoint;
|
||||
std::array<std::byte, payloadSizeMax> m_sendBuffer;
|
||||
size_t m_sendBufferOffset;
|
||||
|
||||
void send();
|
||||
|
||||
protected:
|
||||
void receive(const Message& message, const boost::asio::ip::udp::endpoint& remoteEndpoint) final;
|
||||
|
||||
public:
|
||||
UDPClientIOHandler(ClientKernel& kernel, const std::string& hostname, uint16_t port);
|
||||
|
||||
bool send(const Message& message) final;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -77,8 +77,23 @@ std::string toString(const Message& message, bool raw)
|
||||
switch(static_cast<const LanX&>(message).xheader)
|
||||
{
|
||||
case 0x21:
|
||||
if(message == LanXGetStatus())
|
||||
if(message == LanXGetVersion())
|
||||
s = "LAN_X_GET_VERSION";
|
||||
else if(message == LanXGetStatus())
|
||||
s = "LAN_X_GET_STATUS";
|
||||
else if(message == LanXSetTrackPowerOff())
|
||||
s = "LAN_X_SET_TRACK_POWER_OFF";
|
||||
else if(message == LanXSetTrackPowerOn())
|
||||
s = "LAN_X_SET_TRACK_POWER_ON";
|
||||
else
|
||||
raw = true;
|
||||
break;
|
||||
|
||||
case 0x61:
|
||||
if(message == LanXBCTrackPowerOff())
|
||||
s = "LAN_X_BC_TRACK_POWER_OFF";
|
||||
else if(message == LanXBCTrackPowerOn())
|
||||
s = "LAN_X_BC_TRACK_POWER_ON";
|
||||
else
|
||||
raw = true;
|
||||
break;
|
||||
@ -96,6 +111,13 @@ std::string toString(const Message& message, bool raw)
|
||||
raw = true;
|
||||
break;
|
||||
|
||||
case 0x80:
|
||||
if(message == LanXSetStop())
|
||||
s = "LAN_X_SET_STOP";
|
||||
else
|
||||
raw = true;
|
||||
break;
|
||||
|
||||
case 0xE3:
|
||||
if(const auto& getLocoInfo = static_cast<const LanXGetLocoInfo&>(message); getLocoInfo.db0 == 0xF0)
|
||||
{
|
||||
|
||||
@ -504,6 +504,11 @@ struct LanXSetLocoFunction : LanX
|
||||
Invalid = 3,
|
||||
};
|
||||
|
||||
static constexpr uint8_t functionNumberMax = 28;
|
||||
static constexpr uint8_t functionNumberMask = 0x3F;
|
||||
static constexpr uint8_t switchTypeMask = 0xC0;
|
||||
static constexpr uint8_t switchTypeShift = 6;
|
||||
|
||||
uint8_t db0 = 0xf8;
|
||||
uint8_t addressHigh;
|
||||
uint8_t addressLow;
|
||||
@ -515,6 +520,15 @@ struct LanXSetLocoFunction : LanX
|
||||
{
|
||||
}
|
||||
|
||||
LanXSetLocoFunction(uint16_t address, bool longAddress, uint8_t functionIndex, SwitchType value)
|
||||
: LanXSetLocoFunction()
|
||||
{
|
||||
setAddress(address, longAddress);
|
||||
setFunctionIndex(functionIndex);
|
||||
setSwitchType(value);
|
||||
calcChecksum();
|
||||
}
|
||||
|
||||
inline uint16_t address() const
|
||||
{
|
||||
return (static_cast<uint16_t>(addressHigh & 0x3F) << 8) | addressLow;
|
||||
@ -533,12 +547,23 @@ struct LanXSetLocoFunction : LanX
|
||||
|
||||
inline SwitchType switchType() const
|
||||
{
|
||||
return static_cast<SwitchType>(db3 >> 6);
|
||||
return static_cast<SwitchType>(db3 >> switchTypeShift);
|
||||
}
|
||||
|
||||
inline void setSwitchType(SwitchType value)
|
||||
{
|
||||
db3 = (db3 & functionNumberMask) | (static_cast<uint8_t>(value) << switchTypeShift);
|
||||
}
|
||||
|
||||
inline uint8_t functionIndex() const
|
||||
{
|
||||
return db3 & 0x3F;
|
||||
return db3 & functionNumberMask;
|
||||
}
|
||||
|
||||
inline void setFunctionIndex(uint8_t value)
|
||||
{
|
||||
assert(value <= functionNumberMax);
|
||||
db3 = (db3 & switchTypeMask) | (value & functionNumberMask);
|
||||
}
|
||||
} ATTRIBUTE_PACKED;
|
||||
static_assert(sizeof(LanXSetLocoFunction) == 10);
|
||||
|
||||
@ -70,6 +70,8 @@ enum class LogMessage : uint32_t
|
||||
I1004_CONNECTION_LOST = LogMessageOffset::info + 1004,
|
||||
I1005_BUILDING_WORLD_INDEX = LogMessageOffset::info + 1005,
|
||||
I2001_UNKNOWN_LOCO_ADDRESS_X = LogMessageOffset::info + 2001,
|
||||
I2002_HARDWARE_TYPE_X = LogMessageOffset::info + 2002,
|
||||
I2003_FIRMWARE_VERSION_X = LogMessageOffset::info + 2003,
|
||||
I9999_X = LogMessageOffset::info + 9999,
|
||||
|
||||
// Notice:
|
||||
|
||||
@ -236,6 +236,8 @@ message:I1003=Client connected
|
||||
message:I1004=Connection lost
|
||||
message:I1005=Building world index
|
||||
message:I2001=Unknown loco address: %1
|
||||
message:I2002=Hardware type: %1
|
||||
message:I2003=Firmware version: %1
|
||||
message:I9999=%1
|
||||
message:N1001=Received signal: %1
|
||||
message:N1002=Created new world
|
||||
|
||||
Laden…
x
In neuem Issue referenzieren
Einen Benutzer sperren