You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Modelbahn/z21emu/z21prot.cc

269 lines
6.3 KiB

/*
* z21prot.cc
*
* Created on: 15.12.2017
* Author: steffen
*/
#include "config.h"
#include "z21prot.h"
#include "z21emu.h"
#include "debug.h"
Z21Client::Z21Client() {
// printf ("** %s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
};
Z21Client::~Z21Client() {
// printf ("** %s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
};
bool Z21Client::LoopData (UDPConnection *udp, char *buffer, size_t len) {
short unsigned int pktlen = le16toh(bufgetint16(buffer, 0));
short int pktheader = le16toh(bufgetint16(buffer, 2));
int i, k;
// LAN_GET_SERIAL_NUMBER
if (pktlen == 4 && pktheader == 0x10) {
bufsetint16 (outbuf, 0, htole16(8));
bufsetint16 (outbuf, 2, htole16(0x10));
bufsetint32 (outbuf, 4, htole32(55555953));
udp->Send(source, outbuf, 8);
}
// LAN_LOGOFF
else if (pktlen == 4 && pktheader == 0x0030) {
return false;
}
// LAN_X_ Commandos
else if (pktheader == 0x0040) {
if (pktlen > len) {
printf ("ERROR: got broken udp paket? pktlen(%d) > len(%ld)\n", pktlen, len);
} else {
Z21_lan_x_data xdata;
int pos = 4;
int chksum;
char db;
while (pos < pktlen) {
xdata.xheader = buffer[pos++];
for (i = 0; i < Z21_X_LEN && (unsigned int) i+pos < len; i++)
xdata.db[i] = buffer[pos+i];
// LAN_X_GET_VERSION
if (xdata.xheader == 0x21 &&
xdata.db[0] == 0x21 && xdata.db[1] == 0x00) {
printf ("LAN_X_GET_VERSION\n");
pos += 2;
bufsetint16 (outbuf, 0, htole16(9));
bufsetint16 (outbuf, 2, htole16(0x40));
k = 4;
bufsetint8 (outbuf, k++, db=0x63); // XHeader
chksum = db;
bufsetint8 (outbuf, k++, db=0x21); // DB0
chksum = chksum ^ db;
bufsetint8 (outbuf, k++, db=0x30); // DB1
chksum = chksum ^ db;
bufsetint8 (outbuf, k++, db=0x12); // DB2
chksum = chksum ^ db;
bufsetint8 (outbuf, k++, chksum); // XOR-Byte
// debug_mem(outbuf, k);
udp->Send(source, outbuf, k);
}
// LAN_X_GET_FIRMWARE_VERSION
else if (xdata.xheader == 0xF1 &&
xdata.db[0] == 0x0A && xdata.db[1] == 0xFB) {
printf ("LAN_X_GET_FIRMWARE_VERSION\n");
pos += 3;
bufsetint16 (outbuf, 0, htole16(9)); // LEN
bufsetint16 (outbuf, 2, htole16(0x40)); // X_CMD
k = 4;
bufsetint8 (outbuf, k++, db=0xF3); // XHeader
chksum = db;
bufsetint8 (outbuf, k++, db=0x0A); // DB0
chksum = chksum ^ db;
bufsetint8 (outbuf, k++, db=0x01); // DB1
chksum = chksum ^ db;
bufsetint8 (outbuf, k++, db=0x20); // DB2
chksum = chksum ^ db;
bufsetint8 (outbuf, k++, chksum); // XOR-Byte
// debug_mem(outbuf, k);
udp->Send(source, outbuf, k);
}
// LAN_X_SET_TURNOUT
else if (xdata.xheader == 0x53) {
int addr = (xdata.db[0] << 8) + xdata.db[1];
int cmd = xdata.db[2];
int cmd_q = cmd & 0x20;
int cmd_a = cmd & 0x08;
int cmd_p = cmd & 0x01;
pos += 4;
printf ("LAN_X_SETTURNOUT addr: %d cmd: %x (q:%d a:%d p:%d)\n", addr, cmd, cmd_q, cmd_a, cmd_p);
xlan_turnout (addr, (cmd_a != 0));
}
else {
printf ("LAN_X command not understood: %x\n", xdata.xheader);
}
}
}
}
// LAN_RMBUS_GETDATA --> send LAN_RMBUS_DATACHANGED
else if (pktheader == 0x0081 && pktlen == 5) {
unsigned char rmbus_grpidx = bufgetint8(buffer, 4);
printf ("LAN_RMBUS_GETDATA\n");
Send_LAN_RMBUS_DATACHANGED(udp, rmbus_grpidx);
}
else {
printf ("unknown command: ");
// debug_mem (buffer, len);
}
return true;
}
void Z21Client::Send_LAN_RMBUS_DATACHANGED (UDPConnection *udp, unsigned char grpidx) {
int k, i;
printf ("Send LAN_RMBUS_DATACHANGED\n");
if (grpidx >= RBUS_GROUPS) {
printf (" giving group (%d) is not supported.\n", grpidx);
return;
}
bufsetint16 (outbuf, 0, htole16(15)); // LEN
bufsetint16 (outbuf, 2, htole16(0x80)); // X_CMD
k = 4;
bufsetint8 (outbuf, k++, grpidx); // Gruppenindex
for (i = 0; i < RBUS_BYTESPERGROUP; i++)
bufsetint8 (outbuf, k++, rbus[grpidx*RBUS_BYTESPERGROUP + i].status);
udp->Send(source, outbuf, k);
}
/************************************************************************/
Z21Server::Z21Server () {
// printf ("** %s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
};
Z21Server::~Z21Server () {
// printf ("** %s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
};
void Z21Server::Start() {
// printf ("** %s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
udp.Listen(DEFAULT_Z21PORT);
};
int Z21Server::Loop() {
string source;
size_t len;
int newclient = 1;
list<Z21Client*>::iterator iter;
int i, grp;
if (udp.isListen()) {
//
// read new data from UDP
len = udp.ReadTimeout(&source, buffer, NET_BUFFERSIZE, 25); // timeout 25ms
if (len > 0) {
//
// check known connection
newclient = 1;
iter = clients.begin();
while (iter != clients.end()) {
if ((*iter)->isSource(source)) {
newclient = 0;
if (!((*iter)->LoopData(&udp, buffer, len))) {
printf ("remove client: '%s' count:%ld\n", source.c_str(), clients.size());
iter = clients.erase(iter);
}
break;
}
iter++;
}
//
// new connection
if (newclient && iter == clients.end()) {
Z21Client *client;
client = new Z21Client();
client->setSource(source);
if (client->LoopData(&udp, buffer, len)) {
clients.push_back(client);
printf ("add client: '%s' count:%ld\n", source.c_str(), clients.size());
}
}
}
//
// broadcast changed values
for (grp = 0; grp < RBUS_GROUPS; grp++) {
for (i = 0; i < RBUS_BYTESPERGROUP; i++)
if (rbus[grp*RBUS_BYTESPERGROUP+i].status != rbus[grp*RBUS_BYTESPERGROUP+i].last) break;
if (i != RBUS_BYTESPERGROUP) {
//
// send broadcast to all clients
for (iter = clients.begin(); iter != clients.end(); iter++)
(*iter)->Send_LAN_RMBUS_DATACHANGED(&udp, grp);
}
}
}
return clients.size();
};
/************************************************************************/
uint8_t bufgetint8 (char *buf, int pos) {
int8_t i;
memcpy (&i, buf+pos, 1);
return i;
};
uint16_t bufgetint16 (char *buf, int pos) {
int16_t i;
memcpy (&i, buf+pos, 2);
return i;
};
uint32_t bufgetint32 (char *buf, int pos) {
int32_t i;
memcpy (&i, buf+pos, 4);
return i;
};
void bufsetint8 (char *buf, int pos, uint8_t i) {
memcpy (buf+pos, &i, 1);
};
void bufsetint16 (char *buf, int pos, uint16_t i) {
memcpy (buf+pos, &i, 2);
};
void bufsetint32 (char *buf, int pos, uint32_t i) {
memcpy (buf+pos, &i, 4);
};