385 lines
10 KiB
C++
385 lines
10 KiB
C++
#include "z21server.h"
|
|
|
|
#include <QUdpSocket>
|
|
|
|
#include "z21library/z21.h"
|
|
|
|
#include "server/retroaction/rbusretroaction.h"
|
|
#include "server/accessories/accessorymanager.h"
|
|
#include "server/loco/locomanager.h"
|
|
|
|
#include <iostream> //console debugging
|
|
|
|
namespace Z21 {
|
|
|
|
//Defined in z21server_constants.h
|
|
QString getPowerStateName(PowerState state)
|
|
{
|
|
switch (state)
|
|
{
|
|
case PowerState::Normal:
|
|
return QLatin1String("Normal");
|
|
case PowerState::EmergencyStop:
|
|
return QLatin1String("Emergency Stop");
|
|
case PowerState::TrackVoltageOff:
|
|
return QLatin1String("Track Voltage Off");
|
|
case PowerState::ShortCircuit:
|
|
return QLatin1String("Short Circuit");
|
|
case PowerState::ServiceMode:
|
|
return QLatin1String("Service Mode");
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return QString();
|
|
}
|
|
|
|
}
|
|
|
|
//Callbacks from Z21 Library
|
|
|
|
static Z21Server *m_instance = nullptr; //Used by callbacks
|
|
|
|
extern "C" void notifyz21getSystemInfo(uint8_t client)
|
|
{
|
|
if(!m_instance)
|
|
return;
|
|
|
|
auto power = m_instance->getPower();
|
|
|
|
//Report fake values
|
|
uint16_t maincurrent = 15; //Ampere
|
|
uint16_t mainvoltage = 15; //Volts
|
|
uint16_t temp = 15; //Celsius degrees
|
|
if(power == Z21::PowerState::TrackVoltageOff || power == Z21::PowerState::ShortCircuit)
|
|
{
|
|
maincurrent = 0;
|
|
mainvoltage = 0;
|
|
}
|
|
|
|
m_instance->m_z21->sendSystemInfo(client, maincurrent, mainvoltage, temp);
|
|
}
|
|
|
|
extern "C" void notifyz21EthSend(uint8_t client, uint8_t *data)
|
|
{
|
|
if(m_instance)
|
|
m_instance->sendDatagram(client, reinterpret_cast<const char *>(data), data[0]);
|
|
}
|
|
|
|
extern "C" void notifyz21LNdetector(uint8_t client, uint8_t typ, uint16_t Adr)
|
|
{
|
|
if(!m_instance)
|
|
return;
|
|
|
|
if (typ == 0x80)
|
|
{
|
|
//"Stationary Interrogate Request" (SIC)
|
|
byte data[4];
|
|
data[0] = 0x01; //Typ
|
|
data[1] = Adr & 0xFF;
|
|
data[2] = Adr >> 8;
|
|
data[3] = m_instance->getAccessoryMgr()->getAccessoryState(Adr); //Condition of feedback
|
|
|
|
m_instance->m_z21->setLNDetector(client, data, 4);
|
|
}
|
|
}
|
|
//extern uint8_t notifyz21LNdispatch(uint16_t Adr) __attribute__((weak));
|
|
//extern void notifyz21LNSendPacket(uint8_t *data, uint8_t length) __attribute__((weak));
|
|
|
|
//extern void notifyz21CANdetector(uint8_t client, uint8_t typ, uint16_t ID) __attribute__((weak));
|
|
|
|
extern "C" void notifyz21RailPower(uint8_t State)
|
|
{
|
|
if(!m_instance)
|
|
return;
|
|
|
|
m_instance->setPower(Z21::PowerState(State));
|
|
}
|
|
|
|
/*
|
|
extern void notifyz21CVREAD(uint8_t cvAdrMSB, uint8_t cvAdrLSB) __attribute__((weak));
|
|
extern void notifyz21CVWRITE(uint8_t cvAdrMSB, uint8_t cvAdrLSB, uint8_t value) __attribute__((weak));
|
|
extern void notifyz21CVPOMWRITEBYTE(uint16_t Adr, uint16_t cvAdr, uint8_t value) __attribute__((weak));
|
|
extern void notifyz21CVPOMWRITEBIT(uint16_t Adr, uint16_t cvAdr, uint8_t value) __attribute__((weak));
|
|
extern void notifyz21CVPOMREADBYTE(uint16_t Adr, uint16_t cvAdr) __attribute__((weak));
|
|
extern void notifyz21CVPOMACCWRITEBYTE(uint16_t Adr, uint16_t cvAdr, uint8_t value) __attribute__((weak));
|
|
extern void notifyz21CVPOMACCWRITEBIT(uint16_t Adr, uint16_t cvAdr, uint8_t value) __attribute__((weak));
|
|
extern void notifyz21CVPOMACCREADBYTE(uint16_t Adr, uint16_t cvAdr) __attribute__((weak));
|
|
*/
|
|
|
|
extern "C" uint8_t notifyz21AccessoryInfo(uint16_t Adr)
|
|
{
|
|
if(!m_instance)
|
|
return 0;
|
|
|
|
return m_instance->getAccessoryMgr()->getAccessoryState(Adr);
|
|
}
|
|
|
|
extern "C" void notifyz21Accessory(uint16_t Adr, bool state, bool active)
|
|
{
|
|
Q_UNUSED(active) //TODO: What is the purpose of "active" flag?
|
|
if(!m_instance)
|
|
return;
|
|
|
|
m_instance->getAccessoryMgr()->setAccessoryState(Adr, state);
|
|
}
|
|
|
|
//extern void notifyz21ExtAccessory(uint16_t Adr, byte state) __attribute__((weak));
|
|
|
|
extern "C" void notifyz21LocoState(uint16_t Adr, uint8_t data[])
|
|
{
|
|
if(!m_instance)
|
|
return;
|
|
|
|
m_instance->getLocoMgr()->getLocoData(Adr, data);
|
|
}
|
|
|
|
extern "C" void notifyz21LocoFkt(uint16_t Adr, uint8_t type, uint8_t fkt)
|
|
{
|
|
if(!m_instance)
|
|
return;
|
|
|
|
m_instance->getLocoMgr()->setLocoFuncHelper(Adr, type, fkt);
|
|
}
|
|
|
|
extern "C" void notifyz21LocoFkt0to4(uint16_t Adr, uint8_t fkt)
|
|
{
|
|
if(!m_instance)
|
|
return;
|
|
|
|
auto locoMgr = m_instance->getLocoMgr();
|
|
uint8_t Slot = locoMgr->getSlotForAddress(Adr);
|
|
locoMgr->loco_slots[Slot].setFunctions0to4(fkt);
|
|
emit locoMgr->locoSlotChanged(Slot);
|
|
}
|
|
|
|
extern void notifyz21LocoFkt5to8(uint16_t Adr, uint8_t fkt)
|
|
{
|
|
if(!m_instance)
|
|
return;
|
|
|
|
auto locoMgr = m_instance->getLocoMgr();
|
|
uint8_t Slot = locoMgr->getSlotForAddress(Adr);
|
|
locoMgr->loco_slots[Slot].setFunctions5to8(fkt);
|
|
emit locoMgr->locoSlotChanged(Slot);
|
|
}
|
|
|
|
extern void notifyz21LocoFkt9to12(uint16_t Adr, uint8_t fkt)
|
|
{
|
|
if(!m_instance)
|
|
return;
|
|
|
|
auto locoMgr = m_instance->getLocoMgr();
|
|
uint8_t Slot = locoMgr->getSlotForAddress(Adr);
|
|
locoMgr->loco_slots[Slot].setFunctions9to12(fkt);
|
|
emit locoMgr->locoSlotChanged(Slot);
|
|
}
|
|
|
|
extern void notifyz21LocoFkt13to20(uint16_t Adr, uint8_t fkt)
|
|
{
|
|
if(!m_instance)
|
|
return;
|
|
|
|
auto locoMgr = m_instance->getLocoMgr();
|
|
uint8_t Slot = locoMgr->getSlotForAddress(Adr);
|
|
locoMgr->loco_slots[Slot].setFunctions13to20(fkt);
|
|
emit locoMgr->locoSlotChanged(Slot);
|
|
}
|
|
|
|
extern void notifyz21LocoFkt21to28(uint16_t Adr, uint8_t fkt)
|
|
{
|
|
if(!m_instance)
|
|
return;
|
|
|
|
auto locoMgr = m_instance->getLocoMgr();
|
|
uint8_t Slot = locoMgr->getSlotForAddress(Adr);
|
|
locoMgr->loco_slots[Slot].setFunctions21to28(fkt);
|
|
emit locoMgr->locoSlotChanged(Slot);
|
|
}
|
|
|
|
extern void notifyz21LocoFkt29to36(uint16_t Adr, uint8_t fkt)
|
|
{
|
|
if(!m_instance)
|
|
return;
|
|
|
|
auto locoMgr = m_instance->getLocoMgr();
|
|
uint8_t Slot = locoMgr->getSlotForAddress(Adr);
|
|
locoMgr->loco_slots[Slot].setFunctions29to36(fkt);
|
|
emit locoMgr->locoSlotChanged(Slot);
|
|
}
|
|
|
|
//extern void notifyz21LocoFkt37to44(uint16_t Adr, uint8_t fkt) __attribute__((weak));
|
|
//extern void notifyz21LocoFkt45to52(uint16_t Adr, uint8_t fkt) __attribute__((weak));
|
|
//extern void notifyz21LocoFkt53to60(uint16_t Adr, uint8_t fkt) __attribute__((weak));
|
|
//extern void notifyz21LocoFkt61to68(uint16_t Adr, uint8_t fkt) __attribute__((weak));
|
|
//extern void notifyz21LocoFktExt(uint16_t Adr, uint8_t low, uint8_t high) __attribute__((weak));
|
|
|
|
extern "C" void notifyz21LocoSpeed(uint16_t Adr, uint8_t speed, uint8_t steps)
|
|
{
|
|
if(!m_instance)
|
|
return;
|
|
|
|
auto locoMgr = m_instance->getLocoMgr();
|
|
|
|
switch (steps)
|
|
{
|
|
case 14: locoMgr->setSpeed14(Adr, speed); break;
|
|
case 28: locoMgr->setSpeed28(Adr, speed); break;
|
|
default: locoMgr->setSpeed128(Adr, speed);
|
|
}
|
|
}
|
|
|
|
extern "C" void notifyz21S88Data(uint8_t group)
|
|
{
|
|
if(!m_instance)
|
|
return;
|
|
|
|
m_instance->getRBUS()->sendS88Status(group);
|
|
}
|
|
|
|
//extern uint16_t notifyz21Railcom() __attribute__((weak)); //return global Railcom Adr
|
|
|
|
//extern void notifyz21UpdateConf() __attribute__((weak)); //information for DCC via EEPROM (RailCom, ProgMode,...)
|
|
|
|
extern "C" uint8_t notifyz21ClientHash(uint8_t client)
|
|
{
|
|
if(!m_instance)
|
|
return 0;
|
|
return m_instance->getClientHash(client);
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------
|
|
// Z21Server
|
|
|
|
Z21Server::Z21Server(QObject *parent) :
|
|
QObject{parent}
|
|
{
|
|
Q_ASSERT_X(!m_instance, "Z21Server", "Only one instance allowed");
|
|
m_instance = this;
|
|
|
|
m_z21 = new z21Class;
|
|
m_RBUS = new RBusRetroaction(this);
|
|
m_accessoryMgr = new AccessoryManager(this);
|
|
m_locoMgr = new LocoManager(this);
|
|
|
|
m_server = new QUdpSocket(this);
|
|
connect(m_server, &QUdpSocket::readyRead, this, &Z21Server::readPendingDatagram);
|
|
}
|
|
|
|
Z21Server::~Z21Server()
|
|
{
|
|
delete m_z21;
|
|
m_server->close();
|
|
|
|
m_instance = nullptr;
|
|
}
|
|
|
|
bool Z21Server::startServer(quint16 port)
|
|
{
|
|
if(!m_server->bind(port))
|
|
return false;
|
|
|
|
setPower(Z21::PowerState::Normal);
|
|
return true;
|
|
}
|
|
|
|
void Z21Server::setPower(Z21::PowerState state)
|
|
{
|
|
if(state == getPower())
|
|
{
|
|
std::cerr << "Z21 POWER NOTIFY SAME: " << int(state) << std::endl << std::flush;
|
|
}
|
|
|
|
m_z21->setPower(byte(state));
|
|
emit powerStateChanged(int(state));
|
|
}
|
|
|
|
Z21::PowerState Z21Server::getPower() const
|
|
{
|
|
return Z21::PowerState(m_z21->getPower());
|
|
}
|
|
|
|
void Z21Server::readPendingDatagram()
|
|
{
|
|
while(m_server->hasPendingDatagrams())
|
|
{
|
|
//Receive datagram
|
|
char buffer[Z21::Z21_UDP_TX_MAX_SIZE] = {};
|
|
Client client;
|
|
m_server->readDatagram(buffer, Z21::Z21_UDP_TX_MAX_SIZE, &client.remoteAddr, &client.remotePort);
|
|
|
|
//Register client
|
|
int clientIdx = addClientAndGetIndex(client);
|
|
|
|
//Handle requests
|
|
m_z21->receive(clientIdx, reinterpret_cast<uint8_t *>(buffer));
|
|
}
|
|
}
|
|
|
|
int Z21Server::addClientAndGetIndex(const Client &client)
|
|
{
|
|
//If client is already registered, get it's index
|
|
int idx = m_clients.indexOf(client);
|
|
if(idx == -1)
|
|
{
|
|
//Register new client
|
|
idx = m_clients.size();
|
|
m_clients.append(client);
|
|
}
|
|
|
|
//Reserve 0 for "broadcast" by adding 1 to index
|
|
return idx + 1;
|
|
}
|
|
|
|
void Z21Server::sendDatagram(int clientIdx, const char *data, const qint64 size)
|
|
{
|
|
if (clientIdx == 0)
|
|
{
|
|
//Broadcast message
|
|
for(const Client& client : qAsConst(m_clients))
|
|
{
|
|
if(!client.remotePort || client.remoteAddr.isNull())
|
|
continue; //Skip invalid clients
|
|
|
|
m_server->writeDatagram(data, size, client.remoteAddr, client.remotePort);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const Client client = m_clients.value(clientIdx - 1);
|
|
if(!client.remotePort || client.remoteAddr.isNull())
|
|
return; //Skip invalid clients
|
|
|
|
m_server->writeDatagram(data, size, client.remoteAddr, client.remotePort);
|
|
}
|
|
}
|
|
|
|
quint8 Z21Server::getClientHash(int clientIdx)
|
|
{
|
|
const Client client = m_clients.value(clientIdx - 1);
|
|
if(!client.remotePort || client.remoteAddr.isNull())
|
|
return 0; //Skip invalid clients
|
|
|
|
quint8 fourBytes[4] = {};
|
|
*reinterpret_cast<quint32 *>(fourBytes) = client.remoteAddr.toIPv4Address();
|
|
|
|
quint8 HashIP = fourBytes[0] ^ fourBytes[1] ^ fourBytes[2] ^ fourBytes[3]; //make Hash from IP
|
|
return HashIP;
|
|
}
|
|
|
|
RBusRetroaction *Z21Server::getRBUS() const
|
|
{
|
|
return m_RBUS;
|
|
}
|
|
|
|
AccessoryManager *Z21Server::getAccessoryMgr() const
|
|
{
|
|
return m_accessoryMgr;
|
|
}
|
|
|
|
LocoManager *Z21Server::getLocoMgr() const
|
|
{
|
|
return m_locoMgr;
|
|
}
|