From 5974f8f0375d4e73719b9f795e31d0cf11083cb9 Mon Sep 17 00:00:00 2001 From: Reinder Feenstra Date: Sun, 23 Aug 2020 22:17:44 +0200 Subject: [PATCH] WIP: refactor Z21 protocol --- server/CMakeLists.txt | 4 +- .../src/hardware/commandstation/rocoz21.cpp | 115 +- server/src/hardware/controller/wlanmaus.cpp | 140 +-- server/src/hardware/controller/wlanmaus.hpp | 3 +- server/src/hardware/protocol/z21.cpp | 43 - server/src/hardware/protocol/z21.hpp | 1034 ----------------- server/src/hardware/protocol/z21/messages.cpp | 143 +++ server/src/hardware/protocol/z21/messages.hpp | 973 ++++++++++++++++ server/src/hardware/protocol/z21/utils.hpp | 118 ++ 9 files changed, 1361 insertions(+), 1212 deletions(-) delete mode 100644 server/src/hardware/protocol/z21.cpp delete mode 100644 server/src/hardware/protocol/z21.hpp create mode 100644 server/src/hardware/protocol/z21/messages.cpp create mode 100644 server/src/hardware/protocol/z21/messages.hpp create mode 100644 server/src/hardware/protocol/z21/utils.hpp diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index f2809f38..c5a6ea6d 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -33,12 +33,12 @@ file(GLOB SOURCES "src/hardware/decoder/*.cpp" "src/hardware/input/*.hpp" "src/hardware/input/*.cpp" - "src/hardware/protocol/*.hpp" - "src/hardware/protocol/*.cpp" "src/hardware/protocol/loconet/*.hpp" "src/hardware/protocol/loconet/*.cpp" "src/hardware/protocol/xpressnet/*.hpp" "src/hardware/protocol/xpressnet/*.cpp" + "src/hardware/protocol/z21/*.hpp" + "src/hardware/protocol/z21/*.cpp" "src/train/*.hpp" "src/train/*.cpp" "src/vehicle/*.hpp" diff --git a/server/src/hardware/commandstation/rocoz21.cpp b/server/src/hardware/commandstation/rocoz21.cpp index 35f5987a..ab69047d 100644 --- a/server/src/hardware/commandstation/rocoz21.cpp +++ b/server/src/hardware/commandstation/rocoz21.cpp @@ -27,7 +27,7 @@ #include "../../core/attributes.hpp" #include "../decoder/decoderchangeflags.hpp" #include "../protocol/xpressnet/messages.hpp" -#include "../protocol/z21.hpp" +#include "../protocol/z21/messages.hpp" #include "../../utils/to_hex.hpp" @@ -128,7 +128,7 @@ RocoZ21::RocoZ21(const std::weak_ptr& world, std::string_view _id) : void RocoZ21::emergencyStopChanged(bool value) { if(online && value) - send(z21_lan_x_set_stop()); + send(Z21::LanXSetStop()); } void RocoZ21::trackVoltageOffChanged(bool value) @@ -136,9 +136,9 @@ void RocoZ21::trackVoltageOffChanged(bool value) if(online) { if(value) - send(z21_lan_x_set_track_power_off()); + send(Z21::LanXSetTrackPowerOff()); else - send(z21_lan_x_set_track_power_on()); + send(Z21::LanXSetTrackPowerOn()); } } @@ -159,9 +159,9 @@ void RocoZ21::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, { if(has(changes, DecoderChangeFlags::EmergencyStop | DecoderChangeFlags::Direction | DecoderChangeFlags::SpeedStep | DecoderChangeFlags::SpeedSteps)) { - z21_lan_x_set_loco_drive cmd; - cmd.dataLen = sizeof(cmd); - cmd.header = Z21_LAN_X; + Z21::LanXSetLocoDrive cmd; + //cmd.dataLen = sizeof(cmd); + //cmd.header = Z21::LAN_X; SET_ADDRESS; assert(decoder.speedStep <= decoder.speedSteps); @@ -202,7 +202,7 @@ void RocoZ21::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, cmd.speedAndDirection |= 0x80; cmd.checksum = XpressNet::calcChecksum(*reinterpret_cast(&cmd.xheader)); - send(&cmd); + send(cmd); } else if(has(changes, DecoderChangeFlags::FunctionValue)) { @@ -210,13 +210,13 @@ void RocoZ21::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, { if(const auto& f = decoder.getFunction(functionNumber)) { - z21_lan_x_set_loco_function cmd; - cmd.dataLen = sizeof(cmd); - cmd.header = Z21_LAN_X; + Z21::LanXSetLocoFunction cmd; + //cmd.dataLen = sizeof(cmd); + //cmd.header = Z21_LAN_X; SET_ADDRESS; cmd.db3 = (f->value ? 0x40 : 0x00) | static_cast(functionNumber); cmd.checksum = XpressNet::calcChecksum(*reinterpret_cast(&cmd.xheader)); - send(&cmd); + send(cmd); } } } @@ -250,23 +250,23 @@ bool RocoZ21::setOnline(bool& value) receive(); // start receiving messages - send(z21_lan_set_broadcastflags(0x07000000 | /*0x00010000 |*/ 0x00000100 | 0x00000001)); + send(Z21::LanSetBroadcastFlags(0x07000000 | /*0x00010000 |*/ 0x00000100 | 0x00000001)); // try to communicate with Z21 send(Z21::LanGetSerialNumber()); - send(z21_lan_get_hwinfo()); - send(z21_lan_get_broadcastflags()); - send(z21_lan_systemstate_getdata()); + send(Z21::LanGetHardwareInfo()); + send(Z21::LanGetBroadcastFlags()); + send(Z21::LanSystemStateGetData()); for(auto& decoder : *decoders) - send(z21_lan_x_get_loco_info(decoder->address, decoder->longAddress)); + send(Z21::LanXGetLocoInfo(decoder->address, decoder->longAddress)); hostname.setAttributeEnabled(false); port.setAttributeEnabled(false); } else if(m_socket.is_open() && !value) { - send(z21_lan_logoff()); + send(Z21::LanLogoff()); serialNumber.setValueInternal(""); hardwareType.setValueInternal(""); @@ -287,14 +287,14 @@ void RocoZ21::receive() { if(!ec) { - if((bytesReceived >= sizeof(z21_lan_header))) + if((bytesReceived >= sizeof(Z21::Message))) { bool unknownMessage = false; const Z21::Message* message = reinterpret_cast(m_receiveBuffer.data()); - const z21_lan_header* cmd = reinterpret_cast(m_receiveBuffer.data()); - switch(cmd->header) + //const z21_lan_header* cmd = reinterpret_cast(m_receiveBuffer.data()); + switch(message->header()) { - case Z21_LAN_GET_SERIAL_NUMBER: + case Z21::LAN_GET_SERIAL_NUMBER: if(message->dataLen() == sizeof(Z21::LanGetSerialNumberReply)) { EventLoop::call( @@ -307,34 +307,34 @@ void RocoZ21::receive() unknownMessage = true; break; - case Z21_LAN_GET_HWINFO: + case Z21::LAN_GET_HWINFO: { - const z21_lan_get_hwinfo_reply* reply = static_cast(cmd); + const Z21::LanGetHardwareInfoReply* reply = static_cast(message); std::string hwType; - switch(reply->hardwareType) + switch(reply->hardwareType()) { - case Z21_HWT_Z21_OLD: + case Z21::HWT_Z21_OLD: hwType = "Black Z21 (hardware variant from 2012)"; break; - case Z21_HWT_Z21_NEW: + case Z21::HWT_Z21_NEW: hwType = "Black Z21 (hardware variant from 2013)"; break; - case Z21_HWT_SMARTRAIL: + case Z21::HWT_SMARTRAIL: hwType = "SmartRail (from 2012)"; break; - case Z21_HWT_Z21_SMALL: + case Z21::HWT_Z21_SMALL: hwType = "White Z21 (starter set variant from 2013)"; break; - case Z21_HWT_Z21_START : + case Z21::HWT_Z21_START : hwType = "Z21 start (starter set variant from 2016)"; break; default: - hwType = "0x" + to_hex(reply->hardwareType); + hwType = "0x" + to_hex(reply->hardwareType()); break; } - const std::string fwVersion = std::to_string((reply->firmwareVersion >> 8) & 0xFF) + "." + std::to_string(reply->firmwareVersion & 0xFF); + const std::string fwVersion = std::to_string(reply->firmwareVersionMajor()) + "." + std::to_string(reply->firmwareVersionMinor()); EventLoop::call( [this, hwType, fwVersion]() @@ -344,15 +344,15 @@ void RocoZ21::receive() }); break; } - case Z21_LAN_X: + case Z21::LAN_X: { // TODO check XOR - const uint8_t xheader = static_cast(cmd)->xheader; + const uint8_t xheader = static_cast(message)->xheader; switch(xheader) { - case Z21_LAN_X_LOCO_INFO: + case Z21::LAN_X_LOCO_INFO: { - const z21_lan_x_loco_info* info = static_cast(cmd); + const Z21::LanXLocoInfo* info = static_cast(message); const uint16_t address = (static_cast(info->addressHigh) << 8) | info->addressLow; const uint8_t speedStepMode = info->db2 & 0x07; const Direction direction = (info->speedAndDirection & 0x80) ? Direction::Forward : Direction::Reverse; @@ -386,12 +386,12 @@ void RocoZ21::receive() }); break; } - case Z21_LAN_X_BC: + case Z21::LAN_X_BC: { break; } - case Z21_LAN_X_BC_STOPPED: + case Z21::LAN_X_BC_STOPPED: EventLoop::call( [this]() { @@ -405,9 +405,9 @@ void RocoZ21::receive() } break; } - case Z21_LAN_SYSTEMSTATE_DATACHANGED: + case Z21::LAN_SYSTEMSTATE_DATACHANGED: { - const z21_lan_systemstate_datachanged state = *reinterpret_cast(m_receiveBuffer.data()); + const Z21::LanSystemStateDataChanged state = *reinterpret_cast(m_receiveBuffer.data()); /*EventLoop::call( [this, state]() { @@ -428,10 +428,10 @@ void RocoZ21::receive() });*/ break; } - case Z21_LAN_LOCONET_Z21_RX: + case Z21::LAN_LOCONET_Z21_RX: //case Z21_LAN_LOCONET_Z21_TX: //case Z21_LAN_LOCONET_Z21_LAN: - loconet->receive(*reinterpret_cast(m_receiveBuffer.data() + sizeof(z21_lan_header))); + loconet->receive(*reinterpret_cast(m_receiveBuffer.data() + sizeof(Z21::Message))); break; /* @@ -476,14 +476,14 @@ void RocoZ21::receive() default: //if(debugEnabled) { - std::string message = "unknown message: dataLen=0x" + to_hex(cmd->dataLen) + ", header=0x" + to_hex(cmd->header); - if(cmd->dataLen > 4) + std::string log = "unknown message: dataLen=0x" + to_hex(message->dataLen()) + ", header=0x" + to_hex(message->header()); + if(message->dataLen() > 4) { - message += ", data="; - for(int i = 4; i < cmd->dataLen; i++) - message += to_hex(reinterpret_cast(cmd)[i]); + log += ", data="; + for(int i = sizeof(Z21::Message); i < message->dataLen(); i++) + log += to_hex(reinterpret_cast(message)[i]); } - EventLoop::call([this, message](){ logDebug(message); }); + EventLoop::call([this, log](){ logDebug(log); }); } break; } @@ -513,22 +513,3 @@ void RocoZ21::send(const Z21::Message& message) EventLoop::call([this, ec](){ logError(id, "socket.async_send_to: " + ec.message()); }); });*/ } - -void RocoZ21::send(const z21_lan_header* data) -{ - logDebug("z21_lan_header->dataLen = " + std::to_string(data->dataLen)); - - boost::system::error_code ec; - m_socket.send_to(boost::asio::buffer(data, data->dataLen), m_remoteEndpoint, 0, ec); - if(ec) - logError("socket.send_to: " + ec.message()); - - - //m_socket.send_to(boost::asio::buffer(data, data->dataLen), 0, m_remoteEndpoint); - /*, - [this](const boost::system::error_code& ec, std::size_t) - { - if(ec) - EventLoop::call([this, ec](){ logError(id, "socket.async_send_to: " + ec.message()); }); - });*/ -} diff --git a/server/src/hardware/controller/wlanmaus.cpp b/server/src/hardware/controller/wlanmaus.cpp index aa38f64e..3cbdd124 100644 --- a/server/src/hardware/controller/wlanmaus.cpp +++ b/server/src/hardware/controller/wlanmaus.cpp @@ -25,18 +25,32 @@ #include "../../core/eventloop.hpp" #include "../commandstation/commandstation.hpp" #include "../decoder/decoder.hpp" -#include "../protocol/z21.hpp" +#include "../protocol/z21/messages.hpp" #include "../../utils/to_hex.hpp" #include "../../core/attributes.hpp" +static std::string to_string(const boost::asio::ip::udp::endpoint& endpoint) +{ + std::stringstream ss; + ss << endpoint; + return ss.str(); +} + WLANmaus::WLANmaus(const std::weak_ptr world, std::string_view _id) : Controller(world, _id), m_socket{Traintastic::instance->ioContext()}, m_blockLocoInfo{nullptr}, - port{this, "port", 21105, PropertyFlags::ReadWrite | PropertyFlags::Store} + m_debugLog{false}, + port{this, "port", 21105, PropertyFlags::ReadWrite | PropertyFlags::Store}, + debugLog{this, "debug_log", m_debugLog, PropertyFlags::ReadWrite | PropertyFlags::Store, + [this](bool value) + { + m_debugLog = value; + }} { Attributes::addEnabled(port, !active); m_interfaceItems.add(port); + m_interfaceItems.add(debugLog); } bool WLANmaus::setActive(bool& value) @@ -76,17 +90,15 @@ void WLANmaus::emergencyStopChanged(bool value) { if(value) { - const z21_lan_x_bc_stopped message; for(auto it : m_clients) if(it.second.broadcastFlags & Z21::PowerLocoTurnout) - sendTo(message, it.first); + sendTo(Z21::LanXBCStopped(), it.first); } else if(commandStation && !commandStation->trackVoltageOff) // send z21_lan_x_bc_track_power_on if power is on { - const z21_lan_x_bc_track_power_on message; for(auto it : m_clients) if(it.second.broadcastFlags & Z21::PowerLocoTurnout) - sendTo(message, it.first); + sendTo(Z21::LanXBCTrackPowerOn(), it.first); } } @@ -94,17 +106,15 @@ void WLANmaus::trackPowerChanged(bool value) { if(value) { - const z21_lan_x_bc_track_power_on message; for(auto it : m_clients) if(it.second.broadcastFlags & Z21::PowerLocoTurnout) - sendTo(message, it.first); + sendTo(Z21::LanXBCTrackPowerOn(), it.first); } else { - const z21_lan_x_bc_track_power_off message; for(auto it : m_clients) if(it.second.broadcastFlags & Z21::PowerLocoTurnout) - sendTo(message, it.first); + sendTo(Z21::LanXBCTrackPowerOff(), it.first); } } @@ -133,22 +143,29 @@ void WLANmaus::receive() { bool unknownMessage = false; const Z21::Message* message = reinterpret_cast(m_receiveBuffer.data()); - /*[[deprecated]]*/ const z21_lan_header* cmd = reinterpret_cast(m_receiveBuffer.data()); + + if(m_debugLog) + EventLoop::call( + [this, log=to_string(m_receiveEndpoint) + " rx: " + Z21::to_string(*message)]() + { + logDebug(log); + }); + switch(message->header()) { case Z21::LAN_X: { // TODO check XOR - const uint8_t xheader = static_cast(cmd)->xheader; + const uint8_t xheader = static_cast(message)->xheader; switch(xheader) { case 0x21: - if(*cmd == z21_lan_x_get_status()) + if(*message == Z21::LanXGetStatus()) { EventLoop::call( [this, endpoint=m_receiveEndpoint]() { - z21_lan_x_status_changed response; + Z21::LanXStatusChanged response; if(!commandStation || commandStation->emergencyStop) response.db1 |= Z21_CENTRALSTATE_EMERGENCYSTOP; @@ -160,7 +177,7 @@ void WLANmaus::receive() sendTo(response, endpoint); }); } - else if(*cmd == z21_lan_x_set_track_power_on()) + else if(*message == Z21::LanXSetTrackPowerOn()) { EventLoop::call( [this, endpoint=m_receiveEndpoint]() @@ -170,11 +187,11 @@ void WLANmaus::receive() commandStation->emergencyStop = false; commandStation->trackVoltageOff = false; if(!commandStation->trackVoltageOff) - sendTo(z21_lan_x_bc_track_power_on(), endpoint); + sendTo(Z21::LanXBCTrackPowerOn(), endpoint); } }); } - else if(*cmd == z21_lan_x_set_track_power_off()) + else if(*message == Z21::LanXSetTrackPowerOff()) { EventLoop::call( [this, endpoint=m_receiveEndpoint]() @@ -183,7 +200,7 @@ void WLANmaus::receive() { commandStation->trackVoltageOff = true; if(commandStation->trackVoltageOff) - sendTo(z21_lan_x_bc_track_power_off(), endpoint); + sendTo(Z21::LanXBCTrackPowerOff(), endpoint); } }); } @@ -192,7 +209,7 @@ void WLANmaus::receive() break; case 0x80: - if(*cmd == z21_lan_x_set_stop()) + if(*message == Z21::LanXSetStop()) { EventLoop::call( [this, endpoint=m_receiveEndpoint]() @@ -201,7 +218,7 @@ void WLANmaus::receive() { commandStation->emergencyStop = true; if(commandStation->emergencyStop) - sendTo(z21_lan_x_bc_stopped(), endpoint); + sendTo(Z21::LanXBCStopped(), endpoint); } }); } @@ -210,7 +227,7 @@ void WLANmaus::receive() break; case 0xE3: - if(const z21_lan_x_get_loco_info* r = static_cast(cmd); + if(const Z21::LanXGetLocoInfo* r = static_cast(message); r->db0 == 0xF0) { EventLoop::call( @@ -227,7 +244,7 @@ void WLANmaus::receive() // logDebug("m_clients[endpoint].broadcastFlags = " + std::to_string(m_clients[endpoint].broadcastFlags)); // logDebug("m_clients[endpoint].locoInfo.size() = " + std::to_string(m_clients[endpoint].locoInfo.size())); // logDebug("m_clients.size() = " + std::to_string(m_clients.size())); - sendTo(z21_lan_x_loco_info(*decoder), endpoint); + sendTo(Z21::LanXLocoInfo(*decoder), endpoint); } }); } @@ -236,7 +253,7 @@ void WLANmaus::receive() break; case 0xE4: - if(const z21_lan_x_set_loco_drive* r = static_cast(cmd); + if(const Z21::LanXSetLocoDrive* r = static_cast(message); r->db0 >= 0x10 && r->db0 <= 0x13) { EventLoop::call( @@ -265,9 +282,9 @@ void WLANmaus::receive() logInfo("Unknown loco address: " + std::to_string(request.address())); }); } - else if(const z21_lan_x_set_loco_function* r = static_cast(cmd); + else if(const Z21::LanXSetLocoFunction* r = static_cast(message); r->db0 == 0xF8 && - r->switchType() != z21_lan_x_set_loco_function::SwitchType::Invalid) + r->switchType() != Z21::LanXSetLocoFunction::SwitchType::Invalid) { EventLoop::call( [this, request=*r]() @@ -277,19 +294,19 @@ void WLANmaus::receive() if(auto function = decoder->getFunction(request.functionIndex())) switch(request.switchType()) { - case z21_lan_x_set_loco_function::SwitchType::Off: + case Z21::LanXSetLocoFunction::SwitchType::Off: function->value = false; break; - case z21_lan_x_set_loco_function::SwitchType::On: + case Z21::LanXSetLocoFunction::SwitchType::On: function->value = true; break; - case z21_lan_x_set_loco_function::SwitchType::Toggle: + case Z21::LanXSetLocoFunction::SwitchType::Toggle: function->value = !function->value; break; - case z21_lan_x_set_loco_function::SwitchType::Invalid: + case Z21::LanXSetLocoFunction::SwitchType::Invalid: assert(false); break; } @@ -298,12 +315,12 @@ void WLANmaus::receive() break; case 0xF1: - if(*cmd == z21_lan_x_get_firmware_version()) + if(*message == Z21::LanXGetFirmwareVersion()) { EventLoop::call( [this, endpoint=m_receiveEndpoint]() { - sendTo(z21_lan_x_get_firmware_version_reply(1, 30), endpoint); + sendTo(Z21::LanXGetFirmwareVersionReply(1, 30), endpoint); }); } else @@ -353,12 +370,12 @@ void WLANmaus::receive() break; case Z21::LAN_GET_HWINFO: - if(cmd->dataLen == sizeof(z21_lan_get_hwinfo)) + if(message->dataLen() == sizeof(Z21::LanGetHardwareInfo)) { EventLoop::call( [this, endpoint=m_receiveEndpoint]() { - sendTo(z21_lan_get_hwinfo_reply(Z21_HWT_Z21_START, 1, 30), endpoint); + sendTo(Z21::LanGetHardwareInfoReply(Z21::HWT_Z21_START, 1, 30), endpoint); }); } else @@ -366,12 +383,15 @@ void WLANmaus::receive() break; case Z21::LAN_SET_BROADCASTFLAGS: - if(message->dataLen() == sizeof(z21_lan_set_broadcastflags)) + if(message->dataLen() == sizeof(Z21::LanSetBroadcastFlags)) { EventLoop::call( - [this, request=*static_cast(cmd), endpoint=m_receiveEndpoint]() + [this, broadcastFlags=static_cast(message)->broadcastFlags(), endpoint=m_receiveEndpoint]() { - m_clients[endpoint].broadcastFlags = request.broadcastFlags; + if(debugLog) + logDebug(to_string(endpoint) + " LAN_SET_BROADCASTFLAGS 0x" + to_hex(broadcastFlags)); + + m_clients[endpoint].broadcastFlags = broadcastFlags; }); } else @@ -379,12 +399,15 @@ void WLANmaus::receive() break; case Z21::LAN_SYSTEMSTATE_GETDATA: - if(message->dataLen() == sizeof(z21_lan_systemstate_getdata)) + if(message->dataLen() == sizeof(Z21::LanSystemStateGetData)) { EventLoop::call( [this, endpoint=m_receiveEndpoint]() { - z21_lan_systemstate_datachanged response; + if(debugLog) + logDebug(to_string(endpoint) + " LAN_SYSTEMSTATE_GETDATA"); + + Z21::LanSystemStateDataChanged response; if(!commandStation || commandStation->emergencyStop) response.centralState |= Z21_CENTRALSTATE_EMERGENCYSTOP; @@ -404,6 +427,8 @@ void WLANmaus::receive() EventLoop::call( [this, endpoint=m_receiveEndpoint]() { + if(debugLog) + logDebug(to_string(endpoint) + " LAN_LOGOFF"); m_clients.erase(endpoint); }); } @@ -416,17 +441,17 @@ void WLANmaus::receive() break; } - if(unknownMessage /*&& debugEnabled*/) + /*if(unknownMessage && debugLog) { - std::string message = "unknown message: dataLen=0x" + to_hex(cmd->dataLen) + ", header=0x" + to_hex(cmd->header); - if(cmd->dataLen > 4) + std::string log = "unknown message: dataLen=0x" + to_hex(message->dataLen()) + ", header=0x" + to_hex(message->header()); + if(message->dataLen() > 4) { - message += ", data="; - for(int i = 4; i < cmd->dataLen; i++) - message += to_hex(reinterpret_cast(cmd)[i]); + log += ", data="; + for(int i = sizeof(Z21::Message); i < message->dataLen(); i++) + log += to_hex(reinterpret_cast(message)[i]); } - EventLoop::call([this, message](){ logDebug(message); }); - } + EventLoop::call([this, log](){ logDebug(log); }); + }*/ } receive(); } @@ -439,26 +464,11 @@ void WLANmaus::receive() }); } -void WLANmaus::sendTo(const z21_lan_header& msg, const boost::asio::ip::udp::endpoint& endpoint) -{ - // TODO: add to queue, send async - - boost::system::error_code ec; - m_socket.send_to(boost::asio::buffer(&msg, msg.dataLen), endpoint, 0, ec); - if(ec) - EventLoop::call([this, ec](){ logError("socket.send_to: " + ec.message()); }); -/* - m_socket.async_send_to(boost::asio::buffer(&msg, msg.dataLen), endpoint, - [this](const boost::system::error_code& ec, std::size_t) - { - if(ec) - EventLoop::call([this, ec](){ logError("socket.async_send_to: " + ec.message()); }); - }); - */ -} - void WLANmaus::sendTo(const Z21::Message& message, const boost::asio::ip::udp::endpoint& endpoint) { + if(debugLog) + logDebug(to_string(endpoint) + " tx: " + Z21::to_string(message)); + // TODO: add to queue, send async boost::system::error_code ec; @@ -478,7 +488,7 @@ void WLANmaus::sendTo(const Z21::Message& message, const boost::asio::ip::udp::e void WLANmaus::broadcastLocoInfo(const Decoder& decoder) { const uint16_t key = locoInfoKey(decoder.address, decoder.longAddress); - const z21_lan_x_loco_info message(decoder); + const Z21::LanXLocoInfo message(decoder); //logDebug("z21_lan_x_loco_info.speedAndDirection=" + std::to_string(message.speedAndDirection)); diff --git a/server/src/hardware/controller/wlanmaus.hpp b/server/src/hardware/controller/wlanmaus.hpp index cb06655d..0e1c455b 100644 --- a/server/src/hardware/controller/wlanmaus.hpp +++ b/server/src/hardware/controller/wlanmaus.hpp @@ -49,6 +49,7 @@ class WLANmaus : public Controller std::array m_receiveBuffer; std::map m_clients; Decoder* m_blockLocoInfo; + std::atomic_bool m_debugLog; constexpr uint16_t locoInfoKey(uint16_t address, bool longAddress) { @@ -65,7 +66,6 @@ class WLANmaus : public Controller void decoderChanged(const Decoder& decoder, DecoderChangeFlags, uint32_t) final; void receive(); - /*[[deprecated]]*/ void sendTo(const z21_lan_header& msg, const boost::asio::ip::udp::endpoint& endpoint); void sendTo(const Z21::Message& message, const boost::asio::ip::udp::endpoint& endpoint); void broadcastLocoInfo(const Decoder& decoder); @@ -74,6 +74,7 @@ class WLANmaus : public Controller CREATE(WLANmaus); Property port; + Property debugLog; WLANmaus(const std::weak_ptr world, std::string_view _id); }; diff --git a/server/src/hardware/protocol/z21.cpp b/server/src/hardware/protocol/z21.cpp deleted file mode 100644 index 2cd1f84f..00000000 --- a/server/src/hardware/protocol/z21.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/** - * server/src/hardware/protocol/z21.cpp - * - * This file is part of the traintastic source code. - * - * Copyright (C) 2019-2020 Reinder Feenstra - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include "z21.hpp" -#include "../decoder/decoder.hpp" - -//namespace Z21 { - -z21_lan_x_loco_info::z21_lan_x_loco_info(const Decoder& decoder) : - z21_lan_x_loco_info() -{ - setAddress(decoder.address, decoder.longAddress); - setSpeedSteps(decoder.speedSteps); - setDirection(decoder.direction); - if(decoder.emergencyStop) - setEmergencyStop(); - else - setSpeedStep(decoder.speedStep); - for(auto function : *decoder.functions) - setFunction(function->number, function->value); - calcChecksum(); -} - -//} diff --git a/server/src/hardware/protocol/z21.hpp b/server/src/hardware/protocol/z21.hpp deleted file mode 100644 index 6235a173..00000000 --- a/server/src/hardware/protocol/z21.hpp +++ /dev/null @@ -1,1034 +0,0 @@ -/** - * server/src/hardware/commandstation/protocol/z21.hpp - * - * This file is part of the traintastic source code. - * - * Copyright (C) 2019-2020 Reinder Feenstra - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#ifndef TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_Z21_HPP -#define TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_Z21_HPP - -#include -#include -#include -#include -#include "../../utils/endian.hpp" -#include "../../utils/packed.hpp" - -class Decoder; - -namespace Z21 { - -namespace Utils { - -inline constexpr uint8_t directionFlag = 0x80; - -constexpr Direction getDirection(uint8_t db) -{ - return (db & directionFlag) ? Direction::Forward : Direction::Reverse; -} - -constexpr void setDirection(uint8_t& db, Direction direction) -{ - if(direction == Direction::Forward) - db |= directionFlag; - else - db &= ~directionFlag; -} - -constexpr bool isEmergencyStop(uint8_t db, uint8_t speedSteps) -{ - switch(speedSteps) - { - case 126: - return (db & 0x7F) == 0x01; - - case 28: - return (db & 0x1F) == 0x01 || (db & 0x1F) == 0x11; - - case 14: - return (db & 0x0F) == 0x01; - } - return true; -} - -constexpr void setEmergencyStop(uint8_t& db) -{ - db = (db & directionFlag) | 0x01; -} - -constexpr uint8_t getSpeedStep(uint8_t db, uint8_t speedSteps) -{ - switch(speedSteps) - { - case 126: - db &= 0x7F; - break; - - case 28: - db = ((db & 0x0F) << 1) | ((db & 0x10) >> 4); - break; - - case 14: - db &= 0x0F; - break; - - default: - return 0; - } - return db > 1 ? db - 1 : 0; // step 1 = EStop -} - -constexpr void setSpeedStep(uint8_t& db, uint8_t speedSteps, uint8_t speedStep) -{ - db &= directionFlag; // preserve direction flag - if(++speedStep > 1) - switch(speedSteps) - { - case 126: - db |= speedStep & 0x7F; - break; - - case 28: - db |= ((speedStep >> 1) & 0x0F) | ((speedStep << 4) & 0x01); - break; - - case 14: - db |= speedStep & 0x0F; - break; - } -} - -constexpr uint8_t toBCD(uint8_t value) -{ - return ((value / 10) << 4) | (value % 10); -} - -constexpr uint8_t fromBCD(uint8_t value) -{ - return ((value >> 4) * 10) + (value & 0x0F); -} - -} - -enum Header : uint16_t -{ - LAN_GET_SERIAL_NUMBER = 0x10, - LAN_GET_HWINFO = 0x1A, - LAN_LOGOFF = 0x30, - LAN_X = 0x40, - LAN_SET_BROADCASTFLAGS = 0x50, - LAN_GET_BROADCASTFLAGS = 0x51, - LAN_GET_LOCO_MODE = 0x60, - LAN_SET_LOCO_MODE = 0x61, - LAN_SYSTEMSTATE_DATACHANGED = 0x84, - LAN_SYSTEMSTATE_GETDATA = 0x85, - LAN_LOCONET_Z21_RX = 0xA0, - LAN_LOCONET_Z21_TX = 0xA1, -}; - -enum BroadcastFlags : uint32_t -{ - /** - * Broadcasts and info messages concerning driving and switching are delivered to the registered clients automatically. - * The following messages are concerned: - * 2.7 LAN_X_BC_TRACK_POWER_OFF - * 2.8 LAN_X_BC_TRACK_POWER_ON - * 2.9 LAN_X_BC_PROGRAMMING_MODE - * 2.10 LAN_X_BC_TRACK_SHORT_CIRCUIT - * 2.14 LAN_X_BC_STOPPED - * 4.4 LAN_X_LOCO_INFO (loco address must be subscribed too) - * 5.3 LAN_X_TURNOUT_INFO - */ - PowerLocoTurnout = 0x00000001, -}; - -enum LocoMode : uint8_t -{ - DCC = 0, - Motorola = 1, -}; - -PRAGMA_PACK_PUSH_1 - -class Message -{ - protected: - uint16_t m_dataLen; //!< DataLen (little endian): Total length over the entire data set including DataLen, Header and Data, i.e. DataLen = 2+2+n. - uint16_t m_header; //!< Header (little endian): Describes the Command and the Protocol’s group. \see Header - - public: - Message(uint16_t _dataLen, Header _header) : - m_dataLen{host_to_le(_dataLen)}, - m_header{host_to_le(_header)} - { - } - - inline uint16_t dataLen() const - { - return le_to_host(m_dataLen); - } - - inline Header header() const - { - return static_cast
(le_to_host(m_header)); - } -} ATTRIBUTE_PACKED; -static_assert(sizeof(Message) == 4); - -class LanGetSerialNumber : public Message -{ - public: - LanGetSerialNumber() : - Message(sizeof(LanGetSerialNumber), LAN_GET_SERIAL_NUMBER) - { - } -} ATTRIBUTE_PACKED; -static_assert(sizeof(LanGetSerialNumber) == 4); - -class LanGetSerialNumberReply : public Message -{ - protected: - uint32_t m_serialNumber; // LE - - public: - LanGetSerialNumberReply(uint32_t _serialNumber) : - Message(sizeof(LanGetSerialNumberReply), LAN_GET_SERIAL_NUMBER), - m_serialNumber{host_to_le(_serialNumber)} - { - } - - inline uint32_t serialNumber() const - { - return le_to_host(m_serialNumber); - } - - inline void setSerialNumber(uint32_t value) - { - m_serialNumber = host_to_le(value); - } -} ATTRIBUTE_PACKED; -static_assert(sizeof(LanGetSerialNumberReply) == 8); - - - - - -// hw_info - -class LanLogoff : public Message -{ - LanLogoff() : - Message(sizeof(LanLogoff), LAN_LOGOFF) - { - } -} ATTRIBUTE_PACKED; -static_assert(sizeof(LanLogoff) == 4); - - - -class LanGetLocoMode : public Message -{ - protected: - uint16_t m_address; // BE - - public: - LanGetLocoMode(uint16_t _address = 0) : - Message(sizeof(LanGetLocoMode), LAN_GET_LOCO_MODE) - { - setAddress(_address); - } - - inline uint16_t address() const - { - return be_to_host(m_address); - } - - inline void setAddress(uint16_t value) - { - m_address = host_to_be(value); - } -} ATTRIBUTE_PACKED; -static_assert(sizeof(LanGetLocoMode) == 6); - -class LanGetLocoModeReply : public Message -{ - protected: - uint16_t m_address; // BE - LocoMode m_mode; - - public: - LanGetLocoModeReply(uint16_t _address = 0, LocoMode _mode = DCC) : - Message(sizeof(LanGetLocoModeReply), LAN_GET_LOCO_MODE), - m_address{host_to_be(_address)} - { - } - - inline uint16_t address() const - { - return be_to_host(m_address); - } - - inline void setAddress(uint16_t value) - { - m_address = host_to_be(value); - } - - inline LocoMode locoMode() const - { - return m_mode; - } - - inline void setLocoMode(LocoMode value) - { - m_mode = value; - } -} ATTRIBUTE_PACKED; -static_assert(sizeof(LanGetLocoModeReply) == 7); - -class LanSetLocoMode : public Message -{ - protected: - uint16_t m_address; // BE - LocoMode m_mode; - - public: - LanSetLocoMode(uint16_t _address = 0, LocoMode _mode = DCC) : - Message(sizeof(LanSetLocoMode), LAN_SET_LOCO_MODE), - m_address{host_to_be(_address)} - { - } - - inline uint16_t address() const - { - return be_to_host(m_address); - } - - inline void setAddress(uint16_t value) - { - m_address = host_to_be(value); - } - - inline LocoMode locoMode() const - { - return m_mode; - } - - inline void setLocoMode(LocoMode value) - { - m_mode = value; - } -} ATTRIBUTE_PACKED; -static_assert(sizeof(LanSetLocoMode) == 7); - -PRAGMA_PACK_POP - - - - - - - - - - - - - -} - - -#define Z21_LAN_GET_SERIAL_NUMBER 0x10 -#define Z21_LAN_GET_HWINFO 0x1A -#define Z21_LAN_LOGOFF 0x30 -#define Z21_LAN_X 0x40 - #define Z21_LAN_X_SET_STOP 0x80 - #define Z21_LAN_X_TURNOUT_INFO 0x43 - #define Z21_LAN_X_BC 0x61 - #define Z21_LAN_X_BC_TRACK_POWER_OFF 0x00 - #define Z21_LAN_X_BC_TRACK_POWER_ON 0x01 - #define Z21_LAN_X_BC_PROGRAMMING_MODE 0x02 - #define Z21_LAN_X_BC_TRACK_SHORT_CIRCUIT 0x08 - #define Z21_LAN_X_CV_NACK_SC 0x12 - #define Z21_LAN_X_CV_NACK 0x13 - #define Z21_LAN_X_UNKNOWN_COMMAND 0x82 - #define Z21_LAN_X_BC_STOPPED 0x81 - #define Z21_LAN_X_LOCO_INFO 0xEF -#define Z21_LAN_SET_BROADCASTFLAGS 0x50 -#define Z21_LAN_GET_BROADCASTFLAGS 0x51 -#define Z21_LAN_SYSTEMSTATE_DATACHANGED 0x84 -#define Z21_LAN_SYSTEMSTATE_GETDATA 0x85 -#define Z21_LAN_LOCONET_Z21_RX 0xA0 -#define Z21_LAN_LOCONET_Z21_TX 0xA1 - -#define Z21_HWT_Z21_OLD 0x00000200 //!< „black Z21” (hardware variant from 2012) -#define Z21_HWT_Z21_NEW 0x00000201 //!< „black Z21”(hardware variant from 2013) -#define Z21_HWT_SMARTRAIL 0x00000202 //!< SmartRail (from 2012) -#define Z21_HWT_Z21_SMALL 0x00000203 //!< „white z21” starter set variant (from 2013) -#define Z21_HWT_Z21_START 0x00000204 //!< „z21 start” starter set variant (from 2016) - -#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 -#define Z21_CENTRALSTATE_PROGRAMMINGMODEACTIVE 0x20 //!< The programming mode is active - -#define Z21_CENTRALSTATEEX_HIGHTEMPERATURE 0x01 //!< Temperature too high -#define Z21_CENTRALSTATEEX_POWERLOST 0x02 //!< Input voltage too low -#define Z21_CENTRALSTATEEX_SHORTCIRCUITEXTERNAL 0x04 //!< Short circuit at the external booster output -#define Z21_CENTRALSTATEEX_SHORTCIRCUITINTERNAL 0x08 //!< Short circuit at the main track or programming track - -PRAGMA_PACK_PUSH_1 - -struct z21_lan_header -{ - uint16_t dataLen; // LE - uint16_t header; // LE -} ATTRIBUTE_PACKED; -static_assert(sizeof(z21_lan_header) == 0x04); - - -struct z21_lan_get_hwinfo : z21_lan_header -{ - z21_lan_get_hwinfo() - { - dataLen = sizeof(z21_lan_get_hwinfo); - header = Z21_LAN_GET_HWINFO; - } -} ATTRIBUTE_PACKED; -static_assert(sizeof(z21_lan_get_hwinfo) == 0x04); - -struct z21_lan_get_hwinfo_reply : z21_lan_header -{ - uint32_t hardwareType; // LE - uint32_t firmwareVersion; // LE - - z21_lan_get_hwinfo_reply(uint32_t _hardwareType = 0, uint8_t _firmwareVersionMajor = 0, uint8_t _firmwareVersionMinor = 0) : - hardwareType{host_to_le(_hardwareType)}, - firmwareVersion{host_to_le(static_cast(Z21::Utils::toBCD(_firmwareVersionMajor)) << 8 | Z21::Utils::toBCD(_firmwareVersionMinor))} - { - dataLen = sizeof(z21_lan_get_hwinfo_reply); - header = Z21_LAN_GET_HWINFO; - } -} ATTRIBUTE_PACKED; -static_assert(sizeof(z21_lan_get_hwinfo_reply) == 0x0C); - -struct z21_lan_logoff : z21_lan_header -{ - z21_lan_logoff() - { - dataLen = sizeof(z21_lan_logoff); - header = Z21_LAN_LOGOFF; - } -} ATTRIBUTE_PACKED; -static_assert(sizeof(z21_lan_logoff) == 0x04); - -struct z21_lan_x : z21_lan_header -{ - uint8_t xheader; - - z21_lan_x() - { - header = Z21_LAN_X; - } - - void calcChecksum(uint8_t len) - { - uint8_t* checksum = &xheader + len + 1; - *checksum = xheader; - for(uint8_t* db = &xheader + 1; db < checksum; db++) - *checksum ^= *db; - } - - inline void calcChecksum() - { - calcChecksum(xheader & 0x0F); - } -} ATTRIBUTE_PACKED; -static_assert(sizeof(z21_lan_x) == 0x05); - - - - -struct z21_lan_x_get_firmware_version : z21_lan_x -{ - uint8_t db0 = 0x0A; - uint8_t checksum = 0xFB; - - z21_lan_x_get_firmware_version() - { - dataLen = sizeof(z21_lan_x_get_firmware_version); - xheader = 0xF1; - } -} ATTRIBUTE_PACKED; -static_assert(sizeof(z21_lan_x_get_firmware_version) == 7); - -struct z21_lan_x_get_firmware_version_reply : z21_lan_x -{ - uint8_t db0 = 0x0A; - uint8_t majorBCD; - uint8_t minorBCD; - uint8_t checksum; - - z21_lan_x_get_firmware_version_reply() - { - dataLen = sizeof(z21_lan_x_get_firmware_version_reply); - xheader = 0xF3; - } - - z21_lan_x_get_firmware_version_reply(uint8_t _major, uint8_t _minor) : - z21_lan_x_get_firmware_version_reply() - { - setVersionMajor(_major); - setVersionMinor(_minor); - calcChecksum(); - } - - inline uint8_t versionMajor() const - { - return Z21::Utils::fromBCD(majorBCD); - } - - inline uint8_t versionMinor() const - { - return Z21::Utils::fromBCD(minorBCD); - } - - inline void setVersionMajor(uint8_t value) - { - assert(value < 100); - majorBCD = Z21::Utils::toBCD(value); - } - - inline void setVersionMinor(uint8_t value) - { - assert(value < 100); - minorBCD = Z21::Utils::toBCD(value); - } -}; -static_assert(sizeof(z21_lan_x_get_firmware_version_reply) == 9); - - - - - - - - -struct z21_lan_x_set_stop : z21_lan_x -{ - uint8_t checksum = 0x80; - - z21_lan_x_set_stop() - { - dataLen = sizeof(z21_lan_x_set_stop); - xheader = Z21_LAN_X_SET_STOP; - } -} ATTRIBUTE_PACKED; -static_assert(sizeof(z21_lan_x_set_stop) == 0x06); - -struct z21_lan_x_bc_stopped : z21_lan_x -{ - uint8_t db0 = 0x00; - uint8_t checksum = 0x80; - - z21_lan_x_bc_stopped() - { - dataLen = sizeof(z21_lan_x_bc_stopped); - xheader = Z21_LAN_X_BC_STOPPED; - } -}; -static_assert(sizeof(z21_lan_x_bc_stopped) == 0x07); - -struct z21_lan_x_bc_track_power_off : z21_lan_x -{ - uint8_t db0 = 0x00; - uint8_t checksum = 0x61; - - z21_lan_x_bc_track_power_off() - { - dataLen = sizeof(z21_lan_x_bc_track_power_off); - xheader = 0x61; - } -}; -static_assert(sizeof(z21_lan_x_bc_track_power_off) == 0x07); - -struct z21_lan_x_bc_track_power_on : z21_lan_x -{ - uint8_t db0 = 0x01; - uint8_t checksum = 0x60; - - z21_lan_x_bc_track_power_on() - { - dataLen = sizeof(z21_lan_x_bc_track_power_on); - xheader = 0x61; - } -}; -static_assert(sizeof(z21_lan_x_bc_track_power_on) == 0x07); - - - - - - - - -struct z21_lan_x_get_status : z21_lan_x -{ - uint8_t db0 = 0x24; - uint8_t checksum = 0x05; - - z21_lan_x_get_status() - { - dataLen = sizeof(z21_lan_x_get_status); - xheader = 0x21; - } -}; -static_assert(sizeof(z21_lan_x_get_status) == 0x07); - -struct z21_lan_x_status_changed : z21_lan_x -{ - uint8_t db0 = 0x22; - uint8_t db1 = 0; - uint8_t checksum; - - z21_lan_x_status_changed() - { - dataLen = sizeof(z21_lan_x_status_changed); - xheader = 0x62; - } -}; -static_assert(sizeof(z21_lan_x_status_changed) == 0x08); - -struct z21_lan_x_set_track_power_off : z21_lan_x -{ - uint8_t db0 = 0x80; - uint8_t checksum = 0xa1; - - z21_lan_x_set_track_power_off() - { - dataLen = sizeof(z21_lan_x_set_track_power_off); - header = Z21_LAN_X; - xheader = 0x21; - } -} ATTRIBUTE_PACKED; - - -struct z21_lan_x_set_track_power_on : z21_lan_x -{ - uint8_t db0 = 0x81; - uint8_t checksum = 0xa0; - - z21_lan_x_set_track_power_on() - { - dataLen = sizeof(z21_lan_x_set_track_power_off); - xheader = 0x21; - } -} ATTRIBUTE_PACKED; -static_assert(sizeof(z21_lan_x_set_track_power_off) == 0x07); - -struct z21_lan_x_get_loco_info : z21_lan_x -{ - uint8_t db0 = 0xF0; - uint8_t addressHigh; - uint8_t addressLow; - uint8_t checksum; - - z21_lan_x_get_loco_info(uint16_t address, bool longAddress) - { - dataLen = sizeof(z21_lan_x_get_loco_info); - header = Z21_LAN_X; - xheader = 0xE3; - setAddress(address, longAddress); - calcChecksum(); - } - - inline uint16_t address() const - { - return (static_cast(addressHigh & 0x3F) << 8) | addressLow; - } - - inline bool isLongAddress() const - { - 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 void calcChecksum() - { - checksum = xheader ^ db0 ^ addressHigh ^ addressLow; - } -} ATTRIBUTE_PACKED; -static_assert(sizeof(z21_lan_x_get_loco_info) == 0x09); - -struct z21_lan_x_loco_info : z21_lan_x -{ - static constexpr uint8_t db2_busy_flag = 0x08; - static constexpr uint8_t db2_speed_steps_14 = 0x00; - static constexpr uint8_t db2_speed_steps_28 = 0x02; - static constexpr uint8_t db2_speed_steps_128 = 0x04; - static constexpr uint8_t db2_speed_steps_mask = 0x07; - static constexpr uint8_t directionFlag = 0x80; - static constexpr uint8_t speedStepMask = 0x7F; - static constexpr uint8_t flagF0 = 0x10; - - uint8_t addressHigh = 0; - uint8_t addressLow = 0; - uint8_t db2 = 0; - uint8_t speedAndDirection = 0; - uint8_t db4 = 0; - uint8_t f5f12 = 0; - uint8_t f13f20 = 0; - uint8_t f21f28 = 0; - uint8_t checksum = 0; - - z21_lan_x_loco_info() - { - dataLen = sizeof(z21_lan_x_loco_info); - xheader = 0xEF; - } - - z21_lan_x_loco_info(const Decoder& decoder); - - inline uint16_t address() const - { - return (static_cast(addressHigh & 0x3F) << 8) | addressLow; - } - - inline bool isLongAddress() const - { - 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 bool isBusy() const - { - return db2 & db2_busy_flag; - } - - inline void setBusy(bool value) - { - if(value) - db2 |= db2_busy_flag; - else - db2 &= ~db2_busy_flag; - } - - inline uint8_t speedSteps() const - { - switch(db2 & db2_speed_steps_mask) - { - case db2_speed_steps_14: return 14; - case db2_speed_steps_28: return 28; - case db2_speed_steps_128: return 126; - } - return 0; - } - - inline void setSpeedSteps(uint8_t value) - { - db2 &= ~db2_speed_steps_mask; - switch(value) - { - case 14: db2 |= db2_speed_steps_14; break; - case 28: db2 |= db2_speed_steps_28; break; - case 126: db2 |= db2_speed_steps_128; break; - } - } - - inline Direction direction() const - { - return Z21::Utils::getDirection(speedAndDirection); - } - - inline void setDirection(Direction value) - { - Z21::Utils::setDirection(speedAndDirection, value); - } - - inline bool isEmergencyStop() const - { - return Z21::Utils::isEmergencyStop(speedAndDirection, speedSteps()); - } - - inline void setEmergencyStop() - { - Z21::Utils::setEmergencyStop(speedAndDirection); - } - - inline uint8_t speedStep() const - { - return Z21::Utils::getSpeedStep(speedAndDirection, speedSteps()); - } - - inline void setSpeedStep(uint8_t value) - { - Z21::Utils::setSpeedStep(speedAndDirection, speedSteps(), value); - } - - bool getFunction(uint8_t index) - { - if(index == 0) - return db4 & flagF0; - else if(index <= 4) - return db4 & (1 << (index - 1)); - else if(index <= 12) - return f5f12 & (1 << (index - 5)); - else if(index <= 20) - return f13f20 & (1 << (index - 13)); - else if(index <= 28) - return f21f28 & (1 << (index - 21)); - else - return false; - } - - void setFunction(uint8_t index, bool value) - { - if(index == 0) - { - if(value) - db4 |= flagF0; - else - db4 &= ~flagF0; - } - else if(index <= 4) - { - const uint8_t flag = (1 << (index - 1)); - if(value) - db4 |= flag; - else - db4 &= ~flag; - } - else if(index <= 12) - { - const uint8_t flag = (1 << (index - 5)); - if(value) - f5f12 |= flag; - else - f5f12 &= ~flag; - } - else if(index <= 20) - { - const uint8_t flag = (1 << (index - 13)); - if(value) - f13f20 |= flag; - else - f13f20 &= ~flag; - } - else if(index <= 28) - { - const uint8_t flag = (1 << (index - 21)); - if(value) - f21f28 |= flag; - else - f21f28 &= ~flag; - } - } - - void calcChecksum() - { - checksum = xheader; - for(uint8_t* db = &addressHigh; db < &checksum; db++) - checksum ^= *db; - } -} ATTRIBUTE_PACKED; - -struct z21_lan_x_set_loco_drive : z21_lan_header -{ - static constexpr uint8_t directionFlag = 0x80; - - uint8_t xheader = 0xe4; - uint8_t db0; - uint8_t addressHigh; - uint8_t addressLow; - uint8_t speedAndDirection = 0; - uint8_t checksum; - - inline uint16_t address() const - { - return (static_cast(addressHigh & 0x3F) << 8) | addressLow; - } - - inline bool isLongAddress() const - { - return (addressHigh & 0xC0) == 0xC0; - } - - inline uint8_t speedSteps() const - { - switch(db0 & 0x0F) - { - case 0: return 14; - case 2: return 28; - case 3: return 126; - default: return 0; - } - } - - inline Direction direction() const - { - return Z21::Utils::getDirection(speedAndDirection); - } - - inline void setDirection(Direction value) - { - Z21::Utils::setDirection(speedAndDirection, value); - } - - inline bool isEmergencyStop() const - { - return Z21::Utils::isEmergencyStop(speedAndDirection, speedSteps()); - } - - inline void setEmergencyStop() - { - Z21::Utils::setEmergencyStop(speedAndDirection); - } - - inline uint8_t speedStep() const - { - return Z21::Utils::getSpeedStep(speedAndDirection, speedSteps()); - } - - inline void setSpeedStep(uint8_t value) - { - Z21::Utils::setSpeedStep(speedAndDirection, speedSteps(), value); - } -} ATTRIBUTE_PACKED; -static_assert(sizeof(z21_lan_x_set_loco_drive) == 0x0a); - -struct z21_lan_x_set_loco_function : z21_lan_header -{ - enum class SwitchType - { - Off = 0, - On = 1, - Toggle = 2, - Invalid = 3, - }; - - uint8_t xheader = 0xe4; - uint8_t db0 = 0xf8; - uint8_t addressHigh; - uint8_t addressLow; - uint8_t db3; - uint8_t checksum; - - inline uint16_t address() const - { - return (static_cast(addressHigh & 0x3F) << 8) | addressLow; - } - - inline bool isLongAddress() const - { - return (addressHigh & 0xC0) == 0xC0; - } - - inline SwitchType switchType() const - { - return static_cast(db3 >> 6); - } - - inline uint8_t functionIndex() const - { - return db3 & 0x3F; - } -} ATTRIBUTE_PACKED; -static_assert(sizeof(z21_lan_x_set_loco_function) == 0x0a); - -struct z21_lan_systemstate_getdata : z21_lan_header -{ - z21_lan_systemstate_getdata() - { - dataLen = sizeof(z21_lan_systemstate_getdata); - header = Z21_LAN_SYSTEMSTATE_GETDATA; - } -} ATTRIBUTE_PACKED; -static_assert(sizeof(z21_lan_systemstate_getdata) == 0x04); - -struct z21_lan_set_broadcastflags : z21_lan_header -{ - Z21::BroadcastFlags broadcastFlags; // LE - - z21_lan_set_broadcastflags(uint32_t _broadcastFlags = 0) : - broadcastFlags{_broadcastFlags} - { - dataLen = sizeof(z21_lan_set_broadcastflags); - header = Z21_LAN_SET_BROADCASTFLAGS; - } -} ATTRIBUTE_PACKED; -static_assert(sizeof(z21_lan_set_broadcastflags) == 0x08); - -struct z21_lan_get_broadcastflags : z21_lan_header -{ - z21_lan_get_broadcastflags() - { - dataLen = sizeof(z21_lan_get_broadcastflags); - header = Z21_LAN_GET_BROADCASTFLAGS; - } -} ATTRIBUTE_PACKED; -static_assert(sizeof(z21_lan_get_broadcastflags) == 0x04); - -struct z21_lan_systemstate_datachanged : z21_lan_header -{ - int16_t mainCurrent= 0; //!< Current on the main track in mA - int16_t progCurrent = 0; //!< Current on programming track in mA; - int16_t filteredMainCurrent = 0; //!< Smoothed current on the main track in mA - int16_t temperature = 0; //!< Command station internal temperature in °C - uint16_t supplyVoltage = 0; //!< Supply voltage in mV - uint16_t vccVoltage = 0; //!< Internal voltage, identical to track voltage in mV - uint8_t centralState = 0; //!< bitmask, see Z21_CENTRALSTATE - uint8_t centralStateEx = 0; //!< bitmask, see Z21_CENTRALSTATEEX - uint8_t _reserved1 = 0; - uint8_t _reserved2 = 0; - - z21_lan_systemstate_datachanged() - { - dataLen = sizeof(z21_lan_systemstate_datachanged); - header = Z21_LAN_SYSTEMSTATE_DATACHANGED; - } -} ATTRIBUTE_PACKED; -static_assert(sizeof(z21_lan_systemstate_datachanged) == 0x14); - -PRAGMA_PACK_POP - - -inline bool operator ==(const z21_lan_header& lhs, const z21_lan_header& rhs) -{ - return lhs.dataLen == rhs.dataLen && std::memcmp(&lhs.header, &rhs.header, lhs.dataLen - sizeof(lhs.dataLen)) == 0; -} - - - - - -inline bool operator ==(const Z21::Message& lhs, const Z21::Message& rhs) -{ - return lhs.dataLen() == rhs.dataLen() && std::memcmp(&lhs, &rhs, lhs.dataLen()) == 0; -} - -#endif diff --git a/server/src/hardware/protocol/z21/messages.cpp b/server/src/hardware/protocol/z21/messages.cpp new file mode 100644 index 00000000..05ef566d --- /dev/null +++ b/server/src/hardware/protocol/z21/messages.cpp @@ -0,0 +1,143 @@ +/** + * server/src/hardware/protocol/z21/messages.cpp + * + * This file is part of the traintastic source code. + * + * Copyright (C) 2019-2020 Reinder Feenstra + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "messages.hpp" +#include "../../decoder/decoder.hpp" +#include "../../../utils/to_hex.hpp" + +namespace Z21 { + +static std::string_view to_string(Header header) +{ + switch(header) + { + case LAN_GET_SERIAL_NUMBER: return "LAN_GET_SERIAL_NUMBER"; + case LAN_GET_HWINFO: return "LAN_GET_HWINFO"; + case LAN_LOGOFF: return "LAN_LOGOFF"; + case LAN_X: return "LAN_X"; + case LAN_SET_BROADCASTFLAGS: return "LAN_SET_BROADCASTFLAGS"; + case LAN_GET_BROADCASTFLAGS: return "LAN_GET_BROADCASTFLAGS"; + case LAN_SYSTEMSTATE_DATACHANGED: return "LAN_SYSTEMSTATE_DATACHANGED"; + case LAN_SYSTEMSTATE_GETDATA: return "LAN_SYSTEMSTATE_GETDATA"; + case LAN_LOCONET_Z21_RX: return "LAN_LOCONET_Z21_RX"; + case LAN_LOCONET_Z21_TX: return "LAN_LOCONET_Z21_TX"; + } + return {}; +} + +std::string to_string(const Message& message, bool raw) +{ + std::string s; + if(std::string_view sv = to_string(message.header()); !sv.empty()) + s = sv; + else + s = to_hex(message.header()); + + switch(message.header()) + { + case LAN_LOGOFF: + if(message.dataLen() != sizeof(Z21::LanLogoff)) + raw = true; + break; + + case LAN_X: + switch(static_cast(message).xheader) + { + case 0x21: + if(message == LanXGetStatus()) + s = "LAN_X_GET_STATUS"; + else + raw = true; + break; + + case 0x62: + if(const LanXStatusChanged& statusChanged = static_cast(message); statusChanged.db0 == 0x22) + { + s = "LAN_X_STATUS_CHANGED"; + s.append(" emergency_stop=").append(statusChanged.db1 & Z21_CENTRALSTATE_EMERGENCYSTOP ? "yes" : "no"); + s.append(" track_voltage=").append(statusChanged.db1 & Z21_CENTRALSTATE_TRACKVOLTAGEOFF ? "off" : "on"); + s.append(" short_circuit=").append(statusChanged.db1 & Z21_CENTRALSTATE_SHORTCIRCUIT ? "yes" : "no"); + s.append(" programming_mode_active=").append(statusChanged.db1 & Z21_CENTRALSTATE_PROGRAMMINGMODEACTIVE ? "yes" : "no"); + } + else + raw = true; + break; + + default: + raw = true; + break; + } + break; + + case Z21::LAN_GET_BROADCASTFLAGS: + if(message.dataLen() == sizeof(Z21::LanGetBroadcastFlags)) + s = "LAN_GET_BROADCASTFLAGS"; + //else if(message.dataLen() == sizeof(Z21::LanGetBroadcastFlagsReply)) + // s = "LAN_GET_BROADCASTFLAGS flags=0x" + to_hex(static_cast(message).broadcastFlags())); + else + raw = true; + break; + + case Z21::LAN_SET_BROADCASTFLAGS: + if(message.dataLen() == sizeof(Z21::LanSetBroadcastFlags)) + s = "LAN_SET_BROADCASTFLAGS flags=0x" + to_hex(static_cast(message).broadcastFlags()); + else + raw = true; + break; + + default: + raw = true; + break; + } + + if(raw) + { + s.append(" ["); + const uint8_t* bytes = reinterpret_cast(&message); + for(uint16_t i = sizeof(Message); i < message.dataLen(); i++) + { + if(i != sizeof(Message)) + s.append(" "); + s.append(to_hex(bytes[i])); + } + s.append("]"); + } + + return s; +} + +LanXLocoInfo::LanXLocoInfo(const Decoder& decoder) : + LanXLocoInfo() +{ + setAddress(decoder.address, decoder.longAddress); + setSpeedSteps(decoder.speedSteps); + setDirection(decoder.direction); + if(decoder.emergencyStop) + setEmergencyStop(); + else + setSpeedStep(decoder.speedStep); + for(auto function : *decoder.functions) + setFunction(function->number, function->value); + calcChecksum(); +} + +} \ No newline at end of file diff --git a/server/src/hardware/protocol/z21/messages.hpp b/server/src/hardware/protocol/z21/messages.hpp new file mode 100644 index 00000000..3146b997 --- /dev/null +++ b/server/src/hardware/protocol/z21/messages.hpp @@ -0,0 +1,973 @@ +/** + * server/src/hardware/protocol/z21/messages.hpp + * + * This file is part of the traintastic source code. + * + * Copyright (C) 2019-2020 Reinder Feenstra + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_Z21_MESSAGES_HPP +#define TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_Z21_MESSAGES_HPP + +#include +#include +#include +#include +#include "utils.hpp" +#include "../../../utils/packed.hpp" +#include "../../../utils/endian.hpp" + +class Decoder; + +namespace Z21 { + +struct Message; + +std::string to_string(const Message& message, bool raw = false); + +enum Header : uint16_t +{ + LAN_GET_SERIAL_NUMBER = 0x10, + LAN_GET_HWINFO = 0x1A, + LAN_LOGOFF = 0x30, + LAN_X = 0x40, + LAN_SET_BROADCASTFLAGS = 0x50, + LAN_GET_BROADCASTFLAGS = 0x51, + LAN_GET_LOCO_MODE = 0x60, + LAN_SET_LOCO_MODE = 0x61, + LAN_SYSTEMSTATE_DATACHANGED = 0x84, + LAN_SYSTEMSTATE_GETDATA = 0x85, + LAN_LOCONET_Z21_RX = 0xA0, + LAN_LOCONET_Z21_TX = 0xA1, +}; + +enum BroadcastFlags : uint32_t +{ + /** + * Broadcasts and info messages concerning driving and switching are delivered to the registered clients automatically. + * The following messages are concerned: + * 2.7 LAN_X_BC_TRACK_POWER_OFF + * 2.8 LAN_X_BC_TRACK_POWER_ON + * 2.9 LAN_X_BC_PROGRAMMING_MODE + * 2.10 LAN_X_BC_TRACK_SHORT_CIRCUIT + * 2.14 LAN_X_BC_STOPPED + * 4.4 LAN_X_LOCO_INFO (loco address must be subscribed too) + * 5.3 LAN_X_TURNOUT_INFO + */ + PowerLocoTurnout = 0x00000001, +}; + +enum LocoMode : uint8_t +{ + DCC = 0, + Motorola = 1, +}; + +static constexpr uint8_t LAN_X_SET_STOP = 0x80; +//static constexpr uint8_t LAN_X_TURNOUT_INFO = 0x43; +static constexpr uint8_t LAN_X_BC = 0x61; +static constexpr uint8_t LAN_X_BC_TRACK_POWER_OFF = 0x00; +static constexpr uint8_t LAN_X_BC_TRACK_POWER_ON = 0x01; +//static constexpr uint8_t LAN_X_BC_PROGRAMMING_MODE = 0x02; +//static constexpr uint8_t LAN_X_BC_TRACK_SHORT_CIRCUIT = 0x08; +//static constexpr uint8_t LAN_X_CV_NACK_SC = 0x12; +//static constexpr uint8_t LAN_X_CV_NACK = 0x13; +//static constexpr uint8_t LAN_X_UNKNOWN_COMMAND = 0x82; +static constexpr uint8_t LAN_X_BC_STOPPED = 0x81; +static constexpr uint8_t LAN_X_LOCO_INFO = 0xEF; + +enum HardwareType : uint32_t +{ + 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) + HWT_Z21_SMALL = 0x00000203, //!< „white z21” starter set variant (from 2013) + HWT_Z21_START = 0x00000204, //!< „z21 start” starter set variant (from 2016) +}; + +#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 +#define Z21_CENTRALSTATE_PROGRAMMINGMODEACTIVE 0x20 //!< The programming mode is active + +#define Z21_CENTRALSTATEEX_HIGHTEMPERATURE 0x01 //!< Temperature too high +#define Z21_CENTRALSTATEEX_POWERLOST 0x02 //!< Input voltage too low +#define Z21_CENTRALSTATEEX_SHORTCIRCUITEXTERNAL 0x04 //!< Short circuit at the external booster output +#define Z21_CENTRALSTATEEX_SHORTCIRCUITINTERNAL 0x08 //!< Short circuit at the main track or programming track + +PRAGMA_PACK_PUSH_1 + +struct Message +{ + uint16_t dataLenLE; //!< DataLen (little endian): Total length over the entire data set including DataLen, Header and Data, i.e. DataLen = 2+2+n. + Header headerLE; //!< Header (little endian): Describes the Command and the Protocol’s group. \see Header + + Message(uint16_t _dataLen, Header _header) : + dataLenLE{host_to_le(_dataLen)}, + headerLE{host_to_le(_header)} + { + } + + inline uint16_t dataLen() const + { + return le_to_host(dataLenLE); + } + + inline Header header() const + { + return static_cast
(le_to_host(headerLE)); + } +} ATTRIBUTE_PACKED; +static_assert(sizeof(Message) == 4); + +struct LanX : Message +{ + uint8_t xheader; + + LanX(uint16_t _dataLen, uint8_t _xheader) : + Message(_dataLen, LAN_X), + xheader{_xheader} + { + } + + void calcChecksum(uint8_t len) + { + uint8_t* checksum = &xheader + len + 1; + *checksum = xheader; + for(uint8_t* db = &xheader + 1; db < checksum; db++) + *checksum ^= *db; + } + + inline void calcChecksum() + { + calcChecksum(xheader & 0x0F); + } +} ATTRIBUTE_PACKED; +static_assert(sizeof(LanX) == 5); + +//============================================================================= +// Client to Z21 + +// LAN_GET_SERIAL_NUMBER +struct LanGetSerialNumber : Message +{ + LanGetSerialNumber() : + Message(sizeof(LanGetSerialNumber), LAN_GET_SERIAL_NUMBER) + { + } +} ATTRIBUTE_PACKED; +static_assert(sizeof(LanGetSerialNumber) == 4); + +// LAN_GET_CODE + +// LAN_GET_HWINFO +struct LanGetHardwareInfo : Message +{ + LanGetHardwareInfo() : + Message(sizeof(LanGetHardwareInfo), LAN_GET_HWINFO) + { + } +} ATTRIBUTE_PACKED; +static_assert(sizeof(LanGetHardwareInfo) == 4); + +// LAN_LOGOFF +struct LanLogoff : Message +{ + LanLogoff() : + Message(sizeof(LanLogoff), LAN_LOGOFF) + { + } +} ATTRIBUTE_PACKED; +static_assert(sizeof(LanLogoff) == 4); + +// LAN_X_GET_VERSION +struct LanXGetFirmwareVersion : LanX +{ + uint8_t db0 = 0x0A; + uint8_t checksum = 0xFB; + + LanXGetFirmwareVersion() : + LanX(sizeof(LanXGetFirmwareVersion), 0xF1) + { + } +} ATTRIBUTE_PACKED; +static_assert(sizeof(LanXGetFirmwareVersion) == 7); + +// LAN_X_GET_STATUS +struct LanXGetStatus : LanX +{ + uint8_t db0 = 0x24; + uint8_t checksum = 0x05; + + LanXGetStatus() : + LanX(sizeof(LanXGetStatus), 0x21) + { + } +}; +static_assert(sizeof(LanXGetStatus) == 7); + +// LAN_X_SET_TRACK_POWER_OFF +struct LanXSetTrackPowerOff : LanX +{ + uint8_t db0 = 0x80; + uint8_t checksum = 0xa1; + + LanXSetTrackPowerOff() : + LanX(sizeof(LanXSetTrackPowerOff), 0x21) + { + } +} ATTRIBUTE_PACKED; +static_assert(sizeof(LanXSetTrackPowerOff) == 7); + +// LAN_X_SET_TRACK_POWER_ON +struct LanXSetTrackPowerOn : LanX +{ + uint8_t db0 = 0x81; + uint8_t checksum = 0xa0; + + LanXSetTrackPowerOn() : + LanX(sizeof(LanXSetTrackPowerOn), 0x21) + { + } +} ATTRIBUTE_PACKED; +static_assert(sizeof(LanXSetTrackPowerOn) == 7); + +// LAN_X_DCC_READ_REGISTER + +// LAN_X_CV_READ + +// LAN_X_DCC_WRITE_REGISTER + +// LAN_X_CV_WRITE + +// LAN_X_MWRITE_BYTE + +// LAN_X_GET_TURNOUT_INFO + +// LAN_X_SET_TURNOUT + +// LAN_X_SET_STOP +struct LanXSetStop : LanX +{ + uint8_t checksum = 0x80; + + LanXSetStop() : + LanX(sizeof(LanXSetStop), LAN_X_SET_STOP) + { + } +} ATTRIBUTE_PACKED; +static_assert(sizeof(LanXSetStop) == 6); + +// LAN_X_GET_LOCO_INFO +struct LanXGetLocoInfo : LanX +{ + uint8_t db0 = 0xF0; + uint8_t addressHigh; + uint8_t addressLow; + uint8_t checksum; + + LanXGetLocoInfo(uint16_t address, bool longAddress) : + LanX(sizeof(LanXGetLocoInfo), 0xE3) + { + setAddress(address, longAddress); + calcChecksum(); + } + + inline uint16_t address() const + { + return (static_cast(addressHigh & 0x3F) << 8) | addressLow; + } + + inline bool isLongAddress() const + { + 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 void calcChecksum() + { + checksum = xheader ^ db0 ^ addressHigh ^ addressLow; + }*/ +} ATTRIBUTE_PACKED; +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; + uint8_t speedAndDirection = 0; + uint8_t checksum; + + LanXSetLocoDrive() : + LanX(sizeof(LanXSetLocoDrive), 0xE4) + { + } + + inline uint16_t address() const + { + return (static_cast(addressHigh & 0x3F) << 8) | addressLow; + } + + inline bool isLongAddress() const + { + return (addressHigh & 0xC0) == 0xC0; + } + + inline uint8_t speedSteps() const + { + switch(db0 & 0x0F) + { + case 0: return 14; + case 2: return 28; + case 3: return 126; + default: return 0; + } + } + + inline Direction direction() const + { + return Utils::getDirection(speedAndDirection); + } + + inline void setDirection(Direction value) + { + Utils::setDirection(speedAndDirection, value); + } + + inline bool isEmergencyStop() const + { + return Utils::isEmergencyStop(speedAndDirection, speedSteps()); + } + + inline void setEmergencyStop() + { + Utils::setEmergencyStop(speedAndDirection); + } + + inline uint8_t speedStep() const + { + return Utils::getSpeedStep(speedAndDirection, speedSteps()); + } + + inline void setSpeedStep(uint8_t value) + { + Utils::setSpeedStep(speedAndDirection, speedSteps(), value); + } +} ATTRIBUTE_PACKED; +static_assert(sizeof(LanXSetLocoDrive) == 10); + +// LAN_X_SET_LOCO_FUNCTION +struct LanXSetLocoFunction : LanX +{ + enum class SwitchType + { + Off = 0, + On = 1, + Toggle = 2, + Invalid = 3, + }; + + uint8_t db0 = 0xf8; + uint8_t addressHigh; + uint8_t addressLow; + uint8_t db3; + uint8_t checksum; + + LanXSetLocoFunction() : + LanX(sizeof(LanXSetLocoFunction), 0xE4) + { + } + + inline uint16_t address() const + { + return (static_cast(addressHigh & 0x3F) << 8) | addressLow; + } + + inline bool isLongAddress() const + { + return (addressHigh & 0xC0) == 0xC0; + } + + inline SwitchType switchType() const + { + return static_cast(db3 >> 6); + } + + inline uint8_t functionIndex() const + { + return db3 & 0x3F; + } +} ATTRIBUTE_PACKED; +static_assert(sizeof(LanXSetLocoFunction) == 10); + +// LAN_X_CV_POWRITE_BYTE + +// LAN_X_CV_POWRITE_BIT + +// LAN_X_CV_POREAD_BYTE + +// LAN_X_CV_POACCESSORY_WRITE_BYTE + +// LAN_X_CV_PO ACCESSORY_WRITE_BIT + +// LAN_X_CV_PO ACCESSORY_READ_BYTE + +// LAN_X_GET_FIRMWARE_VERSION + +// LAN_SET_BROADCASTFLAGS +struct LanSetBroadcastFlags : Message +{ + BroadcastFlags broadcastFlagsLE; // LE + + LanSetBroadcastFlags(uint32_t _broadcastFlags = 0) : + Message(sizeof(LanSetBroadcastFlags), LAN_SET_BROADCASTFLAGS), + broadcastFlagsLE{host_to_le(_broadcastFlags)} + { + } + + inline BroadcastFlags broadcastFlags() const + { + return le_to_host(broadcastFlagsLE); + } + +} ATTRIBUTE_PACKED; +static_assert(sizeof(LanSetBroadcastFlags) == 8); + +// LAN_GET_BROADCASTFLAGS +struct LanGetBroadcastFlags : Message +{ + LanGetBroadcastFlags() : + Message(sizeof(LanGetBroadcastFlags), LAN_GET_BROADCASTFLAGS) + { + } +} ATTRIBUTE_PACKED; +static_assert(sizeof(LanGetBroadcastFlags) == 4); + +// LAN_GET_LOCOMODE +struct LanGetLocoMode : Message +{ + uint16_t addressBE; // BE + + LanGetLocoMode(uint16_t _address = 0) : + Message(sizeof(LanGetLocoMode), LAN_GET_LOCO_MODE) + { + setAddress(_address); + } + + inline uint16_t address() const + { + return be_to_host(addressBE); + } + + inline void setAddress(uint16_t value) + { + addressBE = host_to_be(value); + } +} ATTRIBUTE_PACKED; +static_assert(sizeof(LanGetLocoMode) == 6); + +// LAN_SET_LOCOMODE +struct LanSetLocoMode : Message +{ + uint16_t addressBE; + LocoMode mode; + + LanSetLocoMode(uint16_t _address, LocoMode _mode) : + Message(sizeof(LanSetLocoMode), LAN_SET_LOCO_MODE), + addressBE{host_to_be(_address)}, + mode{_mode} + { + } + + inline uint16_t address() const + { + return be_to_host(addressBE); + } + + inline void setAddress(uint16_t value) + { + addressBE = host_to_be(value); + } +} ATTRIBUTE_PACKED; +static_assert(sizeof(LanSetLocoMode) == 7); + +// LAN_GET_TURNOUTMODE + +// LAN_SET_TURNOUTMODE + +// LAN_RMBUS_GETDATA + +// LAN_RMBUS_PROGRAMMODULE + +// LAN_SYSTEMSTATE_GETDATA +struct LanSystemStateGetData : Message +{ + LanSystemStateGetData() : + Message(sizeof(LanSystemStateGetData), LAN_SYSTEMSTATE_GETDATA) + { + } +} ATTRIBUTE_PACKED; +static_assert(sizeof(LanSystemStateGetData) == 4); + +// LAN_RAILCOGETDATA + +// LAN_LOCONET_FROLAN + +// LAN_LOCONET_DISPATCH_ADDR + +// LAN_LOCONET_DETECTOR + +// LAN_CAN_DETECTOR + +//============================================================================= +// Z21 to Client: + +// Reply to LAN_GET_SERIAL_NUMBER +struct LanGetSerialNumberReply : Message +{ + uint32_t serialNumberLE; + + LanGetSerialNumberReply(uint32_t _serialNumber) : + Message(sizeof(LanGetSerialNumberReply), LAN_GET_SERIAL_NUMBER) + { + setSerialNumber(_serialNumber); + } + + inline uint32_t serialNumber() const + { + return le_to_host(serialNumberLE); + } + + inline void setSerialNumber(uint32_t value) + { + serialNumberLE = host_to_le(value); + } +} ATTRIBUTE_PACKED; +static_assert(sizeof(LanGetSerialNumberReply) == 8); + +// Reply to LAN_GET_CODE +struct LanXGetFirmwareVersionReply : LanX +{ + uint8_t db0 = 0x0A; + uint8_t majorBCD; + uint8_t minorBCD; + uint8_t checksum; + + LanXGetFirmwareVersionReply() : + LanX(sizeof(LanXGetFirmwareVersionReply), 0xF3) + { + } + + LanXGetFirmwareVersionReply(uint8_t _major, uint8_t _minor) : + LanXGetFirmwareVersionReply() + { + setVersionMajor(_major); + setVersionMinor(_minor); + calcChecksum(); + } + + inline uint8_t versionMajor() const + { + return Utils::fromBCD(majorBCD); + } + + inline uint8_t versionMinor() const + { + return Utils::fromBCD(minorBCD); + } + + inline void setVersionMajor(uint8_t value) + { + assert(value < 100); + majorBCD = Utils::toBCD(value); + } + + inline void setVersionMinor(uint8_t value) + { + assert(value < 100); + minorBCD = Utils::toBCD(value); + } +}; +static_assert(sizeof(LanXGetFirmwareVersionReply) == 9); + +// Reply to LAN_GET_HWINFO +struct LanGetHardwareInfoReply : Message +{ + HardwareType hardwareTypeLE; + uint32_t firmwareVersionLE; + + LanGetHardwareInfoReply(HardwareType _hardwareType, uint8_t _firmwareVersionMajor, uint8_t _firmwareVersionMinor) : + Message(sizeof(LanGetHardwareInfoReply), LAN_GET_HWINFO), + hardwareTypeLE{host_to_le(_hardwareType)}, + firmwareVersionLE{host_to_le(static_cast(Z21::Utils::toBCD(_firmwareVersionMajor)) << 8 | Z21::Utils::toBCD(_firmwareVersionMinor))} + { + } + + HardwareType hardwareType() const + { + return le_to_host(hardwareTypeLE); + } + + uint8_t firmwareVersionMajor() const + { + return Utils::fromBCD((le_to_host(firmwareVersionLE) >> 8) && 0xFF); + } + + uint8_t firmwareVersionMinor() const + { + return Utils::fromBCD(le_to_host(firmwareVersionLE) && 0xFF); + } +} ATTRIBUTE_PACKED; +static_assert(sizeof(LanGetHardwareInfoReply) == 12); + +// LAN_X_TURNOUT_INFO + +// LAN_X_BC_TRACK_POWER_OFF +struct LanXBCTrackPowerOff : LanX +{ + uint8_t db0 = LAN_X_BC_TRACK_POWER_OFF; + uint8_t checksum = 0x61; + + LanXBCTrackPowerOff() : + LanX(sizeof(LanXBCTrackPowerOff), LAN_X_BC) + { + } +}; +static_assert(sizeof(LanXBCTrackPowerOff) == 7); + +// LAN_X_BC_TRACK_POWER_ON +struct LanXBCTrackPowerOn : LanX +{ + uint8_t db0 = LAN_X_BC_TRACK_POWER_ON; + uint8_t checksum = 0x60; + + LanXBCTrackPowerOn() : + LanX(sizeof(LanXBCTrackPowerOn), LAN_X_BC) + { + } +}; +static_assert(sizeof(LanXBCTrackPowerOn) == 7); + +// LAN_X_BC_PROGRAMMING_MODE + +// LAN_X_BC_TRACK_SHORT_CIRCUIT + +// LAN_X_CV_NACK_SC + +// LAN_X_CV_NACK + +// LAN_X_UNKNOWN_COMMAND + +// LAN_X_STATUS_CHANGED +struct LanXStatusChanged : LanX +{ + uint8_t db0 = 0x22; + uint8_t db1 = 0; + uint8_t checksum; + + LanXStatusChanged() : + LanX(sizeof(LanXStatusChanged), 0x62) + { + } +}; +static_assert(sizeof(LanXStatusChanged) == 8); + +// Reply to LAN_X_GET_VERSION + +// LAN_X_CV_RESULT + +// LAN_X_BC_STOPPED +struct LanXBCStopped : LanX +{ + uint8_t db0 = 0x00; + uint8_t checksum = 0x80; + + LanXBCStopped() : + LanX(sizeof(LanXBCStopped), LAN_X_BC_STOPPED) + { + } +}; +static_assert(sizeof(LanXBCStopped) == 7); + +// LAN_X_LOCO_INFO +struct LanXLocoInfo : LanX +{ + static constexpr uint8_t db2_busy_flag = 0x08; + static constexpr uint8_t db2_speed_steps_14 = 0x00; + static constexpr uint8_t db2_speed_steps_28 = 0x02; + static constexpr uint8_t db2_speed_steps_128 = 0x04; + static constexpr uint8_t db2_speed_steps_mask = 0x07; + static constexpr uint8_t directionFlag = 0x80; + static constexpr uint8_t speedStepMask = 0x7F; + static constexpr uint8_t flagF0 = 0x10; + + uint8_t addressHigh = 0; + uint8_t addressLow = 0; + uint8_t db2 = 0; + uint8_t speedAndDirection = 0; + uint8_t db4 = 0; + uint8_t f5f12 = 0; + uint8_t f13f20 = 0; + uint8_t f21f28 = 0; + uint8_t checksum = 0; + + LanXLocoInfo() : + LanX(sizeof(LanXLocoInfo), LAN_X_LOCO_INFO) + { + } + + LanXLocoInfo(const Decoder& decoder); + + inline uint16_t address() const + { + return (static_cast(addressHigh & 0x3F) << 8) | addressLow; + } + + inline bool isLongAddress() const + { + 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 bool isBusy() const + { + return db2 & db2_busy_flag; + } + + inline void setBusy(bool value) + { + if(value) + db2 |= db2_busy_flag; + else + db2 &= ~db2_busy_flag; + } + + inline uint8_t speedSteps() const + { + switch(db2 & db2_speed_steps_mask) + { + case db2_speed_steps_14: return 14; + case db2_speed_steps_28: return 28; + case db2_speed_steps_128: return 126; + } + return 0; + } + + inline void setSpeedSteps(uint8_t value) + { + db2 &= ~db2_speed_steps_mask; + switch(value) + { + case 14: db2 |= db2_speed_steps_14; break; + case 28: db2 |= db2_speed_steps_28; break; + case 126: db2 |= db2_speed_steps_128; break; + } + } + + inline Direction direction() const + { + return Z21::Utils::getDirection(speedAndDirection); + } + + inline void setDirection(Direction value) + { + Z21::Utils::setDirection(speedAndDirection, value); + } + + inline bool isEmergencyStop() const + { + return Z21::Utils::isEmergencyStop(speedAndDirection, speedSteps()); + } + + inline void setEmergencyStop() + { + Z21::Utils::setEmergencyStop(speedAndDirection); + } + + inline uint8_t speedStep() const + { + return Z21::Utils::getSpeedStep(speedAndDirection, speedSteps()); + } + + inline void setSpeedStep(uint8_t value) + { + Z21::Utils::setSpeedStep(speedAndDirection, speedSteps(), value); + } + + bool getFunction(uint8_t index) + { + if(index == 0) + return db4 & flagF0; + else if(index <= 4) + return db4 & (1 << (index - 1)); + else if(index <= 12) + return f5f12 & (1 << (index - 5)); + else if(index <= 20) + return f13f20 & (1 << (index - 13)); + else if(index <= 28) + return f21f28 & (1 << (index - 21)); + else + return false; + } + + void setFunction(uint8_t index, bool value) + { + if(index == 0) + { + if(value) + db4 |= flagF0; + else + db4 &= ~flagF0; + } + else if(index <= 4) + { + const uint8_t flag = (1 << (index - 1)); + if(value) + db4 |= flag; + else + db4 &= ~flag; + } + else if(index <= 12) + { + const uint8_t flag = (1 << (index - 5)); + if(value) + f5f12 |= flag; + else + f5f12 &= ~flag; + } + else if(index <= 20) + { + const uint8_t flag = (1 << (index - 13)); + if(value) + f13f20 |= flag; + else + f13f20 &= ~flag; + } + else if(index <= 28) + { + const uint8_t flag = (1 << (index - 21)); + if(value) + f21f28 |= flag; + else + f21f28 &= ~flag; + } + } + + void calcChecksum() + { + checksum = xheader; + for(uint8_t* db = &addressHigh; db < &checksum; db++) + checksum ^= *db; + } +} ATTRIBUTE_PACKED; +static_assert(sizeof(LanXLocoInfo) == 14); + +// Reply to LAN_X_GET_FIRMWARE_VERSION + +// Reply to LAN_GET_BROADCASTFLAGS + +// Reply to LAN_GET_LOCOMODE +struct LanGetLocoModeReply : Message +{ + uint16_t addressBE; + LocoMode mode; + + LanGetLocoModeReply(uint16_t _address, LocoMode _mode) : + Message(sizeof(LanGetLocoModeReply), LAN_GET_LOCO_MODE), + addressBE{host_to_be(_address)}, + mode{_mode} + { + } + + inline uint16_t address() const + { + return be_to_host(addressBE); + } + + inline void setAddress(uint16_t value) + { + addressBE = host_to_be(value); + } +} ATTRIBUTE_PACKED; +static_assert(sizeof(LanGetLocoModeReply) == 7); + +// Reply to LAN_GET_TURNOUTMODE + +// LAN_RMBUS_DATACHANGED + +// LAN_SYSTEMSTATE_DATACHANGED +struct LanSystemStateDataChanged : Message +{ + int16_t mainCurrent= 0; //!< Current on the main track in mA + int16_t progCurrent = 0; //!< Current on programming track in mA; + int16_t filteredMainCurrent = 0; //!< Smoothed current on the main track in mA + int16_t temperature = 0; //!< Command station internal temperature in °C + uint16_t supplyVoltage = 0; //!< Supply voltage in mV + uint16_t vccVoltage = 0; //!< Internal voltage, identical to track voltage in mV + uint8_t centralState = 0; //!< bitmask, see Z21_CENTRALSTATE + uint8_t centralStateEx = 0; //!< bitmask, see Z21_CENTRALSTATEEX + uint8_t _reserved1 = 0; + uint8_t _reserved2 = 0; + + LanSystemStateDataChanged() : + Message(sizeof(LanSystemStateDataChanged), LAN_SYSTEMSTATE_DATACHANGED) + { + } +} ATTRIBUTE_PACKED; +static_assert(sizeof(LanSystemStateDataChanged) == 20); + +// LAN_RAILCODATACHANGED + +// LAN_LOCONET_Z21_RX + +// LAN_LOCONET_Z21_TX + +// LAN_LOCONET_FROLAN + +// LAN_LOCONET_DISPATCH_ADDR + +// LAN_LOCONET_DETECTOR + +// LAN_CAN_DETECTOR + +//============================================================================= + +PRAGMA_PACK_POP + +} + +inline bool operator ==(const Z21::Message& lhs, const Z21::Message& rhs) +{ + return lhs.dataLen() == rhs.dataLen() && std::memcmp(&lhs, &rhs, lhs.dataLen()) == 0; +} + +#endif diff --git a/server/src/hardware/protocol/z21/utils.hpp b/server/src/hardware/protocol/z21/utils.hpp new file mode 100644 index 00000000..d033cc16 --- /dev/null +++ b/server/src/hardware/protocol/z21/utils.hpp @@ -0,0 +1,118 @@ +/** + * server/src/hardware/protocol/z21/utils.hpp + * + * This file is part of the traintastic source code. + * + * Copyright (C) 2019-2020 Reinder Feenstra + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_Z21_UTILS_HPP +#define TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_Z21_UTILS_HPP + +namespace Z21::Utils { + +inline constexpr uint8_t directionFlag = 0x80; + +constexpr Direction getDirection(uint8_t db) +{ + return (db & directionFlag) ? Direction::Forward : Direction::Reverse; +} + +constexpr void setDirection(uint8_t& db, Direction direction) +{ + if(direction == Direction::Forward) + db |= directionFlag; + else + db &= ~directionFlag; +} + +constexpr bool isEmergencyStop(uint8_t db, uint8_t speedSteps) +{ + switch(speedSteps) + { + case 126: + return (db & 0x7F) == 0x01; + + case 28: + return (db & 0x1F) == 0x01 || (db & 0x1F) == 0x11; + + case 14: + return (db & 0x0F) == 0x01; + } + return true; +} + +constexpr void setEmergencyStop(uint8_t& db) +{ + db = (db & directionFlag) | 0x01; +} + +constexpr uint8_t getSpeedStep(uint8_t db, uint8_t speedSteps) +{ + switch(speedSteps) + { + case 126: + db &= 0x7F; + break; + + case 28: + db = ((db & 0x0F) << 1) | ((db & 0x10) >> 4); + break; + + case 14: + db &= 0x0F; + break; + + default: + return 0; + } + return db > 1 ? db - 1 : 0; // step 1 = EStop +} + +constexpr void setSpeedStep(uint8_t& db, uint8_t speedSteps, uint8_t speedStep) +{ + db &= directionFlag; // preserve direction flag + if(++speedStep > 1) + switch(speedSteps) + { + case 126: + db |= speedStep & 0x7F; + break; + + case 28: + db |= ((speedStep >> 1) & 0x0F) | ((speedStep << 4) & 0x01); + break; + + case 14: + db |= speedStep & 0x0F; + break; + } +} + +constexpr uint8_t toBCD(uint8_t value) +{ + return ((value / 10) << 4) | (value % 10); +} + +constexpr uint8_t fromBCD(uint8_t value) +{ + return ((value >> 4) * 10) + (value & 0x0F); +} + +} + +#endif