WIP: refactor Z21 protocol
Dieser Commit ist enthalten in:
Ursprung
b42a376cef
Commit
5974f8f037
@ -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"
|
||||
|
||||
@ -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()); });
|
||||
});*/
|
||||
}
|
||||
|
||||
@ -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));
|
||||
|
||||
|
||||
@ -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);
|
||||
};
|
||||
|
||||
@ -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
143
server/src/hardware/protocol/z21/messages.cpp
Normale Datei
143
server/src/hardware/protocol/z21/messages.cpp
Normale Datei
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
973
server/src/hardware/protocol/z21/messages.hpp
Normale Datei
973
server/src/hardware/protocol/z21/messages.hpp
Normale Datei
@ -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 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<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
|
||||
118
server/src/hardware/protocol/z21/utils.hpp
Normale Datei
118
server/src/hardware/protocol/z21/utils.hpp
Normale Datei
@ -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
|
||||
Laden…
x
In neuem Issue referenzieren
Einen Benutzer sperren