WIP: refactor Z21 protocol

Dieser Commit ist enthalten in:
Reinder Feenstra 2020-08-23 22:17:44 +02:00
Ursprung b42a376cef
Commit 5974f8f037
9 geänderte Dateien mit 1361 neuen und 1212 gelöschten Zeilen

Datei anzeigen

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

Datei anzeigen

@ -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>& 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<const XpressNet::Message*>(&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<uint8_t>(functionNumber);
cmd.checksum = XpressNet::calcChecksum(*reinterpret_cast<const XpressNet::Message*>(&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<const Z21::Message*>(m_receiveBuffer.data());
const z21_lan_header* cmd = reinterpret_cast<const z21_lan_header*>(m_receiveBuffer.data());
switch(cmd->header)
//const z21_lan_header* cmd = reinterpret_cast<const z21_lan_header*>(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<const z21_lan_get_hwinfo_reply*>(cmd);
const Z21::LanGetHardwareInfoReply* reply = static_cast<const Z21::LanGetHardwareInfoReply*>(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<const z21_lan_x*>(cmd)->xheader;
const uint8_t xheader = static_cast<const Z21::LanX*>(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<const z21_lan_x_loco_info*>(cmd);
const Z21::LanXLocoInfo* info = static_cast<const Z21::LanXLocoInfo*>(message);
const uint16_t address = (static_cast<uint16_t>(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<const z21_lan_systemstate_datachanged*>(m_receiveBuffer.data());
const Z21::LanSystemStateDataChanged state = *reinterpret_cast<const Z21::LanSystemStateDataChanged*>(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<const ::LocoNet::Message*>(m_receiveBuffer.data() + sizeof(z21_lan_header)));
loconet->receive(*reinterpret_cast<const ::LocoNet::Message*>(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<const uint8_t*>(cmd)[i]);
log += ", data=";
for(int i = sizeof(Z21::Message); i < message->dataLen(); i++)
log += to_hex(reinterpret_cast<const uint8_t*>(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()); });
});*/
}

Datei anzeigen

@ -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> 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<const Z21::Message*>(m_receiveBuffer.data());
/*[[deprecated]]*/ const z21_lan_header* cmd = reinterpret_cast<const z21_lan_header*>(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<const z21_lan_x*>(cmd)->xheader;
const uint8_t xheader = static_cast<const Z21::LanX*>(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<const z21_lan_x_get_loco_info*>(cmd);
if(const Z21::LanXGetLocoInfo* r = static_cast<const Z21::LanXGetLocoInfo*>(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<const z21_lan_x_set_loco_drive*>(cmd);
if(const Z21::LanXSetLocoDrive* r = static_cast<const Z21::LanXSetLocoDrive*>(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<const z21_lan_x_set_loco_function*>(cmd);
else if(const Z21::LanXSetLocoFunction* r = static_cast<const Z21::LanXSetLocoFunction*>(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<const z21_lan_set_broadcastflags*>(cmd), endpoint=m_receiveEndpoint]()
[this, broadcastFlags=static_cast<const Z21::LanSetBroadcastFlags*>(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<const uint8_t*>(cmd)[i]);
log += ", data=";
for(int i = sizeof(Z21::Message); i < message->dataLen(); i++)
log += to_hex(reinterpret_cast<const uint8_t*>(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));

Datei anzeigen

@ -49,6 +49,7 @@ class WLANmaus : public Controller
std::array<uint8_t,64> m_receiveBuffer;
std::map<boost::asio::ip::udp::endpoint, Client> 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<uint16_t> port;
Property<bool> debugLog;
WLANmaus(const std::weak_ptr<World> world, std::string_view _id);
};

Datei anzeigen

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

Datei-Diff unterdrückt, da er zu groß ist Diff laden

Datei anzeigen

@ -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<const LanX&>(message).xheader)
{
case 0x21:
if(message == LanXGetStatus())
s = "LAN_X_GET_STATUS";
else
raw = true;
break;
case 0x62:
if(const LanXStatusChanged& statusChanged = static_cast<const LanXStatusChanged&>(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<const LanGetBroadcastFlagsReply&>(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<const LanSetBroadcastFlags&>(message).broadcastFlags());
else
raw = true;
break;
default:
raw = true;
break;
}
if(raw)
{
s.append(" [");
const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&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();
}
}

Datei anzeigen

@ -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 <cstdint>
#include <cstring>
#include <string>
#include <traintastic/enum/direction.hpp>
#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 Protocols 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<Header>(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<uint16_t>(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<uint16_t>(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<uint16_t>(addressHigh & 0x3F) << 8) | addressLow;
}
inline bool isLongAddress() const
{
return (addressHigh & 0xC0) == 0xC0;
}
inline SwitchType switchType() const
{
return static_cast<SwitchType>(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<uint32_t>(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<uint16_t>(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

Datei anzeigen

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