Initial revision

Import
steffen 6 years ago
commit d244d406b2

@ -0,0 +1,7 @@
2020-02-09:
- Züge fahren falsch herrum
- Speed und Fahrtrichtung werden nicht in den Zügen geupdated
- Max Speed ist nicht gleich der Maximalste Step

@ -0,0 +1,4 @@
2020-02-09:
- Initial CVS Import

@ -0,0 +1,9 @@
Dieses Programm wird nur von Steffen Pohle, Siegenburg verwendet und ist in einem Experimentellen Status.
Sollte jemand dieses Programm verwenden, so tut er es auf eigene Verantwortung. Ich übernehme keine Haftung
für Schäden durch die Software.
Steffen Pohle <steffen@gulpe.de>
9.2.2020, 93354 Siegenburg (Germany)

@ -0,0 +1,11 @@
Requirements:
- Webserver with CGI enabled
Installation:
- copy the webinterface files to your location of the webserver
- compile the server
- copy the modelbahn-cgi binary to the cgi folder of the webserver
- run the modelbahn-server application.

Binary file not shown.

@ -0,0 +1,79 @@
# .SILENT:
WEBUSER=www-data
WEBGROUP=www-data
VERSION=0.1
PREFIX=/usr/local
ETCPREFIX=/etc
DISTNAME=modelbahn
CXX=g++
CXXFLAGS= -ggdb -fPIC -Wno-write-strings -g -ggdb -std=c++11
LDFLAGS= -lm -lc -lpthread -L/usr/local/lib -g -ggdb
LDFLAGS_CGI= -lm -lc -lpthread -L/usr/local/lib -g -ggdb
DEPENDFILE=.depend
TARGET=modelbahn-server
SERVEROBJ=server.o network.o session.o server-loadsave.o debug.o \
json.o main.o sensor.o turnout.o railway.o interface.o locomotive.o \
block.o interface-z21.o
CURDIR=`pwd`
all: dep $(TARGET) test-json modelbahn-cgi
modelbahn-server: $(SERVEROBJ)
$(CXX) -o $@ $^ $(LDFLAGS_CGI) -lUDPTCPNetwork -L./ -I./ -lpthread
modelbahn-cgi: modelbahn-cgi.o debug.o
$(CXX) -o $@ $^ $(LDFLAGS) -lUDPTCPNetwork -L./ -I./ -lpthread
test-json: json.o test-json.o debug.o
$(CXX) -o $@ $^ $(LDFLAGS) -L./ -I./ -lpthread
install: $(TARGET)
cp -f $(TARGET) $(PREFIX)/lib/
uninstall:
rm -f $(PREFIX)/lib/$(TARGET)
rebuild: clean all
dep:
$(CXX) -MM `ls *.cc` $(CXXFLAGS) > $(DEPENDFILE)
clean:
rm $(TARGET) -rf
rm modelbahn-cgi -rf
rm -rf gmon.out
rm *.s -rf
rm *.o -rf
rm *.oo -rf
rm *~ -rf
rm -rf config.h
rm -rf .depend
rm -rf *.so
rm -rf *.a
rm -rf *.so.*
cleanall: clean
source: cleanall
config:
echo "#ifndef _CONFIG_H_" > config.h
echo "#define _CONFIG_H_" >> config.h
echo "" >> config.h
echo "#define VERSION \"$(VERSION)\"" >> config.h
echo "" >> config.h
echo "#define PREFIX \"$(PREFIX)\"" >> config.h
echo "#define ETCPREFIX \"$(ETCPREFIX)\"" >> config.h
echo "" >> config.h
echo "#endif" >> config.h
-include $(DEPENDFILE)

@ -0,0 +1,152 @@
#include "modelbahn.h"
#include "block.h"
Blocks::Blocks () {
changed = 0;
blocks = (Block*) malloc(sizeof(Block)*SENSORS_MAX);
max = BLOCKS_MAX;
};
Blocks::~Blocks() {
free (blocks);
blocks = NULL;
max = 0;
};
int Blocks::Lock() {
if (pthread_mutex_lock(&mtx) == 0) return 1;
else return 0;
}
int Blocks::UnLock() {
if (pthread_mutex_unlock(&mtx) == 0) return 1;
else return 0;
}
JSONParse Blocks::_GetJSON(int idx) {
JSONParse json;
JSONElement je;
string s = "";
json.Clear();
s = blocks[idx].name; json.AddObject("name", s);
json.AddObject("flags", blocks[idx].flags);
return json;
};
JSONParse Blocks::GetJSON(string name) {
int i;
JSONParse jp;
jp.Clear();
Lock();
for (i = 0; i < max; i++) if (blocks[i].name[0] != 0) {
if (name.compare(blocks[i].name) == 0) {
jp = _GetJSON(i);
}
}
UnLock();
return jp;
};
void Blocks::GetJSONAll(JSONParse *json) {
int i, cnt;
JSONElement je;
Lock();
//
// write all railway data
// create json object array manualy
je.type = JSON_T_ARRAY;
je.name = "blocks";
for (cnt = 0, i = 0; i < max; i++)
if (blocks[i].name[0] != 0) {
if (cnt != 0) je.value += ","; // not first element
je.value += _GetJSON(i).ToString();
cnt++;
}
json->AddObject(je);
UnLock();
};
Block Blocks::GetBlockFromJSON(JSONParse *j) {
Block bl;
string s;
bl.name[0] = 0;
bl.flags = 0;
j->GetValue("name", &s);
strncpy (bl.name, s.c_str(), REFERENCENAME_LEN);
j->GetValueInt("flags", &bl.flags);
return bl;
};
int Blocks::Change(Block *bl) {
int i;
int ifree = -1;
Lock();
for (i = 0; i < max; i++) {
if (blocks[i].name[0] != 0) {
// found element
if (strncmp(blocks[i].name, bl->name, REFERENCENAME_LEN) == 0) {
ifree = i;
break;
}
}
else if (ifree == -1) ifree = i;
}
// element not found add new element
if (ifree != -1 && ifree < max) {
blocks[ifree] = *bl;
strncpy (blocks[ifree].name, bl->name, REFERENCENAME_LEN);
}
changed = 1;
UnLock();
return 1;
};
int Blocks::Delete(string name) {
int i;
Lock();
for (i = 0; i < max; i++) if (blocks[i].name[0] != 0) {
if (name.compare(blocks[i].name) == 0) {
blocks[i].name[0] = 0;
blocks[i].flags = 0;
changed = 1;
break;
}
}
UnLock();
return 1;
};

@ -0,0 +1,42 @@
#ifndef _BLOCK_H_
#define _BLOCK_H_
#include "modelbahn.h"
#include "server.h"
#define BLOCKF_SHORTTRAIN 0x0001
struct s_Block {
char name[REFERENCENAME_LEN];
int flags;
} typedef Block;
class Blocks {
private:
Block *blocks;
int max;
int changed;
pthread_mutex_t mtx;
int Lock();
int UnLock();
// not thread safe
JSONParse _GetJSON(int idx);
public:
Blocks();
~Blocks();
bool IsChanged() { return changed; }
void ClearChanged() { changed = 0; };
int Change(Block *se);
int Delete(string name);
JSONParse GetJSON(string name);
void GetJSONAll(JSONParse *json);
Block GetBlockFromJSON(JSONParse *j);
};
#endif

@ -0,0 +1,56 @@
// #define _GNU_SOURCE /* See feature_test_macros(7) */
#include <stdarg.h>
#include <execinfo.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/syscall.h>
#include "modelbahn.h"
#define DEBUG_FILE "/tmp/modelbahn-server.log"
int _debuglevel = 0xff;
pid_t gettid();
void debug (int type, char *fmt,...) {
va_list args;
char text1[DEBUG_TEXT1LEN];
char text2[DEBUG_TEXT2LEN];
pid_t pid = gettid();
va_start (args, fmt);
vsnprintf (text1, (DEBUG_TEXT1LEN-1), fmt, args);
va_end (args);
text1[DEBUG_TEXT1LEN-1] = 0;
text2[DEBUG_TEXT2LEN-1] = 0;
if (type > 0) snprintf (text2, DEBUG_TEXT2LEN-1, "(%d)%d: %s", pid, type, text1);
else snprintf (text2, DEBUG_TEXT2LEN-1, "(%d) %s", pid, text1);
if (type == 0 || (type & _debuglevel) != 0) {
FILE *f;
printf ("%s\n", text2);
f= fopen(DEBUG_FILE, "a");
if (f) {
fprintf (f, "%s\n", text2);
fclose (f);
}
}
};
/*******************************************************************************************
* helper stuff
*******************************************************************************************/
//
// return the current thread process id
//
pid_t gettid() {
pid_t tid = 0;
tid = syscall(SYS_gettid);
return tid;
};

@ -0,0 +1,22 @@
#ifndef _DEBUG_H_
#define _DEBUG_H_
#define DEBUG_INFO 0x0001
#define DEBUG_ERROR 0x0002
#define DEBUG_IFACE 0x0004
#define DEBUG_NET 0x0008
#define DEBUG_SESSION 0x0010
#define DEBUG_SERVER 0x0020
#define DEBUG_RAILWAY 0x0040
#define DEBUG_LOCO 0x0080
#define DEBUG_TEXT1LEN 64000
#define DEBUG_TEXT2LEN 65000
void debug(int type, char *fmt,...);
#endif // _DEBUG_H_

@ -0,0 +1,447 @@
#include "modelbahn.h"
#include "interface.h"
#include "interface-z21.h"
//
// command and datasets for communication
static unsigned char TX_X_SET_TRACK_POWER_ON[] { 0x07, 0x00, 0x40, 0x00, 0x21, 0x81, 0xa0 };
static unsigned char TX_X_SET_TRACK_POWER_OFF[] { 0x07, 0x00, 0x40, 0x00, 0x21, 0x80, 0xa1 };
//
// values to comapare for data
static unsigned char RX_SYSTEMSTATE_DATACHANGED[] { 0x14, 0x00, 0x84, 0x00 };
static unsigned char RX_GET_SERIAL_NUMBER[] { 0x08, 0x00, 0x10, 0x00 };
static unsigned char RX_LOCONET_Z21_TX[] { 0x00, 0xa1, 0x00 }; // loconet dynamic length
static unsigned char RX_X_GET_TURNOUT_INFO[] { 0x09, 0x00, 0x40, 0x00, 0x43 };
static unsigned char RX_RMBUS_DATACHANGED[] { 0x0F, 0x00, 0x80, 0x00 };
//
// locomotive values
#define Z21_IDX_SETLOCO_DRIVE_SFMT 5
#define Z21_IDX_SETLOCO_DRIVE_ADRH 6
#define Z21_IDX_SETLOCO_DRIVE_ADRL 7
#define Z21_IDX_SETLOCO_DRIVE_STEP 8
#define Z21_IDX_SETLOCO_DRIVE_CHK 9
//
// turnout values
#define Z21_IDX_SETTURNOUT_ADRH 5
#define Z21_IDX_SETTURNOUT_ADRL 6
#define Z21_IDX_SETTURNOUT_MODE 7
#define Z21_IDX_SETTURNOUT_CHK 8
InterfaceZ21::InterfaceZ21 () {
status_connected = false;
status_poweron = false;
status_programmingmode = false;
status_shortcircuit = false;
status_emergencystop = false;
send_logon = false;
memset (rmsensors, 0x0, INTF_Z21_RMSENSOR_GROUPS * INTF_Z21_RMSENSOR_BYTES);
rmsensorinit = 0;
serial = "";
hostname = "";
timeout = time(NULL);
rmgetdatatimeout = time(NULL) - INTF_Z21_RMGETDATA_TIMEOUT;
};
InterfaceZ21::~InterfaceZ21() {
Disconnect();
};
void InterfaceZ21::Connect (string destination) {
debug (DEBUG_INFO | DEBUG_IFACE, "%s:%d Connect to: %s", __FILE__, __LINE__, destination.c_str());
if (status_connected) Disconnect();
hostname = destination;
if (udp.Listen(0) == 0) {
debug (DEBUG_ERROR | DEBUG_IFACE, "%s:%d Error could not bind UDP socket (%s)",
__FILE__, __LINE__, strerror(errno));
return;
}
udp.SetBlocked(1);
timeout = time(NULL);
send_SET_BROADCASTFLAGS();
send_GET_SERIAL_NUMBER();
status_connected = true;
for (int i = 0; i < INTF_Z21_LOCONET_MAXADDR; i++) {
loconet_map[i] = { 0 , 0, 0, 0, 0};
}
};
//
// if connected, poweroff track and logoff from Z21 controller
void InterfaceZ21::Disconnect() {
if (status_connected) {
PowerOnOff(0);
send_LOGOFF();
}
udp.Close();
status_connected = false;
status_poweron = false;
};
///////////////////////////////////////////////////////////////////////
//
//
// check if we are connected and for timeouts.
// read and go through incoming data
int InterfaceZ21::Loop(string interfacename) {
time_t curtime = time(NULL);
string source;
int inlen;
int i;
int update = false;
if (status_connected) {
//
// Z21 timed out? close all connection
if ((timeout + 2 * INTF_Z21_TIMEOUT) < curtime) {
debug (DEBUG_ERROR | DEBUG_IFACE, "%s:%d connection timed out (%d)", __FILE__, __LINE__, curtime-(timeout + 2 * INTF_Z21_TIMEOUT));
Disconnect();
update = true;
}
else if (send_logon == false && (timeout + INTF_Z21_TIMEOUT) < curtime) {
send_logon = true;
send_GET_SERIAL_NUMBER();
send_SET_BROADCASTFLAGS();
timeout = time(NULL);
update = true;
}
//
// rmgetdatatimeout?
if (curtime - rmgetdatatimeout > INTF_Z21_RMGETDATA_TIMEOUT) {
rmgetdatatimeout = curtime;
send_RM_GETDATA(0);
send_RM_GETDATA(1);
}
}
//
// check for incoming data
if (status_connected) {
if ((inlen = udp.ReadTimeout(&source, inbuffer, INTF_Z21_INBUFFER, 10)) > 0) {
//
// got some data
//
// check for LAN_SYSTEMSTATE_DATACHANGED
if (memcmp (inbuffer, RX_GET_SERIAL_NUMBER, sizeof(RX_GET_SERIAL_NUMBER)) == 0) {
// got serial number... ignore this for now
send_logon = false;
}
//
// check for LAN_X_TURNOUT_INFO
if (memcmp (inbuffer, RX_X_GET_TURNOUT_INFO, sizeof(RX_X_GET_TURNOUT_INFO)) == 0) {
// got turnout information
int addr = 0;
addr = ((unsigned char)inbuffer[Z21_IDX_SETTURNOUT_ADRH] << 8) + (unsigned char)inbuffer[Z21_IDX_SETTURNOUT_ADRL];
if (inbuffer[Z21_IDX_SETTURNOUT_MODE] == 2)
server->TurnoutAddrMode(interfacename, addr, 1);
else if (inbuffer[Z21_IDX_SETTURNOUT_MODE] == 1)
server->TurnoutAddrMode(interfacename, addr, 0);
}
//
// check for LAN_SYSTEMSTATE_DATACHANGED
else if (memcmp (inbuffer, RX_SYSTEMSTATE_DATACHANGED, sizeof(RX_SYSTEMSTATE_DATACHANGED)) == 0) {
int cs = (unsigned char) inbuffer[16];
int csex = (unsigned char) inbuffer[17];
bool old_poweron = status_poweron;
bool old_shortcircuit = status_shortcircuit;
bool old_programmingmode = status_programmingmode;
bool old_connected = status_connected;
bool old_emergencystop = status_emergencystop;
status_emergencystop = (cs & INTF_Z21_CS_EmergencyStop);
status_poweron = (cs & INTF_Z21_CS_TrackVoltageOff);
status_shortcircuit = (cs & INTF_Z21_CS_ShortCircuit);
status_programmingmode = (cs & INTF_Z21_CS_ProgModeActive);
debug (0, "%s:%d cs:%d csex:%d", __FILE__, __LINE__, cs, csex);
update = true;
//if ( old_poweron != status_poweron ||
// old_shortcircuit != status_shortcircuit ||
// old_programmingmode != status_programmingmode ||
// old_emergencystop != status_emergencystop) update = true;
}
//
// LOCONET
//Got some Data:08:00:a1:00:a1:02:00:5c:
//Got some Data:08:00:a1:00:a0:02:55:08:
else if (memcmp (inbuffer+1, RX_LOCONET_Z21_TX, sizeof (RX_LOCONET_Z21_TX)) == 0) {
int loconetopc = (unsigned char)inbuffer[4];
printf ("%s:%d Got some LOCONET (%x) Data:", __FILE__, __LINE__, loconetopc);
for (i = 0; i < inlen; i++) {
int z = (unsigned char) inbuffer[i];
printf ("i:%d %02x:", i, z);
}
printf ("\n");
//
//
if (loconetopc == 0xa0) {
//
// speed
unsigned int inloc = inbuffer[5];
unsigned int inspeed = inbuffer[6];
if (inloc >= 0 && inloc <= INTF_Z21_LOCONET_MAXADDR) {
if (loconet_last.addr) {
debug (0, "%s:%d loconet_lastaddr:%d", __FILE__, __LINE__, loconet_last.addr);
loconet_map[inloc].addr = loconet_last.addr;
}
debug (0, "%s:%d Loc:%d Speed:%d\n", __FILE__, __LINE__, loconet_map[inloc].addr, inspeed);
if (loconet_map[inloc].addr > 0) {
int speed = inspeed;
server->LocomotiveAddrSpeed(interfacename, loconet_map[inloc].addr, speed);
}
}
loconet_last.addr = 0;
}
if (loconetopc == 0xa1) {
//
// speed
unsigned int inloc = inbuffer[5];
unsigned int infunc = inbuffer[6];
if (inloc >= 0 && inloc <= INTF_Z21_LOCONET_MAXADDR) {
if (loconet_last.addr) loconet_map[inloc] = loconet_last;
debug (0, "%s:%d Loc:%d Function:%d\n", __FILE__, __LINE__, loconet_map[inloc].addr, infunc);
}
loconet_last.addr = 0;
}
}
//
// check for LAM_RMBUS_DATACHANGED
else if (memcmp (inbuffer, RX_RMBUS_DATACHANGED, sizeof(RX_RMBUS_DATACHANGED)) == 0) {
int group = (unsigned char)inbuffer[4];
int idx, bit;
printf ("LAN_RMBUS_DATA_CHANGE[%s] ", interfacename.c_str());
for (idx = 0; idx < INTF_Z21_RMSENSOR_BYTES * INTF_Z21_RMSENSOR_GROUPS; idx++) {
printf ("%x ", (unsigned char) inbuffer[4+idx]);
}
printf ("\n");
// groupindex (first or last 10 bytes)
unsigned char rmsold[INTF_Z21_RMSENSOR_BYTES * INTF_Z21_RMSENSOR_GROUPS];
memcpy (rmsold, rmsensors, INTF_Z21_RMSENSOR_BYTES * INTF_Z21_RMSENSOR_GROUPS);
if (group < INTF_Z21_RMSENSOR_GROUPS) {
memcpy (rmsensors+(group * INTF_Z21_RMSENSOR_BYTES), inbuffer+5, INTF_Z21_RMSENSOR_BYTES);
// check for changes
for (idx = 0; idx < INTF_Z21_RMSENSOR_GROUPS*INTF_Z21_RMSENSOR_BYTES; idx++) {
if (rmsold[idx]^rmsensors[idx]) {
for (i = 0; i < 8; i++) if (rmsensorinit || (rmsold[idx] & 1 << i) != (rmsensors[idx] & 1 << i)) {
debug (0, "Sendor Data Changed: %s[%d]", interfacename.c_str(), idx*8+i);
if (rmsensors[idx] & 1 << i) server->SensorAddrChange(interfacename, idx*8+i, 1);
else server->SensorAddrChange(interfacename, idx*8+i, 0);
}
}
}
rmsensorinit = 0;
}
}
else {
printf ("InterfaceZ21(%s) Got some Data:", interfacename.c_str());
for (i = 0; i < inlen; i++) {
int z = (unsigned char) inbuffer[i];
printf ("%02x:", z);
}
printf ("\n");
}
}
else if (inlen < 0) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
debug (DEBUG_ERROR | DEBUG_IFACE, "%s:%d error on reading (%s)",
__FILE__, __LINE__, strerror(errno));
}
}
}
return update;
};
//
// send_SET_BROADCASTFLAGS();
void InterfaceZ21::send_SET_BROADCASTFLAGS() {
unsigned char buffer[] = { 0x08, 0x00, 0x50, 0x00, 0x0F, 0x01, 0x00, 0x03 };
udp.Write(hostname, (char*)buffer, sizeof (buffer));
}
//
// send_GET_SERIAL_NUMBER
void InterfaceZ21::send_GET_SERIAL_NUMBER() {
unsigned char buffer[] = { 0x04, 0x00, 0x10, 0x00 };
udp.Write(hostname, (char*)buffer, sizeof (buffer));
}
//
// send_RM_GETDATA
void InterfaceZ21::send_RM_GETDATA(int group) {
unsigned char buffer[] = { 0x05, 0x00, 0x81, 0x00, 0x00 };
if (group < INTF_Z21_RMSENSOR_GROUPS) {
buffer[4] = (unsigned char)group;
udp.Write(hostname, (char*)buffer, sizeof (buffer));
}
}
//
// send_LOGOFF
void InterfaceZ21::send_LOGOFF() {
if (status_connected == false) return;
unsigned char buffer[] = { 0x04, 0x00, 0x30, 0x00 };
udp.Write(hostname, (char*)buffer, sizeof (buffer));
}
//
// Set Locomotive Speed
void InterfaceZ21::SetLocoSpeed(Locomotive *l, int step) {
unsigned char buffer[] = { 0x0A, 0x00, 0x40, 0x00, 0xE4, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00 };
debug (0, "%s:%d InterfaceZ21::SetLocoSpeed step:%d", __FILE__, __LINE__, step);
//
// reverse?
if (step < 0) {
l->flags |= LOCO_F_REVERSE;
}
else if (step > 0) {
l->flags &= ~LOCO_F_REVERSE;
}
if (l->flags & LOCO_F_REVERSE) buffer[Z21_IDX_SETLOCO_DRIVE_STEP] = 0x80;
else buffer[Z21_IDX_SETLOCO_DRIVE_STEP] = 0x00;
//
// setup loc addr
if (l->addr >= 128) buffer[Z21_IDX_SETLOCO_DRIVE_ADRH] = 0xC0;
else buffer[Z21_IDX_SETLOCO_DRIVE_ADRH] = 0;
buffer[Z21_IDX_SETLOCO_DRIVE_ADRL] = (unsigned char) (l->addr & 0xFF);
buffer[Z21_IDX_SETLOCO_DRIVE_ADRH] |= (unsigned char) (l->addr >> 8);
//
// setup steps and selected step
if (l->steps <= 14) {
buffer[Z21_IDX_SETLOCO_DRIVE_SFMT] = 0x10;
if (step == 0) buffer[Z21_IDX_SETLOCO_DRIVE_STEP] |= 0;
else buffer[Z21_IDX_SETLOCO_DRIVE_STEP] |= (abs(step)+1);
}
else if (l->steps <= 28) {
buffer[Z21_IDX_SETLOCO_DRIVE_SFMT] = 0x12;
if (step == 0) buffer[Z21_IDX_SETLOCO_DRIVE_STEP] |= 0;
else {
buffer[Z21_IDX_SETLOCO_DRIVE_STEP] |= ((abs(step)>>1)+1);
if (abs(step) & 0x01) buffer[Z21_IDX_SETLOCO_DRIVE_STEP] |= 0x10;
}
}
else if (l->steps <= 128) {
buffer[Z21_IDX_SETLOCO_DRIVE_SFMT] = 0x13;
if (step == 0) buffer[Z21_IDX_SETLOCO_DRIVE_STEP] |= 0;
else buffer[Z21_IDX_SETLOCO_DRIVE_STEP] |= (abs(step)+1);
}
//
// XOR Byte
for (int i = 4; i < sizeof (buffer)-1; i++)
buffer[Z21_IDX_SETLOCO_DRIVE_CHK] = (unsigned char)buffer[Z21_IDX_SETLOCO_DRIVE_CHK] xor (unsigned char)buffer[i];
printf ("Send Data:");
for (int i = 0; i < sizeof (buffer); i++) {
int z = (unsigned char) buffer[i];
printf ("%02x:", z);
}
printf ("\n");
udp.Write(hostname, (char*)buffer, sizeof (buffer));
loconet_last.addr = l->addr;
loconet_last.dir = l->flags & LOCO_F_REVERSE;
loconet_last.speed = l->speed;
loconet_last.maxspeed = l->vmax;
loconet_last.steps = l->steps;
};
//
// Set Locomotive Function
void InterfaceZ21::SetLocoFunction(Locomotive *l, int func, int value) {
debug (0, "%s:%d InterfaceZ21::SetLocoFunction", __FILE__, __LINE__);
};
//
// Set Turnout
void InterfaceZ21::SetTurnout(Turnout *t, int activate, int motoractive) {
unsigned char buffer[] = { 0x09, 0x00, 0x40, 0x00, 0x53, 0x00, 0x00, 0x00, 0x00 };
debug (0, "%s:%d InterfaceZ21::SetTurnout (a:%d, m:%d)", __FILE__, __LINE__, activate, motoractive);
//
// setup turnout addr
buffer[Z21_IDX_SETTURNOUT_ADRL] = (unsigned char) (t->addr & 0xFF);
buffer[Z21_IDX_SETTURNOUT_ADRH] |= (unsigned char) (t->addr >> 8);
//
// setup steps and selected step
buffer[Z21_IDX_SETTURNOUT_MODE] = 0x80;
if (activate) buffer[Z21_IDX_SETTURNOUT_MODE] |= 1;
if (motoractive) buffer[Z21_IDX_SETTURNOUT_MODE] |= 8;
//
// XOR Byte
for (int i = 4; i < sizeof (buffer)-1; i++)
buffer[Z21_IDX_SETTURNOUT_CHK] = (unsigned char)buffer[Z21_IDX_SETTURNOUT_CHK] xor (unsigned char)buffer[i];
printf ("Send Data:");
for (int i = 0; i < sizeof (buffer); i++) {
int z = (unsigned char) buffer[i];
printf ("%02x:", z);
}
printf ("\n");
udp.Write(hostname, (char*)buffer, sizeof (buffer));
};
//
// power on off
// send_X_SET_TRACK_POWER_ON(); send_X_SET_TRACK_POWER_ON();
void InterfaceZ21::PowerOnOff(int onoff) {
if (status_connected == false) return;
if (onoff) {
udp.Write(hostname, (char*)TX_X_SET_TRACK_POWER_ON, sizeof (TX_X_SET_TRACK_POWER_ON));
}
else {
udp.Write(hostname, (char*)TX_X_SET_TRACK_POWER_OFF, sizeof (TX_X_SET_TRACK_POWER_OFF));
}
};

@ -0,0 +1,81 @@
#ifndef _INTERFACE_Z21_H_
#define _INTERFACE_Z21_H_
#include "modelbahn.h"
#include "interface.h"
#include <UDPTCPNetwork.h>
#define INTF_Z21_TIMEOUT 10
#define INTF_Z21_INBUFFER 2048
// STATUSCHANGED / CENTRALSTATE
#define INTF_Z21_CS_EmergencyStop 0x01
#define INTF_Z21_CS_TrackVoltageOff 0x02
#define INTF_Z21_CS_ShortCircuit 0x04
#define INTF_Z21_CS_ProgModeActive 0x20
#define INTF_Z21_LOCONET_MAXADDR 255
#define INTF_Z21_RMSENSOR_GROUPS 2
#define INTF_Z21_RMSENSOR_BYTES 10
#define INTF_Z21_RMGETDATA_TIMEOUT 30
struct s_loconet_map{
int addr;
int dir;
int steps;
int speed;
int maxspeed;
};
class InterfaceZ21 {
private:
string hostname;
string serial;
char inbuffer[INTF_Z21_INBUFFER];
struct s_loconet_map loconet_map[INTF_Z21_LOCONET_MAXADDR];
struct s_loconet_map loconet_last; // needed for detecting loco_changes
UDP udp;
time_t timeout;
time_t rmgetdatatimeout;
bool send_logon;
bool status_poweron;
bool status_shortcircuit;
bool status_programmingmode;
bool status_connected;
bool status_emergencystop;
unsigned char rmsensors[INTF_Z21_RMSENSOR_BYTES * INTF_Z21_RMSENSOR_GROUPS];
int rmsensorinit;
// void send_X_SET_TRACK_POWER_ON();
// void send_X_SET_TRACK_POWER_OFF();
void send_GET_SERIAL_NUMBER();
void send_SET_BROADCASTFLAGS();
void send_LOGOFF();
void send_RM_GETDATA(int group);
public:
InterfaceZ21();
~InterfaceZ21();
void Connect(string destination);
void Disconnect();
bool IsConnected() { return status_connected; };
bool IsPoweron() { return status_poweron; };
bool IsSortCircuit() { return status_shortcircuit; };
bool IsProgramminnMode() { return status_programmingmode; };
bool IsEmergencyStop() { return status_emergencystop; };
int Loop(string interfacename);
void PowerOnOff(int onoff);
void SetLocoSpeed(Locomotive *l, int step);
void SetLocoFunction(Locomotive *l, int func, int value);
void SetTurnout(Turnout *t, int activate, int motoractive);
};
#endif

@ -0,0 +1,392 @@
#include "modelbahn.h"
#include "interface.h"
// **************************************************************************
// *
// * I N T E R F A C E (gateway to different devices)
// *
// **************************************************************************
Interface::Interface() {
name[0] = 0;
host[0] = 0;
flags = 0;
type = INTF_T_OFF_UNKNOWN;
needs_update = true;
};
Interface::~Interface() {
};
void Interface::Connect () {
debug (DEBUG_INFO | DEBUG_IFACE, "* Interface (%s) Connect to %s", name, host);
switch (type) {
case INTF_T_Z21: intz21.Connect(host); break;
default: break;
}
};
void Interface::Disconnect() {
debug (DEBUG_INFO | DEBUG_IFACE, "* Interface (%s) Disconnect", name);
switch (type) {
case INTF_T_Z21: intz21.Disconnect(); break;
default: break;
}
};
void Interface::PowerOnOff(int onoff) {
debug (DEBUG_INFO | DEBUG_IFACE, "* Interface (%s) PowerOnOff %d", name, onoff);
switch (type) {
case INTF_T_Z21: intz21.PowerOnOff(onoff); break;
default: break;
}
};
//
// Turnout
//
void Interface::SetTurnout(Turnout *t, int active, int motoractive) {
debug (DEBUG_INFO | DEBUG_IFACE, "* Interface (%s) SetTurnout Addr:%d FinalAcitve:%d Motor:%d", name,
t->addr, active, motoractive);
switch (type) {
case INTF_T_Z21: intz21.SetTurnout(t, active, motoractive); break;
default: break;
}
//
// make sure we turn the motor later off
if (motoractive) t->flags |= TURNOUT_F_ACTIVE;
else t->flags &= ~TURNOUT_F_ACTIVE;
gettimeofday (&t->activatetime, NULL);
};
//
// Locomotive
//
void Interface::SetLocoSpeed(Locomotive *l, int step) {
debug (DEBUG_INFO | DEBUG_IFACE, "* Interface (%s) SetLocoSpeed Addr:%d Speed:%d ", name, l->addr, step);
switch (type) {
case INTF_T_Z21: intz21.SetLocoSpeed(l, step); break;
default: break;
}
};
void Interface::SetLocoFunction(Locomotive *l, int func, int value) {
debug (DEBUG_INFO | DEBUG_IFACE, "* Interface (%s) SetLocoFunction Addr:%d Func:%d:%d", name, l->addr, func, value);
switch (type) {
case INTF_T_Z21: intz21.SetLocoFunction(l, func, value); break;
default: break;
}
};
int Interface::Loop() {
int ret = 0;
flags &= ~(INTF_F_CONNECTED | INTF_F_POWER | INTF_F_STOP | INTF_F_SHORT_CIRCUIT | INTF_F_PROGRAMMING);
if (needs_update) {
ret = 1;
needs_update = 0;
}
switch (type) {
case INTF_T_Z21:
ret = intz21.Loop(name);
if (intz21.IsConnected()) flags |= INTF_F_CONNECTED;
if (intz21.IsPoweron()) flags |= INTF_F_POWER;
if (intz21.IsEmergencyStop()) flags |= INTF_F_STOP;
if (intz21.IsSortCircuit()) flags |= INTF_F_SHORT_CIRCUIT;
break;
default: break;
}
// debug (DEBUG_INFO | DEBUG_IFACE, "%s:%d Interface: name:'%s' , Flags: %d", __FILE__, __LINE__, name, flags);
return ret;
};
bool Interface::IsConnected() {
bool ret = false;
switch (type) {
case INTF_T_Z21: ret = intz21.IsConnected(); break;
default: break;
}
return ret;
}
bool Interface::IsPoweron() {
bool ret = false;
switch (type) {
case 1: ret = intz21.IsPoweron(); break;
default: break;
}
return ret;
}
// **************************************************************************
// *
// * I N T E R F A C E S (gateway to all devices)
// *
// **************************************************************************
Interfaces::Interfaces () {
max = INTERFACES_MAX;
changed = 0;
};
Interfaces::~Interfaces() {
max = 0;
};
int Interfaces::LockThread() {
if (pthread_mutex_lock(&mtx) == 0) return 1;
else return 0;
}
int Interfaces::UnLockThread() {
if (pthread_mutex_unlock(&mtx) == 0) return 1;
else return 0;
}
JSONParse Interfaces::_GetJSON(int idx) {
JSONParse json;
JSONElement je;
string s = "";
json.Clear();
s = interfaces[idx].name; json.AddObject("name", s);
s = interfaces[idx].host; json.AddObject("host", s);
json.AddObject("flags", interfaces[idx].flags);
json.AddObject("type", interfaces[idx].type);
return json;
};
JSONParse Interfaces::GetJSON(string name) {
int i;
JSONParse jp;
jp.Clear();
LockThread();
for (i = 0; i < max; i++) if (interfaces[i].name[0] != 0) {
if (name.compare(interfaces[i].name) == 0) {
jp = _GetJSON(i);
}
}
UnLockThread();
return jp;
};
void Interfaces::GetJSONAll(JSONParse *json) {
int i, cnt;
JSONElement je;
LockThread();
//
// write all railway data
// create json object array manualy
je.type = JSON_T_ARRAY;
je.name = "interfaces";
for (cnt = 0, i = 0; i < max; i++)
if (interfaces[i].name[0] != 0) {
if (cnt != 0) je.value += ","; // not first element
je.value += _GetJSON(i).ToString();
cnt++;
}
json->AddObject(je);
UnLockThread();
};
Interface Interfaces::GetInterfaceFromJSON(JSONParse *j) {
Interface i;
string s;
i.name[0] = 0;
i.host[0] = 0;
j->GetValue("name", &s);
strncpy (i.name, s.c_str(), REFERENCENAME_LEN);
j->GetValue("host", &s);
strncpy (i.host, s.c_str(), REFERENCENAME_LEN);
j->GetValueInt("flags", &i.flags);
j->GetValueInt("type", &i.type);
return i;
};
int Interfaces::Change(Interface *iface) {
int i;
int ifree = -1;
LockThread();
for (i = 0; i < max; i++) {
if (interfaces[i].name[0] != 0) {
// found element
if (strncmp(interfaces[i].name, iface->name, REFERENCENAME_LEN) == 0) {
ifree = i;
break;
}
}
else if (ifree == -1) ifree = i;
}
// element not found add new element
if (ifree != -1 && ifree < max) {
strncpy (interfaces[ifree].name, iface->name, REFERENCENAME_LEN);
strncpy (interfaces[ifree].host, iface->host, REFERENCENAME_LEN);
interfaces[ifree].flags = iface->flags;
interfaces[ifree].type = iface->type;
interfaces[ifree].Connect();
}
changed = 1;
UnLockThread();
return 1;
};
int Interfaces::Delete(string name) {
int i;
LockThread();
for (i = 0; i < max; i++) if (interfaces[i].name[0] != 0) {
if (name.compare(interfaces[i].name) == 0) {
interfaces[i].Disconnect();
interfaces[i].name[0] = 0;
interfaces[i].host[0] = 0;
interfaces[i].flags = 0;
interfaces[i].type = INTF_T_OFF_UNKNOWN;
changed = 1;
break;
}
}
UnLockThread();
return 1;
};
void Interfaces::PowerOnOff(int onoff) {
int i;
LockThread();
for (i = 0; i < max; i++) if (interfaces[i].name[0] != 0) {
if (!interfaces[i].IsConnected()) interfaces[i].Connect();
interfaces[i].PowerOnOff(onoff);
}
UnLockThread();
};
//
// Turnouts
//
void Interfaces::SetTurnout(Turnout *t, int active, int motoractive) {
int i;
LockThread();
for (i = 0; i < max; i++) if (interfaces[i].name[0] != 0) {
if (!interfaces[i].IsConnected()) interfaces[i].Connect();
if (strncmp(t->ifname, interfaces[i].name, REFERENCENAME_LEN) == 0)
interfaces[i].SetTurnout(t, active, motoractive);
}
UnLockThread();
};
//
// Locomotives
//
void Interfaces::SetLocoSpeed(Locomotive *l, int speed) {
int i;
int step = 0;
if (abs(speed) < l->vmin) step = 0;
if (abs(speed) >= l->vmin) step = (speed * (l->steps-1)) / l->vmax;
if (abs(speed) > l->vmax) step = l->steps-1;
l->speed = speed;
LockThread();
for (i = 0; i < max; i++) if (interfaces[i].name[0] != 0) {
if (!interfaces[i].IsConnected()) interfaces[i].Connect();
if (strncmp(l->ifname, interfaces[i].name, REFERENCENAME_LEN) == 0)
interfaces[i].SetLocoSpeed(l, step);
}
UnLockThread();
};
void Interfaces::SetLocoFunction(Locomotive *l, int func, int value) {
int i;
LockThread();
for (i = 0; i < max; i++) if (interfaces[i].name[0] != 0) {
if (!interfaces[i].IsConnected()) interfaces[i].Connect();
if (strncmp(l->ifname, interfaces[i].name, REFERENCENAME_LEN) == 0)
interfaces[i].SetLocoFunction(l, func, value);
}
UnLockThread();
};
void Interfaces::Loop() {
int i;
JSONParse jout;
for (i = 0; i < max; i++) if (interfaces[i].name[0] != 0) {
if (interfaces[i].Loop()) {
//
// now we need to send an update
jout.Clear();
jout.AddObject ("interface", _GetJSON(i));
if (network) network->ChangeListPushToAll(jout.ToString());
}
}
};

@ -0,0 +1,86 @@
#ifndef _INTERFACE_H_
#define _INTERFACE_H_
#include "modelbahn.h"
#include "server.h"
#include "UDPTCPNetwork.h"
#include "json.h"
#include "interface-z21.h"
#define INTF_F_CONNECTED 0x0001
#define INTF_F_POWER 0x0002
#define INTF_F_STOP 0x0004
#define INTF_F_SHORT_CIRCUIT 0x0008
#define INTF_F_PROGRAMMING 0x0010
#define INTF_F_NEEDUPDATE 0x8000 // if something changes during the Loop
#define INTF_T_OFF_UNKNOWN 0
#define INTF_T_Z21 1
class Interface {
private:
InterfaceZ21 intz21;
bool needs_update;
public:
char name[REFERENCENAME_LEN];
char host[NET_HOSTLEN];
int flags;
int type;
Interface();
~Interface();
void Connect();
void Disconnect();
void PowerOnOff(int onoff);
void SetLocoSpeed(Locomotive *l, int step);
void SetLocoFunction(Locomotive *l, int func, int value);
void SetTurnout(Turnout *t, int active, int motoractive);
bool IsConnected();
bool IsPoweron();
int Loop();
};
class Interfaces {
private:
Interface interfaces[INTERFACES_MAX];
int max;
int changed;
pthread_mutex_t mtx;
int LockThread();
int UnLockThread();
// not thread safe
JSONParse _GetJSON(int idx);
public:
Interfaces();
~Interfaces();
bool IsChanged() { return changed; }
void ClearChanged() { changed = 0; };
int Change(Interface *iface);
int Delete(string name);
JSONParse GetJSON(string name);
void GetJSONAll(JSONParse *json);
Interface GetInterfaceFromJSON(JSONParse *j);
//
//
void PowerOnOff(int onoff);
void SetLocoSpeed(Locomotive *l, int speed);
void SetLocoFunction(Locomotive *l, int func, int value);
void SetTurnout(Turnout *t, int active, int motoractive);
void Loop();
};
#endif

@ -0,0 +1,494 @@
#include <list>
#include <string>
#include <string.h>
#include "debug.h"
#include "json.h"
/***********************************************************************
***********************************************************************
*
* JSONParse
*
***********************************************************************
***********************************************************************
*/
enum {
STEP_NONE = 0,
STEP_STARTNAME,
STEP_NAME,
STEP_STARTVALUE,
STEP_VALUE,
STEP_END
};
/////////////////////////////////////////////////////////
//
// clear out all data
//
void JSONParse::Clear() {
jsondata = "";
names.clear();
}
/////////////////////////////////////////////////////////
//
// read every element and keep only this in memory.
//
int JSONParse::Set(string json) {
int i;
int step;
int level;
bool ignorenext;
JSONElement jelement;
Clear();
// find start and read until end
for (step = STEP_NONE, i = 0, ignorenext = false; i < json.length(); i++) {
// need to copy next character
// debug (0, "JSONParse: step:%d i:%d name:'%s' value:'%s'", step, i, jelement.name.c_str(), jelement.value.c_str());
if (ignorenext) {
ignorenext = false;
if (step == STEP_NAME) jelement.name += json[i];
if (step == STEP_VALUE) jelement.value += json[i];
}
// searching for startname
else if (step == STEP_NONE) {
if (json[i] == '{') {
step = STEP_STARTNAME;
continue;
}
}
// searching for startname
else if (step == STEP_STARTNAME) {
if (json[i] == '"') {
step = STEP_NAME;
continue;
}
}
// copy name
else if (step == STEP_NAME) {
if (json[i] == '"') {
step = STEP_STARTVALUE;
continue;
}
else {
jelement.name += json[i];
continue;
}
}
// searching for startvalue
else if (step == STEP_STARTVALUE) {
if (json[i] == '"') {
step = STEP_VALUE;
jelement.type = JSON_T_STRING;
continue;
}
if (json[i] == '{') {
step = STEP_VALUE;
level = 0;
jelement.type = JSON_T_OBJECT;
jelement.value = "{";
continue;
}
if (json[i] == '[') {
step = STEP_VALUE;
level = 0;
jelement.type = JSON_T_ARRAY;
jelement.value = "[";
continue;
}
if ((json[i] >= '0' && json[i] <= '9') ||
(json[i] == '+' || json[i] == '-')) {
step = STEP_VALUE;
level = 0;
jelement.type = JSON_T_NUMBER;
jelement.value = json[i];
continue;
}
}
// copy value
else if (step == STEP_VALUE) {
if (jelement.type == JSON_T_STRING) {
if (json[i] == '"') step = STEP_END;
else jelement.value += json[i];
continue;
}
else if (jelement.type == JSON_T_OBJECT) {
if (json[i] == '}' && level == 0) {
jelement.value += json[i];
step = STEP_END;
}
else {
if (json[i] == '{') level++; // increase level
if (json[i] == '}') level--; // decrease level
jelement.value += json[i];
}
continue;
}
else if (jelement.type == JSON_T_ARRAY) {
if (json[i] == ']' && level == 0) {
jelement.value += json[i];
step = STEP_END;
}
else {
if (json[i] == '[') level++; // increase level
if (json[i] == ']') level--; // decrease level
jelement.value += json[i];
}
continue;
}
else if (jelement.type == JSON_T_NUMBER) {
if ((json[i] < '0' || json[i] > '9') && json[i] != '.' &&
json[i] != '+' && json[i] != 'e' && json[i] != 'E') step = STEP_END;
else {
jelement.value += json[i];
continue;
}
}
}
// another element?
if (step == STEP_END) {
if (json[i] == ',') {
// debug (0, "* JSON.Set Add name:%s", jelement.name.c_str());
if (jelement.type != JSON_T_NONE) {
// debug (0, "%s:%d json add element type:%d", __FILE__, __LINE__, jelement.type);
names.push_back (jelement);
}
jelement.Clear();
step = STEP_STARTNAME;
}
continue;
}
}
// debug (0, "* JSON.Set Add name:%s", jelement.name.c_str());
if (jelement.type != JSON_T_NONE) {
// debug (0, "%s:%d json add element type:%d", __FILE__, __LINE__, jelement.type);
names.push_back (jelement);
}
return 0;
};
int JSONParse::GetValue(string varname, string *dest) {
list<JSONElement>::iterator iter;
if (dest == NULL) return 0;
*dest = "";
for (iter = names.begin(); iter != names.end(); iter++) {
if (varname.compare(iter->name) == 0) {
*dest = iter->value;
return 1;
}
}
return 0;
};
int JSONParse::GetValueInt(string varname, int *dest) {
string s;
int res = GetValue(varname, &s);
if (res) {
*dest = atoi (s.c_str());
return 1;
}
return 0;
};
int JSONParse::GetValueInt64(string varname, int64_t *dest) {
string s;
int res = GetValue(varname, &s);
if (res) {
*dest = atol (s.c_str());
return 1;
}
return 0;
};
int JSONParse::GetObject(string varname, JSONParse *dest) {
list<JSONElement>::iterator iter;
if (dest == NULL) return 0;
for (iter = names.begin(); iter != names.end(); iter++) {
if (varname.compare(iter->name) == 0) {
dest->Set(iter->value);
return 1;
}
}
return 0;
};
#define MAXRECURSIVE 255
int JSONParse::GetIdx(string src, int idx, string *dest) {
char recursive[MAXRECURSIVE];
int i = 0, rcnt = 0, cnt = 0;
(*dest) = "";
// printf("\n***************************************idx:%d\n", idx);
for (i = 0; i < MAXRECURSIVE; i++) recursive[i] = 0;
for (i = 0; i < src.length() && rcnt < MAXRECURSIVE && cnt <= idx; i++) {
// printf ("i:%d rcnt:%d['%c'] cnt:%d char:'%c' ous:'%s'\n",
// i, rcnt, recursive[rcnt], cnt, src[i], dest->c_str());
if (src[i] == '[') {
recursive[rcnt++] = src[i];
continue;
}
else if (src[i] == '{' && recursive[rcnt] != '"') recursive[++rcnt] = src[i];
else if (src[i] == '}' && recursive[rcnt] == '{') rcnt--;
else if (src[i] == '"' && recursive[rcnt] == '"') rcnt--;
else if (src[i] == '"') recursive[++rcnt] = src[i];
else if (src[i] == ',' && rcnt == 1) {
cnt++;
continue;
}
else if (src[i] == ']' && rcnt == 1) {
continue;
}
if (rcnt > 0 && cnt == idx) {
(*dest) += src[i];
if (src[i] == '\\') (*dest) += src[i];
}
else {
if (src[i] == '\\')i++;
}
}
// printf("\n***************************************idx:%d cnt:%d\n", idx, cnt);
// printf("in:'%s'\n***\nout:'%s'\n\n*****\n", src.c_str(), dest->c_str());
//
// final checks
if (cnt == 0 && idx == 0 && // empty source/array?
dest->size() == 0) return 0; //
if (cnt >= idx) return 1; // found the right element
return 0; // element not found
}
#undef MAXRECURSIVE
int JSONParse::GetValueIdx(string varname, int idx, string *dest) {
list<JSONElement>::iterator iter;
if (dest == NULL) return 0;
for (iter = names.begin(); iter != names.end(); iter++) {
if (varname.compare(iter->name) == 0) {
return GetIdx(iter->value, idx, dest);
}
}
return 0;
};
int JSONParse::GetObjectIdx(string varname, int idx, JSONParse *dest) {
list<JSONElement>::iterator iter;
string deststr;
int ret = 0;
if (dest == NULL) return 0;
for (iter = names.begin(); iter != names.end(); iter++) {
if (varname.compare(iter->name) == 0) {
ret = GetIdx(iter->value, idx, &deststr);
if (ret == 1) dest->Set(deststr);
return ret;
}
}
return 0;
};
list<JSONElement> JSONParse::GetElements() {
list<JSONElement> l;
list<JSONElement>::iterator iter;
l.clear();
for (iter = names.begin(); iter != names.end(); iter++) {
l.push_back(*iter);
}
return l;
};
void JSONParse::AddObject (JSONElement element) {
names.push_back (element);
};
void JSONParse::AddObject (string name, JSONParse jp) {
JSONElement je;
je.SetObject(name, jp.ToString());
names.push_back(je);
};
void JSONParse::AddObject (string name, int val) {
JSONElement je;
je.Set(name, val);
names.push_back(je);
};
void JSONParse::AddObject (string name, int64_t val) {
JSONElement je;
je.Set(name, val);
names.push_back(je);
};
void JSONParse::AddObject (string name, string val) {
JSONElement je;
je.Set(name, val);
names.push_back(je);
};
void JSONParse::AddObject (string name, double val) {
JSONElement je;
je.Set(name, val);
names.push_back(je);
};
string JSONParse::ToString() {
list<JSONElement>::iterator iter;
string output;
int level, i;
output = "{";
for (level = 1, iter = names.begin(); iter != names.end(); iter++) {
if (iter != names.begin()) output += ",";
output += "\n";
for (i = 0; i < 4*level; i++) output += " ";
output += iter->GetString();
}
output += "\n}\n";
return output;
};
/***********************************************************************
***********************************************************************
*
* JSONElement
*
***********************************************************************
***********************************************************************
*/
void JSONElement::Set (string n, double v) {
name = n;
value = to_string(v);
type = JSON_T_NUMBER;
};
void JSONElement::Set (string n, int v) {
name = n;
value = to_string(v);
type = JSON_T_NUMBER;
};
void JSONElement::Set (string n, int64_t v) {
name = n;
value = to_string(v);
type = JSON_T_NUMBER;
};
void JSONElement::Set (string n, string v) {
name = n;
value = v;
type = JSON_T_STRING;
};
void JSONElement::SetArray (string n, list<JSONElement> *l) {
list<JSONElement>::iterator iter;
name = n;
value = "[";
type = JSON_T_STRING;
for (iter = l->begin(); iter != l->end(); iter++) {
if (iter != l->begin()) value += ",";
value += iter->GetString();
}
value += "]";
};
void JSONElement::SetObject (string n, string s) {
name = n;
value = s;
type = JSON_T_OBJECT;
};
string JSONElement::GetString () {
string output = "";
string filename = __FILE__;
switch (type) {
case(JSON_T_NUMBER):
output += "\"" + name + "\" : " + value;
break;
case(JSON_T_STRING):
if (value.length()==0) {
output += "\"" + name + "\" : \"\"";
}
// FIXME: we have to define all strings are saved here without ""
// will be set up in the future
// WORKAROUND for empty strings
else if (value[0] != '"') {
output += "\"" + name + "\" : \"" + value + "\"";
}
else output += "\"" + name + "\" : " + value;
break;
case(JSON_T_OBJECT):
output += "\"" + name + "\" : " + value;
break;
case(JSON_T_ARRAY):
if (value.length()==0) {
output += "\"" + name + "\" : []";
}
// WORKAROUND for arrays without []
else if (value[0] != '[') {
output += "\"" + name + "\" : [" + value + "]";
}
else output += "\"" + name + "\" : " + value;
break;
default:
output += "\"error\" : \""+ filename + ":" + to_string(__LINE__) + " JSONElement unknown type error\"("+to_string(type)+")";
break;
}
return output;
};

@ -0,0 +1,72 @@
#ifndef _JSON_H_
#define _JSON_H_
#include <list>
#include <string>
#include <string.h>
using namespace std;
enum {
JSON_T_NONE,
JSON_T_STRING,
JSON_T_NUMBER,
JSON_T_OBJECT,
JSON_T_ARRAY
};
class JSONElement {
public:
int type;
string name;
string value;
JSONElement() { Clear(); };
~JSONElement() {};
void Clear() { type = JSON_T_NONE; name = ""; value = ""; };
void Set (string n, double v);
void Set (string n, int v);
void Set (string n, int64_t v);
void Set (string n, string v);
void SetArray (string n, list<JSONElement> *l);
void SetObject (string n, string s);
string GetString();
};
class JSONParse {
private:
string jsondata;
list<JSONElement> names;
public:
JSONParse() { Set("{}"); };
JSONParse(string json) { Set(json); };
~JSONParse() {};
void Clear();
int Set(string json);
int GetValue(string varname, string *dest);
int GetValueInt(string varname, int *dest);
int GetValueInt64(string varname, int64_t *dest);
int GetObject(string varname, JSONParse *dest);
int GetIdx(string src, int idx, string *dest);
int GetValueIdx(string varname, int idx, string *dest);
int GetObjectIdx(string varname, int idx, JSONParse *dest);
list<JSONElement> GetElements();
void AddObject (JSONElement element);
void AddObject (string name, int val);
void AddObject (string name, int64_t val);
void AddObject (string name, string val);
void AddObject (string name, double val);
void AddObject (string name, JSONParse jp);
string ToString();
};
#endif // _JSON_H_

@ -0,0 +1,269 @@
#include "modelbahn.h"
#include "locomotive.h"
Locomotives::Locomotives () {
changed = 0;
locomotives = (Locomotive*) malloc(sizeof(Locomotive)*LOCOMOTIVES_MAX);
memset(locomotives, 0x0, sizeof(Locomotive)*LOCOMOTIVES_MAX);
max = SENSORS_MAX;
};
Locomotives::~Locomotives() {
free (locomotives);
locomotives = NULL;
max = 0;
};
int Locomotives::Lock() {
if (pthread_mutex_lock(&mtx) == 0) return 1;
else return 0;
}
int Locomotives::UnLock() {
if (pthread_mutex_unlock(&mtx) == 0) return 1;
else return 0;
}
JSONParse Locomotives::_GetJSON(int idx) {
JSONParse json;
JSONElement je;
string s = "";
json.Clear();
s = locomotives[idx].name; json.AddObject("name", s);
s = locomotives[idx].ifname; json.AddObject("ifname", s);
json.AddObject("addr", locomotives[idx].addr);
json.AddObject("steps", locomotives[idx].steps);
json.AddObject("speed", locomotives[idx].speed);
json.AddObject("func", locomotives[idx].func);
json.AddObject("flags", locomotives[idx].flags);
json.AddObject("vmin", locomotives[idx].vmin);
json.AddObject("vslow", locomotives[idx].vslow);
json.AddObject("vmid", locomotives[idx].vmid);
json.AddObject("vfast", locomotives[idx].vfast);
json.AddObject("vmax", locomotives[idx].vmax);
return json;
};
JSONParse Locomotives::GetJSON(string name) {
int i;
JSONParse jp;
jp.Clear();
Lock();
for (i = 0; i < max; i++) if (locomotives[i].name[0] != 0) {
if (name.compare(locomotives[i].name) == 0) {
jp = _GetJSON(i);
}
}
UnLock();
return jp;
};
void Locomotives::GetJSONAll(JSONParse *json) {
int i, cnt;
JSONElement je;
Lock();
//
// write all railway data
// create json object array manualy
je.type = JSON_T_ARRAY;
je.name = "locomotives";
for (cnt = 0, i = 0; i < max; i++)
if (locomotives[i].name[0] != 0) {
if (cnt != 0) je.value += ","; // not first element
je.value += _GetJSON(i).ToString();
cnt++;
}
json->AddObject(je);
UnLock();
};
Locomotive Locomotives::GetLocomotiveFromJSON(JSONParse *j) {
Locomotive l;
string s;
l.name[0] = 0;
l.ifname[0] = 0;
l.addr = 0;
l.steps = 0;
l.vmin = 0;
l.vslow = 0;
l.vmid = 0;
l.vfast = 0;
l.vmax = 0;
l.flags = 0;
l.speed = 0;
l.func = 0;
j->GetValue("name", &s);
strncpy (l.name, s.c_str(), REFERENCENAME_LEN);
j->GetValue("ifname", &s);
strncpy (l.ifname, s.c_str(), REFERENCENAME_LEN);
j->GetValueInt("addr", &l.addr);
j->GetValueInt("steps", &l.steps);
j->GetValueInt("speed", &l.speed);
j->GetValueInt64("func", &l.func);
j->GetValueInt("flags", &l.flags);
j->GetValueInt("vmin", &l.vmin);
j->GetValueInt("vslow", &l.vslow);
j->GetValueInt("vmid", &l.vmid);
j->GetValueInt("vfast", &l.vfast);
j->GetValueInt("vmax", &l.vmax);
return l;
};
int Locomotives::Change(Locomotive *loco) {
int i;
int ifree = -1;
Lock();
for (i = 0; i < max; i++) {
if (locomotives[i].name[0] != 0) {
// found element
if (strncmp(locomotives[i].name, loco->name, REFERENCENAME_LEN) == 0) {
ifree = i;
break;
}
}
else if (ifree == -1) ifree = i;
}
// element not found add new element
if (ifree != -1 && ifree < max) {
locomotives[ifree] = *loco;
strncpy (locomotives[ifree].name, loco->name, REFERENCENAME_LEN);
strncpy (locomotives[ifree].ifname, loco->ifname, REFERENCENAME_LEN);
}
changed = 1;
UnLock();
return 1;
};
int Locomotives::Delete(string name) {
int i;
Lock();
for (i = 0; i < max; i++) if (locomotives[i].name[0] != 0) {
if (name.compare(locomotives[i].name) == 0) {
locomotives[i].name[0] = 0;
locomotives[i].ifname[0] = 0;
locomotives[i].addr = 0;
locomotives[i].steps = 0;
locomotives[i].vmin = 0;
locomotives[i].vslow = 0;
locomotives[i].vmid = 0;
locomotives[i].vfast = 0;
locomotives[i].vmax = 0;
locomotives[i].flags = 0;
changed = 1;
break;
}
}
UnLock();
return 1;
};
int Locomotives::SetSpeed(string name, int speed) {
int i;
Lock();
for (i = 0; i < max; i++) if (locomotives[i].name[0] != 0) {
if (name.compare(locomotives[i].name) == 0) {
server->interfaces.SetLocoSpeed(&locomotives[i], speed);
break;
}
}
UnLock();
return 1;
};
int Locomotives::SetFunction(string name, int func, int value) {
int i;
Lock();
for (i = 0; i < max; i++) if (locomotives[i].name[0] != 0) {
if (name.compare(locomotives[i].name) == 0) {
server->interfaces.SetLocoFunction(&locomotives[i], func, value);
break;
}
}
UnLock();
return 1;
};
//
// set values from bus...
//
int Locomotives::SetSpeedFromBus(string name, int addr, int speed) {
int i;
JSONParse jp;
for (i = 0; i < max; i++) if (locomotives[i].name[0] != 0) {
if (name.compare(locomotives[i].name) == 0 && locomotives[i].addr == addr) {
locomotives[i].speed = speed;
jp.AddObject("locomotive",_GetJSON(i));
if(network) network->ChangeListPushToAll(jp.ToString());
return 1;
}
}
return 0;
};
int Locomotives::SetDirectionFromBus(string name, int addr, int reverse) {
int i;
JSONParse jp;
for (i = 0; i < max; i++) if (locomotives[i].name[0] != 0) {
if (name.compare(locomotives[i].name) == 0 && locomotives[i].addr == addr) {
if (reverse) locomotives[i].flags |= LOCO_F_REVERSE;
else locomotives[i].flags &= ~LOCO_F_REVERSE;
jp.AddObject("locomotive",_GetJSON(i));
if(network) network->ChangeListPushToAll(jp.ToString());
return 1;
}
}
return 0;
};

@ -0,0 +1,59 @@
#ifndef _LOCOMOTIVE_H_
#define _LOCOMOTIVE_H_
#include "modelbahn.h"
#include "server.h"
#define LOCO_F_REVERSE 0x0001
struct s_Locomotive {
char name[REFERENCENAME_LEN];
char ifname[REFERENCENAME_LEN];
int steps;
int speed;
int64_t func;
int flags;
int addr;
int vmin;
int vslow;
int vmid;
int vfast;
int vmax;
} typedef Locomotive;
class Locomotives {
private:
Locomotive *locomotives;
int max;
int changed;
pthread_mutex_t mtx;
int Lock();
int UnLock();
// not thread safe
JSONParse _GetJSON(int idx);
public:
Locomotives();
~Locomotives();
bool IsChanged() { return changed; }
void ClearChanged() { changed = 0; };
int Change(Locomotive *loco);
int Delete(string name);
int SetSpeed(string name, int speed);
int SetFunction(string name, int func, int value);
int SetSpeedFromBus (string name, int addr, int speed);
int SetDirectionFromBus (string name, int addr, int speed);
JSONParse GetJSON(string name);
void GetJSONAll(JSONParse *json);
Locomotive GetLocomotiveFromJSON(JSONParse *j);
};
#endif

@ -0,0 +1,120 @@
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdarg.h>
#include <signal.h>
#include <UDPTCPNetwork.h>
#include "modelbahn.h"
void uprintf (UNIX *u, char *fmt,...);
static void sig_int(int); // signal handler
int running = 1;
Server *server = NULL;
Network *network = NULL;
int main (int argc, char **argv) {
int ret;
//
// setup signals
//
if (signal(SIGINT, sig_int) == SIG_ERR) {
fprintf (stderr, "%s:%d could not set signal for SIGINT\n", __FILE__, __LINE__);
return 0;
}
if (signal(SIGTERM, sig_int) == SIG_ERR) {
fprintf (stderr, "%s:%d could not set signal for SIGTERM\n", __FILE__, __LINE__);
return 0;
}
if (signal(SIGHUP, sig_int) == SIG_ERR) {
fprintf (stderr, "%s:%d could not set signal for SIGHUB\n", __FILE__, __LINE__);
return 0;
}
debug (0, "***************************************************");
debug (0, "* *");
debug (0, "* Modelbahn Server *");
debug (0, "* *");
debug (0, "***************************************************");
debug (0, "");
//////////////////////////////////////////////////////////////////////
//
// application startup
//
debug (0, "* application startup");
server = new Server();
network = new Network();
//////////////////////////////////////////////////////////////////////
//
// application loop
//
debug (0, "* application loop");
server->Start();
network->Start();
while (running) {
sleep (1);
}
//////////////////////////////////////////////////////////////////////
//
// application shutdown
//
while (server->isRunning() || network->isRunning());
debug (0, "* application shutdown");
delete network;
debug (0, "* deleted network");
delete server;
debug (0, "* deleted server");
debug (0, "* application exited");
return 0;
};
void uprintf (UNIX *u, char *fmt,...) {
va_list args;
char buffer[BUFFERSIZE];
va_start (args, fmt);
vsnprintf (buffer, (BUFFERSIZE-1), fmt, args);
va_end (args);
buffer[BUFFERSIZE-1] = 0;
u->Write(buffer, strlen (buffer));
};
static void sig_int(int sig) {
debug (0, "* signal:%d received", sig);
if (signal (SIGINT, sig_int) == SIG_ERR) {
fprintf (stderr, "%s:%d could not set up signal SIGINT\n", __FILE__, __LINE__);
exit (-1);
}
running = 0;
}
void timer_start(struct timeval *tv) {
gettimeofday(tv, NULL);
};
int timer_end(struct timeval *tv) {
struct timeval tvnow;
gettimeofday(&tvnow, NULL);
return ((tvnow.tv_sec - tv->tv_sec)*1000 + (tvnow.tv_usec - tv->tv_usec)/1000);
};

@ -0,0 +1,64 @@
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdarg.h>
#include <signal.h>
#include <UDPTCPNetwork.h>
#define UNIX_SOCKET_FILE "/tmp/modelbahn.socket"
#define LOG_FILE "/tmp/modelbahn-cgi.log"
#define BUFFERSIZE 64000
//extern char **environ;
int main (int argc, char **argv) {
int i, inlen;
FILE *logf;
char buffer[BUFFERSIZE+1];
char *ptr = NULL;
printf ("Content-type: text/html\n");
printf ("Expires: now\n");
printf ("Pragma: no-cache\n");
printf ("\n");
//
// open logfile for writing
logf = fopen(LOG_FILE, "a+");
if (logf == NULL) return 0;
fprintf (logf, "*************************************\n");
//
// read data
memset (buffer, 0x0, BUFFERSIZE+1);
// FIXME: fgets is wrong....
for (ptr = buffer, inlen = 0; inlen < BUFFERSIZE-1 && (fgets (ptr, BUFFERSIZE - inlen, stdin)) > 0;) {
inlen = strlen (buffer);
ptr = buffer+inlen;
}
if (inlen >= BUFFERSIZE-1) fprintf (logf, "read input puffer full.\n");
fprintf (logf, "read from stdin %d bytes\n", strlen(buffer));
//
// send data to server
UNIX u;
if (u.Connect(UNIX_SOCKET_FILE) != 1) return 0;
u.Write(buffer, strlen(buffer));
//
// read data and send back to the web server
do {
i = u.ReadTimeout(buffer, BUFFERSIZE-1, 1000);
buffer[i] = 0;
fprintf (logf, "read from server %d bytes\n", i);
printf ("%s", buffer);
} while (i == BUFFERSIZE-1);
u.Close();
fclose (logf);
return 0;
};

@ -0,0 +1,40 @@
#ifndef _MODELBAHN_H_
#define _MODELBAHN_H_
#define TIMEOUT_REFRESH 1
#define BUFFERSIZE 64000
#define MAXWAITTIME 500
#define UNIX_SOCKET_FILE "/tmp/modelbahn.socket"
#define DEFAULT_DATADIR "/home/steffen/Dokumente/Programmierung/Modelbahn/"
#define SESSIONS_MAX 8
#define REFERENCENAME_LEN 128
#define INTERFACES_MAX 8
#define TURNOUTS_MAX 255
#define LOCOMOTIVES_MAX 255
#define SENSORS_MAX 255
#define BLOCKS_MAX 128
#include "server.h"
#include "turnout.h"
#include "sensor.h"
#include "network.h"
#include "block.h"
#include "debug.h"
extern int running;
extern Server *server;
extern Network *network;
//
// to measure the time in ms (used for debugging)
//
void timer_start(struct timeval *tv);
int timer_end(struct timeval *tv);
#endif // _MODELBAHN_H_

@ -0,0 +1,279 @@
#include <string>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <netdb.h>
#include <errno.h>
#include <syslog.h>
#include <pthread.h>
#include <math.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/syscall.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <list>
#include "modelbahn.h"
#include "network.h"
#include "json.h"
Network::Network() {
thread = 0;
sessions.clear();
thread_running = 0;
mtx = { 0 };
mtx = PTHREAD_MUTEX_INITIALIZER;
}
Network::~Network() {
list<Session*>::iterator iter;
while ((iter = sessions.begin()) != sessions.end()) {
Session *s = *iter;
sessions.remove(*iter);
delete s;
}
Stop();
};
int Network::LockThread() {
if (pthread_mutex_lock(&mtx) == 0) return 1;
else return 0;
}
int Network::UnLockThread() {
if (pthread_mutex_unlock(&mtx) == 0) return 1;
else return 0;
}
void Network::ThreadProcess() {
list<UNIX*>::iterator iteru;
Session *s;
UNIX *u;
while (running) {
//
// check network
//
// server socket
ServerLoop();
//
// client socket
for (iteru = clients.begin(); iteru != clients.end(); iteru++) {
if (ClientLoop((UNIX*) *iteru) < 0) {
u = *iteru;
clients.remove(u);
delete u;
iteru = clients.begin();
}
}
usleep (100000);
}
thread_running = 0;
};
int Network::Start() {
int err;
pthread_attr_t attr;
mtx = { 0 };
mtx = PTHREAD_MUTEX_INITIALIZER;
//
// start socket server
//
if (sockserver.Listen(UNIX_SOCKET_FILE) != 1) {
return 0;
}
chmod(UNIX_SOCKET_FILE, 00666);
thread_running = 1;
pthread_attr_init (&attr);
pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_JOINABLE);
err = pthread_create (&thread, &attr, ThreadEntry, this);
if (err != 0) {
debug (DEBUG_ERROR, (char*)"%s(%s:%d) pthread_create errror: %s", __FUNCTION__, __FILE__, __LINE__, strerror (errno));
running = 0;
return 0;
}
return 1;
};
void Network::Stop() {
sockserver.Close();
unlink(UNIX_SOCKET_FILE);
}
void Network::_ChangeListPushToAll(string changes) {
list<Session*>::iterator iter;
for (iter = sessions.begin(); iter != sessions.end(); iter++) {
if ((*iter)->GetSessionID() > 0) (*iter)->ChangeListPush(changes);
}
};
void Network::ChangeListPushToAll(string changes) {
LockThread();
_ChangeListPushToAll(changes);
UnLockThread();
};
int Network::ServerLoop() {
UNIX *u = NULL;
if (sockserver.IsData(10)) {
u = sockserver.Accept();
clients.push_back(u);
}
return 1;
};
Session *Network::GetSession(int sid) {
Session *s = NULL;
list<Session*>::iterator iter;
for (iter = sessions.begin(); iter != sessions.end(); iter++) {
if ((*iter)->GetSessionID() == sid) {
s = *iter;
break;
}
}
return s;
};
int Network::ClientLoop(UNIX *client) {
char bufferin[BUFFERSIZE];
char bufferout[BUFFERSIZE];
int len, i, rid, sid;
int res;
list<Session*>::iterator siter;
Session *session = NULL;
string value;
string ssid;
string srid;
string s;
int result = -1;
struct timeval timer;
timer_start(&timer);
len = client->ReadTimeout(bufferin, BUFFERSIZE, 20);
if (len > 0 && len < BUFFERSIZE) {
JSONParse json;
JSONParse jsonout;
JSONParse jelement;
bufferin[len] = 0; // prevent reading behind the data
json.Set(bufferin);
if (!json.GetValue("sid", &ssid)) {
debug (0, "json.GetValue error --> sid");
return 0;
}
if (!json.GetValue("rid", &srid)) {
debug (0, "json.GetValue error --> rid");
return 0;
}
//
try {
rid = stoi (srid);
}
catch (...) {
rid = 0;
debug (0, "* rid error");
}
try {
sid = stoi (ssid);
}
catch (...) {
sid = 0;
debug (0, "* sid error");
}
if (sid <= 0) {
int x, y;
debug (0, "* sid not set, gettin new SID");
session = new Session(rid);
sid = session->GetSessionID();
LockThread();
sessions.push_back(session);
UnLockThread();
}
else {
LockThread();
//
// search for session
session = NULL;
for (siter = sessions.begin(); siter != sessions.end(); siter++) {
if ((*siter)->GetRandomID() == rid && ((Session*)(*siter))->GetSessionID() == sid) {
session = (*siter);
break;
}
}
UnLockThread();
}
if (session) {
JSONElement je;
int retval;
LockThread();
retval = session->ProcessData(&json, &jsonout);
UnLockThread();
len = BUFFERSIZE;
if (retval) {
je.Clear();
je.Set("success", 1);
jsonout.AddObject(je);
}
else {
je.Clear();
je.Set("success", 0);
jsonout.AddObject(je);
}
s = jsonout.ToString();
client->Write((char*)s.c_str(), strlen(s.c_str()));
result = 1;
}
else {
// no session found
result = 0;
}
}
else if (len == 0) {
result = 0;
}
return result;
};

@ -0,0 +1,111 @@
#ifndef _NETWORK_H_
#define _NETWORK_H_
#include <UDPTCPNetwork.h>
#include <string>
#include <list>
#include <string>
#include <sys/time.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <netdb.h>
#include <errno.h>
#include <math.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <syslog.h>
#include <pthread.h>
#include <sys/syscall.h>
using namespace std;
#include "modelbahn.h"
#include "json.h"
extern int next_sessionID;
class Session {
private:
int sessionID;
int randomID;
list<string> changes;
void AddJSONRailway(JSONParse *jp);
void DelJSONRailway(JSONParse *jp);
void AddJSONTurnout(JSONParse *jp);
void DelJSONTurnout(JSONParse *jp);
void SetJSONTurnout(JSONParse *jp);
void AddJSONSensor(JSONParse *jp);
void DelJSONSensor(JSONParse *jp);
void AddJSONInterface(JSONParse *jp);
void DelJSONInterface(JSONParse *jp);
void AddJSONLocomotive(JSONParse *jp);
void DelJSONLocomotive(JSONParse *jp);
void SetJSONLocomotive(JSONParse *jp);
public:
Session(int rid);
~Session();
// changed which need to be send to the client //
void ChangeListPush(string chng); // push ChangeList (String must be JSON format)
JSONElement ChangeListGet(); // get ChangeList as JSON and clear list
void ChangesListClear(); // clear out Change list
int GetSessionID() { return sessionID; };
int GetRandomID() { return randomID; };
int SendData(UNIX *u, string data);
int ProcessData(JSONParse *jin, JSONParse *jout);
};
class Network {
private:
void ThreadProcess();
pthread_mutex_t mtx;
pthread_t thread;
int thread_running;
list<Session*> sessions;
list<UNIX*> clients;
Session *GetSession(int Sid);
int ServerLoop(); // loop for network connection and call clientloop if needed
int ClientLoop(UNIX *u);
UNIX sockserver;
void _ChangeListPushToAll (string changes); // not thread save
friend class Session;
public:
Network();
~Network();
int LockThread();
int UnLockThread();
int Start();
void Stop();
void ChangeListPushToAll (string changes); // adds JSON compat. change string too all clients
int isRunning() { return thread_running; }
protected:
static void *ThreadEntry (void *This) { ((Network*)This)->ThreadProcess(); return NULL;};
};
#endif // _NETWORK_H_

@ -0,0 +1,295 @@
#include "modelbahn.h"
#include "server.h"
#include "debug.h"
Railways::Railways() {
debug (0, "* Railways Constructor");
height = 0;
width = 0;
railways = NULL;
mtx = { 0 };
mtx = PTHREAD_MUTEX_INITIALIZER;
changed = 0;
};
Railways::~Railways() {
if (railways) free (railways);
height = 0;
width = 0;
};
//
// thread protection: lock
int Railways::Lock() {
if (pthread_mutex_lock(&mtx) == 0) return 1;
else return 0;
};
//
// thread protection: unlock
int Railways::UnLock() {
if (pthread_mutex_unlock(&mtx) == 0) return 1;
else return 0;
};
//
// return index inside the array (clip to boundery)
int Railways::GetRIdx(int x, int y) {
int _x, _y;
if (x < 0) _x = 0;
else if (x >= width) _x = width-1;
else _x = x;
if (y < 0) _y = 0;
else if (y >= height) _y = height-1;
else _y = y;
return (_x + _y * width);
};
//
// return JSON strign with the Railway data
// not thread safe !!!!
JSONParse Railways::_GetJSONRailway(int x, int y) {
JSONParse json;
JSONElement je;
int idx = GetRIdx(x,y);
string name = "";
json.Clear();
json.AddObject("x", x);
json.AddObject("y", y);
json.AddObject("dir", railways[idx].dir);
json.AddObject("altdir", railways[idx].altdir);
json.AddObject("type", railways[idx].type);
json.AddObject("maxspeed", railways[idx].maxspeed);
json.AddObject("flags", railways[idx].flags);
name = railways[idx].name;
json.AddObject("name", name);
return json;
};
//
// return JSON strign with the Railway data
// thread safe.
JSONParse Railways::GetJSONRailway(int x, int y) {
JSONParse json;
Lock();
json = _GetJSONRailway(x, y);
UnLock();
return json;
};
Railway Railways::GetRailwayFromJSON(JSONParse *j) {
string name;
Railway r = {
type: RAILWAY_NOTHING,
x: 0,
y: 0,
dir: 0,
altdir: 0,
maxspeed: -1,
flags: 0
};
r.name[0] = 0;
j->GetValueInt("x", &r.x);
j->GetValueInt("y", &r.y);
j->GetValueInt("dir", &r.dir);
j->GetValueInt("altdir", &r.altdir);
j->GetValueInt("type", &r.type);
j->GetValueInt("maxspeed", &r.maxspeed);
j->GetValueInt("flags", &r.flags);
j->GetValue("name", &name);
strncpy (r.name, name.c_str(), REFERENCENAME_LEN);
return r;
};
//
// return some data about the railway/tracks
//
JSONParse Railways::GetJSONTrack() {
JSONParse json;
JSONParse jsondata;
JSONElement je;
json.Clear();
json.AddObject("height", height);
json.AddObject("width", width);
return json;
};
//
// change railway
// return 1 on change
int Railways::Change(Railway *rw) {
int x, y;
int result = 1;
JSONParse json;
if (rw == NULL) return 0;
Lock();
if (rw->x < 0 || rw->x >= width) result = 0;
else if (rw->y < 0 || rw->y >= height) result = 0;
else if (rw->dir < 0 || rw->dir > 6) result = 0;
else {
changed = true;
result = 1;
if (rw->dir == 0) rw->type == RAILWAY_NOTHING;
//
// need to delete?
if (rw->type == RAILWAY_NOTHING) {
Railway *r = &railways[GetRIdx(rw->x, rw->y)];
r->type = RAILWAY_NOTHING;
r->dir = 0;
r->altdir = 0;
r->flags = 0;
r->maxspeed = 0;
r->x = rw->x;
r->y = rw->y;
r->name[0] = 0;
}
//
// add normal railway
else if (rw->type == RAILWAY_NORMAL) {
railways[GetRIdx(rw->x, rw->y)] = *rw;
}
//
// add normal railway
else if (rw->type < RAILWAY_MAX) {
railways[GetRIdx(rw->x, rw->y)] = *rw;
}
else {
debug (DEBUG_INFO, "%s:%d add unknown %d,%d dir(%d) type:%d", __FILE__, __LINE__, rw->x, rw->y, rw->dir, rw->type);
}
}
UnLock();
return result;
};
//
// redefine the size of the track, keep data
void Railways::SetSize (int neww, int newh) {
Railway *rarray = railways;
int ow = width;
int oh = height;
int x, y;
debug (0, "* Railways::SetSize (%d, %d)", neww, newh);
Lock();
//
// old values saved during call up of the function, create new array for the railways data
railways = NULL;
_New (neww, newh);
//
// copy old data
for (x = 0; x < ow && x < width; x++)
for (y = 0; y < oh && x < height; y++)
railways[x + y * width] = rarray[x + y * oh];
free (rarray);
UnLock();
};
void Railways::_New (int neww, int newh) {
int x, y;
debug (0, "* Railways New");
//
// clip to minimum and maximum sizes
if (neww < RAILWAYS_MIN_WIDTH) width = RAILWAYS_MIN_WIDTH;
else if (neww > RAILWAYS_MAX_WIDTH) width = RAILWAYS_MAX_WIDTH;
else width = neww;
if (newh < RAILWAYS_MIN_HEIGHT) width = RAILWAYS_MIN_HEIGHT;
else if (newh > RAILWAYS_MAX_HEIGHT) width = RAILWAYS_MAX_HEIGHT;
else height = newh;
//
// if memory is already allocated free it first
if (railways != NULL) free (railways);
//
// allocate memory and reset memory
railways = (Railway*) malloc (sizeof (Railway) * width * height);
for (x = 0; x < width; x++) for (y = 0; y < height; y++) {
Railway *r = &railways[GetRIdx(x, y)];
r->type = RAILWAY_NOTHING;
r->dir = 0;
r->altdir = 0;
r->flags = 0;
r->maxspeed = 0;
r->x = x;
r->y = y;
r->name[0] = 0;
}
};
void Railways::New (int neww, int newh) {
Lock();
_New (neww, newh);
UnLock();
};
//
// append all data to the givin jasonobject
void Railways::GetJSONAll(JSONParse *json) {
int x, y;
if (json == NULL) return;
Lock();
json->AddObject("track", GetJSONTrack());
for (y = 0; y < height; y++)
for (x = 0; x < width; x++)
if (railways[GetRIdx(x, y)].type != RAILWAY_NOTHING) {
json->AddObject("railway", _GetJSONRailway(x, y));
}
UnLock();
}
//
// get railway element
// thread safe
Railway Railways::Get(int x, int y) {
Railway r;
Lock();
r = railways[GetRIdx(x, y)];
UnLock();
return r;
};

@ -0,0 +1,95 @@
#ifndef _RAILWAY_H_
#define _RAILWAY_H_
#include "modelbahn.h"
#include "server.h"
enum {
RAILWAY_NOTHING = 0,
RAILWAY_NORMAL,
RAILWAY_CROSSING,
RAILWAY_TURNOUT,
RAILWAY_SENSOR,
RAILWAY_CONNECTOR,
RAILWAY_BUTTON, // just a simple button on the track to press
RAILWAY_TEXT,
RAILWAY_BLOCK,
RAILWAY_MAX
};
#define RAILWAYS_MIN_WIDTH 40
#define RAILWAYS_MIN_HEIGHT 25
#define RAILWAYS_MAX_WIDTH 500
#define RAILWAYS_MAX_HEIGHT 500
// direktion
//
// +---+ +---+ +---+ +---+
// | | | | | | | |/ |
// 0| | 1| | | 2|---| 3| |
// | | | | | | | | |
// +---+ +---+ +---+ +---+
//
// +---+ +---+ +---+ +---+
// | \| | | | | | |
// 4| | 5| | 6| | 7| |
// | | | /| |\ | | |
// +---+ +---+ +---+ +---+
//
struct s_Railway {
int type;
int x;
int y;
int dir;
int altdir; // turnout or crossing
int maxspeed;
int flags; // not defined yet
char name[REFERENCENAME_LEN]; // reference name
} typedef Railway;
class Railways {
private:
Railway *railways;
int width;
int height;
int changed;
pthread_mutex_t mtx;
int Lock();
int UnLock();
int GetRIdx(int x, int y);
JSONParse _GetJSONRailway(int x, int y);
void _New (int w, int h);
public:
Railways();
~Railways();
void New (int w, int h);
void SetSize (int w, int h);
Railway Get(int x, int y);
Railways* GetBlock(int x, int y, int w, int h);
int Set(Railway *rw);
int SetBlock(Railway *rw, int cnt);
int GetHeight () {return height;};
int GetWidth () {return width;};
int IsChanged() { return changed; };
void ClearChanged() { changed = 0; };
int Change(Railway *rw);
Railway RailwayGet(int x, int y) {return railways[GetRIdx(x, y)];};
JSONParse GetJSONRailway(int x, int y);
JSONParse GetJSONTrack();
void GetJSONAll(JSONParse *json);
Railway GetRailwayFromJSON(JSONParse *j);
};
#endif

@ -0,0 +1,190 @@
#include "modelbahn.h"
#include "sensor.h"
Sensors::Sensors () {
changed = 0;
sensors = (Sensor*) malloc(sizeof(Sensor)*SENSORS_MAX);
max = SENSORS_MAX;
};
Sensors::~Sensors() {
free (sensors);
sensors = NULL;
max = 0;
};
int Sensors::Lock() {
if (pthread_mutex_lock(&mtx) == 0) return 1;
else return 0;
}
int Sensors::UnLock() {
if (pthread_mutex_unlock(&mtx) == 0) return 1;
else return 0;
}
JSONParse Sensors::_GetJSON(int idx) {
JSONParse json;
JSONElement je;
string s = "";
json.Clear();
s = sensors[idx].name; json.AddObject("name", s);
s = sensors[idx].ifname; json.AddObject("ifname", s);
json.AddObject("addr", sensors[idx].addr);
json.AddObject("flags", sensors[idx].flags);
return json;
};
JSONParse Sensors::GetJSON(string name) {
int i;
JSONParse jp;
jp.Clear();
Lock();
for (i = 0; i < max; i++) if (sensors[i].name[0] != 0) {
if (name.compare(sensors[i].name) == 0) {
jp = _GetJSON(i);
}
}
UnLock();
return jp;
};
void Sensors::GetJSONAll(JSONParse *json) {
int i, cnt;
JSONElement je;
Lock();
//
// write all railway data
// create json object array manualy
je.type = JSON_T_ARRAY;
je.name = "sensors";
for (cnt = 0, i = 0; i < max; i++)
if (sensors[i].name[0] != 0) {
if (cnt != 0) je.value += ","; // not first element
je.value += _GetJSON(i).ToString();
cnt++;
}
json->AddObject(je);
UnLock();
};
Sensor Sensors::GetSensorFromJSON(JSONParse *j) {
Sensor se;
string s;
se.name[0] = 0;
se.ifname[0] = 0;
se.addr = 0;
se.flags = 0;
j->GetValue("name", &s);
strncpy (se.name, s.c_str(), REFERENCENAME_LEN);
j->GetValue("ifname", &s);
strncpy (se.ifname, s.c_str(), REFERENCENAME_LEN);
j->GetValueInt("addr", &se.addr);
j->GetValueInt("flags", &se.flags);
return se;
};
int Sensors::Change(Sensor *se) {
int i;
int ifree = -1;
Lock();
for (i = 0; i < max; i++) {
if (sensors[i].name[0] != 0) {
// found element
if (strncmp(sensors[i].name, se->name, REFERENCENAME_LEN) == 0) {
ifree = i;
break;
}
}
else if (ifree == -1) ifree = i;
}
// element not found add new element
if (ifree != -1 && ifree < max) {
sensors[ifree] = *se;
strncpy (sensors[ifree].name, se->name, REFERENCENAME_LEN);
strncpy (sensors[ifree].ifname, se->ifname, REFERENCENAME_LEN);
}
changed = 1;
UnLock();
return 1;
};
int Sensors::Delete(string name) {
int i;
Lock();
for (i = 0; i < max; i++) if (sensors[i].name[0] != 0) {
if (name.compare(sensors[i].name) == 0) {
sensors[i].name[0] = 0;
sensors[i].ifname[0] = 0;
sensors[i].addr = 0;
sensors[i].flags = 0;
changed = 1;
break;
}
}
UnLock();
return 1;
};
//
// got some information from an interface..
void Sensors::SetFromBus(string name, int addr, int active) {
int i;
JSONParse jp;
for (i = 0; i < max; i++) if (sensors[i].name[0] != 0) {
if (strncmp (sensors[i].ifname, name.c_str(), REFERENCENAME_LEN) == 0 && sensors[i].addr == addr) {
debug (0, "* Sensor %s changed:%d", sensors[i].name, active);
if (sensors[i].flags & SENSOR_F_INVERSE) {
if (active) sensors[i].flags &= ~SENSOR_F_ACTIVE;
else sensors[i].flags |= SENSOR_F_ACTIVE;
}
else {
if (active) sensors[i].flags |= SENSOR_F_ACTIVE;
else sensors[i].flags &= ~SENSOR_F_ACTIVE;
}
jp.AddObject("sensor", _GetJSON(i));
if(network) network->ChangeListPushToAll(jp.ToString());
}
}
jp.AddObject("infoline", "Sensor:"+name+"["+ to_string(addr) +"]");
if(network) network->ChangeListPushToAll(jp.ToString());
};

@ -0,0 +1,48 @@
#ifndef _SENSOR_H_
#define _SENSOR_H_
#define SENSOR_F_INVERSE 0x0001
#define SENSOR_F_ACTIVE 0x0002
#include "modelbahn.h"
#include "server.h"
struct s_Sensor {
char name[REFERENCENAME_LEN];
char ifname[REFERENCENAME_LEN];
int addr;
int flags;
} typedef Sensor;
class Sensors {
private:
Sensor *sensors;
int max;
int changed;
pthread_mutex_t mtx;
int Lock();
int UnLock();
// not thread safe
JSONParse _GetJSON(int idx);
public:
Sensors();
~Sensors();
bool IsChanged() { return changed; }
void ClearChanged() { changed = 0; };
int Change(Sensor *se);
int Delete(string name);
JSONParse GetJSON(string name);
void GetJSONAll(JSONParse *json);
Sensor GetSensorFromJSON(JSONParse *j);
void SetFromBus(string name, int addr, int active);
};
#endif

@ -0,0 +1,194 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "modelbahn.h"
int Server::Load() {
string f = DEFAULT_DATADIR;
f = f + "/track.json";
Load (f);
return 1;
};
int Server::Save() {
struct stat sbuf;
string f = DEFAULT_DATADIR;
string nf;
//
// check if the folder does exists.
if (stat (f.c_str(), &sbuf) != 0) {
debug (DEBUG_ERROR, "* Track::Save could not find directory '%s'. Creating it.", f.c_str());
if (mkdir (f.c_str(), 0755) != 0) {
debug (DEBUG_ERROR, "* Track::Save could not create directory '%s'. (%s)", f.c_str(), strerror(errno));
return 0;
}
}
if ((sbuf.st_mode & S_IFMT) != S_IFDIR) {
debug (DEBUG_ERROR, "* Track::Save could not stat directory: '%s'. (%s)", f.c_str(), strerror(errno));
if (errno != EINTR) return 0;
}
//
// check if the file exsists
f = DEFAULT_DATADIR; f = f + "/track.json";
nf = DEFAULT_DATADIR; nf = nf + "/track.json.backup." + to_string(time(NULL));
if (stat (f.c_str(), &sbuf) == 0) {
debug (DEBUG_INFO, "* Track::Save rename backup file to: %s", nf.c_str());
if (rename (f.c_str(), nf.c_str())) {
debug (DEBUG_ERROR, "* Track::Save could not rename file '%s' to '%s'. Error:(%s)",
f.c_str(), nf.c_str(), strerror(errno));
}
}
return Save(f);
};
////////////////////////////////////////////////////////////////
//
// loading and saving of the file is done here. We do no checks
// here.
//
int Server::Load(string fn) {
int fd;
size_t len;
struct stat sbuf;
JSONParse json;
JSONParse jtmp;
char *buf = NULL;
string s;
int x, y, i;
debug (DEBUG_INFO, "* Load File:%s", fn.c_str());
if (stat (fn.c_str(), &sbuf) == 0) {
buf = (char *) malloc(sbuf.st_size+1);
memset (buf, 0x0, sbuf.st_size+1);
fd = open (fn.c_str(), O_RDONLY);
len = read (fd, buf, sbuf.st_size);
close (fd);
// everything read?
if (len < sbuf.st_size) {
free (buf);
debug (DEBUG_ERROR, "* Reading Track File Failed. (len < filesize)");
return 0;
}
s = buf;
json.Set(s);
}
else {
debug (DEBUG_ERROR, "* cloud not stat file (%s)", strerror(errno));
return 0;
}
//
// json holds all the data
//
// read track values
json.GetObject("track", &jtmp);
jtmp.GetValueInt("width", &x);
jtmp.GetValueInt("height", &y);
railways.New (x, y);
//
// read railways
Railway rw;
for (i = 0; json.GetObjectIdx("railways", i, &jtmp); i++) {
rw = railways.GetRailwayFromJSON(&jtmp);
railways.Change(&rw);
}
//
// read interfaces
Interface iface;
for (i = 0; json.GetObjectIdx("interfaces", i, &jtmp); i++) {
iface = interfaces.GetInterfaceFromJSON(&jtmp);
interfaces.Change(&iface);
}
//
// read locomotives
Locomotive loco;
for (i = 0; json.GetObjectIdx("locomotives", i, &jtmp); i++) {
loco = locomotives.GetLocomotiveFromJSON(&jtmp);
locomotives.Change(&loco);
}
//
// read sensors
Sensor se;
for (i = 0; json.GetObjectIdx("sensors", i, &jtmp); i++) {
se = sensors.GetSensorFromJSON(&jtmp);
sensors.Change(&se);
}
//
// read turnouts
Turnout to;
for (i = 0; json.GetObjectIdx("turnouts", i, &jtmp); i++) {
to = turnouts.GetTurnoutFromJSON(&jtmp);
turnouts.Change(&to);
}
railways.ClearChanged();
return 1;
};
int Server::Save(string fn) {
FILE *f;
JSONParse json;
JSONElement je;
int x, y, cnt;
debug (DEBUG_INFO, "* Save File:%s", fn.c_str());
json.Clear();
json.AddObject("track", railways.GetJSONTrack());
//
// write all railway data
// create json object array manualy
je.type = JSON_T_ARRAY;
je.name = "railways";
for (cnt = 0, x = 0; x < railways.GetWidth(); x++) for (y = 0; y < railways.GetHeight(); y++)
if (railways.Get(x, y).type != RAILWAY_NOTHING) {
if (cnt != 0) je.value += ","; // not first element
je.value += railways.GetJSONRailway(x, y).ToString();
cnt++;
}
json.AddObject(je);
//
// write all interfaces
interfaces.GetJSONAll(&json);
//
// write all locomotives
locomotives.GetJSONAll(&json);
//
// write all sensors
sensors.GetJSONAll(&json);
//
// write all turnouts
turnouts.GetJSONAll(&json);
f = fopen (fn.c_str(), "w");
fprintf (f, "%s", json.ToString().c_str());
fclose (f);
railways.ClearChanged();
return 1;
};

@ -0,0 +1,105 @@
#include <string>
#include <sys/time.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <netdb.h>
#include <errno.h>
#include <math.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <syslog.h>
#include <pthread.h>
#include <sys/syscall.h>
#include "modelbahn.h"
#include "server.h"
int Server::Start() {
int err;
pthread_attr_t attr;
mtx = { 0 };
mtx = PTHREAD_MUTEX_INITIALIZER;
thread_running = 1;
pthread_attr_init (&attr);
pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_JOINABLE);
err = pthread_create (&thread, &attr, ThreadEntry, this);
if (err != 0) {
debug (DEBUG_ERROR, (char*)"%s(%s:%d) pthread_create errror: %s", __FUNCTION__, __FILE__, __LINE__, strerror (errno));
running = 0;
return 0;
}
return 1;
};
Server::Server() {
thread = 0;
thread_running = 0;
railways.SetSize(200, 200);
Load ();
};
Server::~Server() {
if (IsChanged()) Save();
};
void Server::ThreadProcess() {
int i = 0;
while (running) {
interfaces.Loop();
turnouts.Loop();
usleep (25000);
}
debug (0, "Server::ThreadProcess Finished");
thread_running = 0;
};
void Server::LockThread() {
debug(DEBUG_INFO, "%s:%d Server::LockThread", __FILE__, __LINE__);
pthread_mutex_lock (&mtx);
};
void Server::UnLockThread() {
debug (DEBUG_INFO, "%s:%d Server::UnLockThreads", __FILE__, __LINE__);
pthread_mutex_unlock (&mtx);
};
//
// return JSONObject with all data
void Server::GetJSONAll(JSONParse *json) {
debug (DEBUG_INFO, "* Track::GetJSONAll data");
if (json == NULL) return;
railways.GetJSONAll(json);
interfaces.GetJSONAll(json);
sensors.GetJSONAll(json);
locomotives.GetJSONAll(json);
turnouts.GetJSONAll(json);
}
bool Server::IsChanged() {
if (railways.IsChanged() ||
interfaces.IsChanged() ||
locomotives.IsChanged() ||
sensors.IsChanged() ||
turnouts.IsChanged()) return true;
else return false;
}

@ -0,0 +1,135 @@
#ifndef _SERVER_H_
#define _SERVER_H_
#include <string>
#include <sys/time.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <netdb.h>
#include <errno.h>
#include <math.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <syslog.h>
#include <pthread.h>
#include <sys/syscall.h>
#include "json.h"
#include "modelbahn.h"
#include "turnout.h"
#include "railway.h"
#include "locomotive.h"
#include "sensor.h"
#include "interface.h"
class Server {
private:
pthread_mutex_t mtx;
pthread_t thread;
int thread_running;
Turnouts turnouts;
Sensors sensors;
Railways railways;
Locomotives locomotives;
Interfaces interfaces;
void ThreadProcess();
void LoopCheckChanges();
bool IsChanged();
int Load(string fn);
int Save(string fn);
friend class Interfaces;
friend class InterfacesZ21;
friend class Locomotives;
friend class Sensors;
friend class Turnouts;
public:
/////////////////////////////////////////
// functions here are required to be thread save
Server();
~Server();
int Start();
int isRunning() { return thread_running; }
void LockThread();
void UnLockThread();
//////////////////////////////////////////////
// General Commands, for controlling trains,
// turnouts and sensors
//
void PowerOnOff(int onoff) { interfaces.PowerOnOff(onoff); };
int GetHeight() { return railways.GetHeight(); };
int GetWidth() { return railways.GetWidth(); };
/////////////////////////////////////////
// Railway
int RailwayChange(Railway *rw) { return railways.Change(rw);};
JSONParse RailwayGetJSONTrack() { return railways.GetJSONTrack(); };
JSONParse RailwayGetJSONRailway (int x, int y) { return railways.GetJSONRailway(x, y); };
Railway GetRailwayFromJSON(JSONParse *j) { return railways.GetRailwayFromJSON(j); };
/////////////////////////////////////////
// Turnout
int TurnoutChange(Turnout *t) { return turnouts.Change(t); };
Turnout TurnoutFromJSON(JSONParse *j) { return turnouts.GetTurnoutFromJSON(j); };
JSONParse TurnoutGetJSON(string name) { return turnouts.GetJSON(name); };
int TurnoutDelete(string name) { return turnouts.Delete(name); };
int TurnoutSet(string name, int active) { return turnouts.Set(name, active); };
/////////////////////////////////////////
// Interface
int InterfaceChange(Interface *i) { return interfaces.Change(i); };
Interface InterfaceFromJSON(JSONParse *j) { return interfaces.GetInterfaceFromJSON(j); };
JSONParse InterfaceGetJSON(string name) { return interfaces.GetJSON(name); };
int InterfaceDelete(string name) { return interfaces.Delete(name); };
/////////////////////////////////////////
// Locomotive
int LocomotiveChange(Locomotive *l) { return locomotives.Change(l); };
Locomotive LocomotiveFromJSON(JSONParse *j) { return locomotives.GetLocomotiveFromJSON(j); };
JSONParse LocomotiveGetJSON(string name) { return locomotives.GetJSON(name); };
int LocomotiveDelete(string name) { return locomotives.Delete(name); };
int LocomotiveSetSpeed(string name, int speed) { return locomotives.SetSpeed(name, speed); };
int LocomotiveSetFunction(string name, int func, int value) { return locomotives.SetFunction(name, func, value); };
/////////////////////////////////////////
// Sensor
int SensorChange(Sensor *s) { return sensors.Change(s); };
Sensor SensorFromJSON(JSONParse *j) { return sensors.GetSensorFromJSON(j); };
JSONParse SensorGetJSON(string name) { return sensors.GetJSON(name); };
int SensorDelete(string name) { return sensors.Delete(name); };
void GetJSONAll(JSONParse *json);
/////////////////////////////////////////
// reports from interfaces
int LocomotiveAddrSpeed(string name, int addr, int speed) { return locomotives.SetSpeedFromBus(name, addr, speed); };
int LocomotiveAddrDirection(string name, int addr, int reverse) { return locomotives.SetDirectionFromBus(name, addr, reverse); };
int TurnoutAddrMode(string name, int addr, int active) { turnouts.SetFromBus(name, addr, active); return 1; };
int SensorAddrChange(string name, int addr, int active) { sensors.SetFromBus(name, addr, active); return 1; };
//
// Load Save Part
int Load();
int Save();
protected:
static void *ThreadEntry (void *This) { ((Server*)This)->ThreadProcess(); return NULL;};
};
#endif // _SERVER_H_

@ -0,0 +1,510 @@
#include "modelbahn.h"
#include <list>
int next_sessionID = 1;
int Session::SendData(UNIX *u, string data) {
return 0;
};
Session::Session(int rid) {
sessionID = random();
randomID = rid;
changes.clear();
};
Session::~Session() {
changes.clear();
};
////////////////////////////////////////////////////////////////
//
// process data and send result back
// it will process the command, and reply with all known
// changes.
//
int Session::ProcessData(JSONParse *jin, JSONParse *jout) {
JSONElement je;
JSONParse json;
JSONParse jsondata;
string command;
list<JSONElement> elements;
list<JSONElement>::iterator iter;
jout->Clear();
// debug (0, "* Session sid:%d rid:%d", sessionID, randomID);
//
// found command
if (jin->GetValue("command", &command) == 1) {
//
// editing elements
//
debug (0, "%s:%d JIN:%s", __FILE__, __LINE__, jin->ToString().c_str());
if (command.compare("addrailway") == 0) {
debug (0, "* Session Add Railways");
AddJSONRailway(jin);
}
else if (command.compare("delrailway") == 0) {
debug (0, "* Session Del Railways");
DelJSONRailway(jin);
}
else if (command.compare("addinterface") == 0) {
debug (0, "* Session Add Interface");
AddJSONInterface(jin);
}
else if (command.compare("delinterface") == 0) {
debug (0, "* Session Del Interface");
DelJSONInterface(jin);
}
else if (command.compare("addsensor") == 0) {
debug (0, "* Session Add Sensor");
AddJSONSensor(jin);
}
else if (command.compare("delsensor") == 0) {
debug (0, "* Session del Sensor");
DelJSONSensor(jin);
}
else if (command.compare("addturnout") == 0) {
debug (0, "* Session Add Turnout");
AddJSONTurnout(jin);
}
else if (command.compare("delturnout") == 0) {
debug (0, "* Session del Turnout");
DelJSONTurnout(jin);
}
else if (command.compare("addlocomotive") == 0) {
debug (0, "* Session Add Locomotive");
AddJSONLocomotive(jin);
}
else if (command.compare("dellocomotive") == 0) {
debug (0, "* Session Del Locomotive");
DelJSONLocomotive(jin);
}
//
// locomotives
//
else if (command.compare("setlocomotive") == 0) {
SetJSONLocomotive(jin);
}
//
// locomotives
//
else if (command.compare("setturnout") == 0) {
SetJSONTurnout(jin);
}
//
// poweron / poweroff
//
else if (command.compare("poweron") == 0) {
debug (0, "* Session Poweron");
server->PowerOnOff(1);
}
else if (command.compare("poweroff") == 0) {
debug (0, "* Session Poweroff");
server->PowerOnOff(0);
}
else if (command.compare("save") == 0) {
debug (0, "* Save All");
server->Save();
}
else if (command.compare("getall") == 0) {
json.Clear();
server->GetJSONAll(&json);
// debuggin maybe we need to fix this somehow
elements = json.GetElements();
for (iter = elements.begin(); iter != elements.end(); iter++)
ChangeListPush("{"+(*iter).GetString()+"}");
}
else {
debug (DEBUG_ERROR | DEBUG_SESSION, "%s:%d Unknown command: '%s' JSON:%s",
__FILE__, __LINE__, command.c_str(), jin->ToString().c_str());
}
}
je = ChangeListGet();
jout->AddObject(je);
jout->AddObject("sid", sessionID);
jout->AddObject("rid", randomID);
return 1;
}
//////////////////////////////////////////////////////////////
//
// add chenges which need to be send to the clients
//
void Session::ChangeListPush(string chng) {
changes.push_back(chng);
};
//
// get the changes as json string and clear list of changes
//
JSONElement Session::ChangeListGet() {
JSONElement je;
list<string>::iterator iter;
// debug (0, "* Session::ChangeListGet cnt:%d", changes.size());
je.type = JSON_T_ARRAY;
je.name = "changes";
je.value = "[";
for (iter = changes.begin(); iter != changes.end(); iter++) {
if (iter != changes.begin()) je.value += ",\n ";
else je.value += "\n ";
je.value += (*iter);
}
changes.clear();
je.value += "]";
// debug (0, "* Session::ChangeListGet string:'%s'", je.GetString().c_str());
return je;
}
/////////////////////////////////////////////////////////////
//
// commands send from the client to the server
//
// add a new jsonrailway to the server the JSONParse Object
// will contains the full JSON string from the web page
//
void Session::AddJSONRailway(JSONParse *jp) {
int i;
JSONParse element;
JSONParse pos;
JSONParse jout;
Railway r;
list<Railway> l;
if (server == NULL) return;
server->LockThread();
for (i = 0; jp->GetObjectIdx("rail", i, &element); i++) {
debug (0, "%s:%d AddJSONRailway Element '%s'", __FILE__, __LINE__, element.ToString().c_str());
r = server->GetRailwayFromJSON(&element);
server->RailwayChange(&r);
jout.Clear();
jout.AddObject("railway", server->RailwayGetJSONRailway(r.x, r.y));
if (network) network->_ChangeListPushToAll(jout.ToString());
}
server->UnLockThread();
}
//
// delete some jsonrailway from the server the JSONParse Object
// will contains the full JSON string from the web page
//
void Session::DelJSONRailway(JSONParse *jp) {
int i;
JSONParse element;
JSONParse pos;
JSONParse jout;
Railway r;
if (server == NULL) return;
server->LockThread();
for (i = 0; jp->GetObjectIdx("rail", i, &element); i++) {
debug (0, "%s:%d DelJSONRailway Element %s", __FILE__, __LINE__, element.ToString().c_str());
r = server->GetRailwayFromJSON(&element);
r.type = RAILWAY_NOTHING;
server->RailwayChange(&r);
jout.Clear();
jout.AddObject("railway", server->RailwayGetJSONRailway(r.x, r.y));
if (network) network->_ChangeListPushToAll(jout.ToString());
}
server->UnLockThread();
}
//
// add new interface
//
void Session::AddJSONInterface(JSONParse *jp) {
Interface iface;
JSONParse jiface;
JSONParse jout;
server->LockThread();
jp->GetObject("interface", &jiface);
iface = server->InterfaceFromJSON(&jiface);
if (iface.name[0] != 0) {
debug (0, "%s:%d AddJSONInterface Element %s", __FILE__, __LINE__, iface.name);
// add element
server->InterfaceChange(&iface);
jout.Clear();
jout.AddObject("interface", server->InterfaceGetJSON(iface.name));
if (network) network->_ChangeListPushToAll(jout.ToString());
}
server->UnLockThread();
};
//
// Delete interface
//
void Session::DelJSONInterface(JSONParse *jp) {
Interface iface;
JSONParse jiface;
JSONParse jout;
string s;
server->LockThread();
jp->GetObject("interface", &jiface);
iface = server->InterfaceFromJSON(&jiface);
if (iface.name[0] != 0) {
debug (0, "%s:%d DelJSONInterface Element %s", __FILE__, __LINE__, iface.name);
// add element
server->InterfaceDelete(iface.name);
jout.Clear();
s = iface.name;
jout.AddObject("interfacedelete", s);
if (network) network->_ChangeListPushToAll(jout.ToString());
}
server->UnLockThread();
};
//
// add new Sensor
//
void Session::AddJSONSensor(JSONParse *jp) {
Sensor se;
JSONParse jtmp;
JSONParse jout;
server->LockThread();
jp->GetObject("sensor", &jtmp);
se = server->SensorFromJSON(&jtmp);
if (se.name[0] != 0) {
debug (0, "%s:%d AddJSONSensor Element %s", __FILE__, __LINE__, se.name);
// add element
server->SensorChange(&se);
jout.Clear();
jout.AddObject("sensor", server->SensorGetJSON(se.name));
if (network) network->_ChangeListPushToAll(jout.ToString());
}
server->UnLockThread();
};
//
// Delete Sensor
//
void Session::DelJSONSensor(JSONParse *jp) {
Sensor se;
JSONParse jtmp;
JSONParse jout;
string s;
server->LockThread();
jp->GetObject("sensor", &jtmp);
se = server->SensorFromJSON(&jtmp);
if (se.name[0] != 0) {
debug (0, "%s:%d DelJSONTurnout Element %s", __FILE__, __LINE__, se.name);
// add element
server->SensorDelete(se.name);
jout.Clear();
s = se.name;
jout.AddObject("sensordelete", s);
if (network) network->_ChangeListPushToAll(jout.ToString());
}
server->UnLockThread();
};
//
// add new Locomotive
//
void Session::AddJSONLocomotive(JSONParse *jp) {
Locomotive loco;
JSONParse jloco;
JSONParse jout;
server->LockThread();
jp->GetObject("locomotive", &jloco);
loco = server->LocomotiveFromJSON(&jloco);
if (loco.name[0] != 0) {
debug (0, "%s:%d AddJSONLocomotive Element %s", __FILE__, __LINE__, loco.name);
// add element
server->LocomotiveChange(&loco);
jout.Clear();
jout.AddObject("locomotive", server->LocomotiveGetJSON(loco.name));
if (network) network->_ChangeListPushToAll(jout.ToString());
}
server->UnLockThread();
};
//
// Set Locomotive
//
void Session::SetJSONLocomotive(JSONParse *jp) {
JSONParse jloco;
JSONParse jout;
string loconame;
int speed;
int func;
int value;
server->LockThread();
jp->GetObject("locomotive", &jloco);
jloco.GetValue("name", &loconame);
if (loconame.length() > 0) {
if (jloco.GetValueInt("speed", &speed) == 1) {
//
// set speed
debug (0, "%s:%d SetJSONLocomotive Element %s Speed:%d", __FILE__, __LINE__, loconame.c_str(), speed);
server->LocomotiveSetSpeed(loconame, speed);
}
if (jloco.GetValueInt("function", &func) == 1 && jloco.GetValueInt("value", &value) == 1) {
//
// set function
debug (0, "%s:%d SetJSONLocomotive Element %s Function:%d Value:%d", __FILE__, __LINE__, loconame.c_str(), func, value);
server->LocomotiveSetFunction(loconame, func, speed);
}
jout.Clear();
jout.AddObject("locomotive", server->LocomotiveGetJSON(loconame));
if (network) network->_ChangeListPushToAll(jout.ToString());
}
server->UnLockThread();
};
//
// Delete Locomotive
//
void Session::DelJSONLocomotive(JSONParse *jp) {
Locomotive loco;
JSONParse jloco;
JSONParse jout;
string s;
server->LockThread();
jp->GetObject("locomotive", &jloco);
loco = server->LocomotiveFromJSON(&jloco);
if (loco.name[0] != 0) {
debug (0, "%s:%d DelJSONLocomotive Element %s", __FILE__, __LINE__, loco.name);
// add element
server->LocomotiveDelete(loco.name);
jout.Clear();
s = loco.name;
jout.AddObject("locomotivedelete", s);
if (network) network->_ChangeListPushToAll(jout.ToString());
}
server->UnLockThread();
};
//
// add new Turnout
//
void Session::AddJSONTurnout(JSONParse *jp) {
Turnout to;
JSONParse jtmp;
JSONParse jout;
server->LockThread();
jp->GetObject("turnout", &jtmp);
to = server->TurnoutFromJSON(&jtmp);
if (to.name[0] != 0) {
debug (0, "%s:%d AddJSONTurnout Element %s", __FILE__, __LINE__, to.name);
// add element
server->TurnoutChange(&to);
jout.Clear();
jout.AddObject("turnout", server->TurnoutGetJSON(to.name));
if (network) network->_ChangeListPushToAll(jout.ToString());
}
server->UnLockThread();
};
//
// Delete Turnout
//
void Session::DelJSONTurnout(JSONParse *jp) {
Turnout to;
JSONParse jtmp;
JSONParse jout;
string s;
server->LockThread();
jp->GetObject("turnout", &jtmp);
to = server->TurnoutFromJSON(&jtmp);
if (to.name[0] != 0) {
debug (0, "%s:%d DelJSONTurnout Element %s", __FILE__, __LINE__, to.name);
// add element
server->TurnoutDelete(to.name);
jout.Clear();
s = to.name;
jout.AddObject("turnoutdelete", s);
if (network) network->_ChangeListPushToAll(jout.ToString());
}
server->UnLockThread();
};
//
// Set Turnout
//
void Session::SetJSONTurnout(JSONParse *jp) {
JSONParse jout;
string name;
int active;
server->LockThread();
jp->GetValue("name", &name);
jp->GetValueInt("activate", &active);
if (name.length() > 0) {
//
// activate
debug (0, "%s:%d SetJSONTurnout Element %s active:%d", __FILE__, __LINE__, name.c_str(), active);
server->TurnoutSet(name, active);
jout.Clear();
jout.AddObject("turnout", server->TurnoutGetJSON(name));
if (network) network->_ChangeListPushToAll(jout.ToString());
}
server->UnLockThread();
};
/*
res = json.GetValue("command", &value);
if (res && value.compare("addelemens") == 0) {
for (i = 0; json.GetObjectIdx("elements", i, &jelement) != 0; i++) {
s = "";
jelement.GetValue("type", &s);
// printf ("i:%d t:'%s'\n", i, s.c_str());
}
}
snprintf (bufferout, BUFFERSIZE, "{\"success\":1,\"sid\":123}");
client->Write(bufferout, strlen(bufferout));
debug (0, "* write:\n%s\n* %d bytes", bufferout, strlen(bufferout));
result = 1;
*/

@ -0,0 +1,26 @@
#include <iostream>
#include "json.h"
using namespace std;
int main(int argc, char **argv) {
string input;
string json_line;
JSONParse json;
list<JSONElement> l;
list<JSONElement>::iterator iter;
for (json_line = "", input = ""; getline(cin, input);)
json_line += input;
json.Set(json_line);
l = json.GetElements();
for (iter = l.begin(); iter != l.end(); iter++) {
cout << iter->name << endl;
}
return 0;
};

@ -0,0 +1,262 @@
#include "modelbahn.h"
#include "turnout.h"
Turnouts::Turnouts() {
changed = 0;
turnouts = (Turnout*) malloc(sizeof(Turnout)*TURNOUTS_MAX);
max = TURNOUTS_MAX;
};
Turnouts::~Turnouts() {
free (turnouts);
max = 0;
turnouts = NULL;
};
int Turnouts::Lock() {
if (pthread_mutex_lock(&mtx) == 0) return 1;
else return 0;
}
int Turnouts::UnLock() {
if (pthread_mutex_unlock(&mtx) == 0) return 1;
else return 0;
}
JSONParse Turnouts::_GetJSON(int idx) {
JSONParse json;
JSONElement je;
string s = "";
json.Clear();
s = turnouts[idx].name; json.AddObject("name", s);
s = turnouts[idx].ifname; json.AddObject("ifname", s);
json.AddObject("addr", turnouts[idx].addr);
json.AddObject("activetimeout", turnouts[idx].activetimeout);
json.AddObject("flags", turnouts[idx].flags);
return json;
};
JSONParse Turnouts::GetJSON(string name) {
int i;
JSONParse jp;
jp.Clear();
Lock();
for (i = 0; i < max; i++) if (turnouts[i].name[0] != 0) {
if (name.compare(turnouts[i].name) == 0) {
jp = _GetJSON(i);
}
}
UnLock();
return jp;
};
void Turnouts::GetJSONAll(JSONParse *json) {
int i, cnt;
JSONElement je;
Lock();
//
// write all railway data
// create json object array manualy
je.type = JSON_T_ARRAY;
je.name = "turnouts";
for (cnt = 0, i = 0; i < max; i++)
if (turnouts[i].name[0] != 0) {
if (cnt != 0) je.value += ","; // not first element
je.value += _GetJSON(i).ToString();
cnt++;
}
json->AddObject(je);
UnLock();
};
Turnout Turnouts::GetTurnoutFromJSON(JSONParse *j) {
Turnout to;
string s;
to.name[0] = 0;
to.ifname[0] = 0;
to.addr = 0;
to.activetimeout = TURNOUT_DEFAULT_ACTIVETIMEOUT; // default active timeout in MS
to.flags = 0;
j->GetValue("name", &s);
strncpy (to.name, s.c_str(), REFERENCENAME_LEN);
j->GetValue("ifname", &s);
strncpy (to.ifname, s.c_str(), REFERENCENAME_LEN);
j->GetValueInt("addr", &to.addr);
j->GetValueInt("activetimeout", &to.activetimeout);
j->GetValueInt("flags", &to.flags);
return to;
};
int Turnouts::Change(Turnout *to) {
int i;
int ifree = -1;
Lock();
for (i = 0; i < max; i++) {
if (turnouts[i].name[0] != 0) {
// found element
if (strncmp(turnouts[i].name, to->name, REFERENCENAME_LEN) == 0) {
ifree = i;
break;
}
}
else if (ifree == -1) ifree = i;
}
// element not found add new element
if (ifree != -1 && ifree < max) {
turnouts[ifree] = *to;
strncpy (turnouts[ifree].name, to->name, REFERENCENAME_LEN);
strncpy (turnouts[ifree].ifname, to->ifname, REFERENCENAME_LEN);
turnouts[ifree].activetimeout = to->activetimeout;
}
changed = 1;
UnLock();
return 1;
};
int Turnouts::Delete(string name) {
int i;
Lock();
for (i = 0; i < max; i++) if (turnouts[i].name[0] != 0) {
if (name.compare(turnouts[i].name) == 0) {
turnouts[i].name[0] = 0;
turnouts[i].ifname[0] = 0;
turnouts[i].addr = 0;
turnouts[i].flags = 0;
turnouts[i].activetimeout = TURNOUT_DEFAULT_ACTIVETIMEOUT;
changed = 1;
break;
}
}
UnLock();
return 1;
};
int Turnouts::Set(string name, int value) {
int i;
//
Lock();
for (i = 0; i < max; i++) if (turnouts[i].name[0] != 0) {
if (name.compare(turnouts[i].name) == 0) {
debug (0, "Turnout::Set: Name:%s Flags:%d[%c%c%c] Value:%d", name.c_str(), turnouts[i].flags,
(turnouts[i].flags & TURNOUT_F_INVERSE) ? 'I' : '-',
(turnouts[i].flags & TURNOUT_F_ACTIVE) ? 'A' : '-',
(turnouts[i].flags & TURNOUT_F_TURNOUT) ? 'T' : '-',
value);
if (turnouts[i].flags & TURNOUT_F_INVERSE)
server->interfaces.SetTurnout(&turnouts[i], !value, 1); // motor on
else
server->interfaces.SetTurnout(&turnouts[i], value, 1); // motor on
gettimeofday (&turnouts[i].activatetime, NULL);
changed = 1;
break;
}
}
UnLock();
return 1;
};
//
// got some data from bus
void Turnouts::SetFromBus(string ifname, int addr, int value) {
int i;
JSONParse jp;
debug (0, "Turnouts::SetFromBus Interface:%s, addr: %d, value:%d", ifname.c_str(), addr, value);
for (i = 0; i < max; i++) if (turnouts[i].name[0] != 0) {
if (ifname.compare(turnouts[i].ifname) == 0 && turnouts[i].addr == addr) {
debug (0, "Turnout::SetFromBus Name:%s Flags:%d[%c%c%c]", turnouts[i].name, turnouts[i].flags,
(turnouts[i].flags & TURNOUT_F_INVERSE) ? 'I' : '-',
(turnouts[i].flags & TURNOUT_F_ACTIVE) ? 'A' : '-',
(turnouts[i].flags & TURNOUT_F_TURNOUT) ? 'T' : '-');
// if (value) turnouts[i].flags |= TURNOUT_F_ACTIVE;
// else turnouts[i].flags &= ~TURNOUT_F_ACTIVE;
if (turnouts[i].flags & TURNOUT_F_INVERSE) {
if (value) turnouts[i].flags &= ~TURNOUT_F_TURNOUT;
else turnouts[i].flags |= TURNOUT_F_TURNOUT;
}
else {
if (value) turnouts[i].flags |= TURNOUT_F_TURNOUT;
else turnouts[i].flags &= ~TURNOUT_F_TURNOUT;
}
debug (0, "Turnout::SetFromBus Name:%s Flags:%d[%c%c%c]", turnouts[i].name, turnouts[i].flags,
(turnouts[i].flags & TURNOUT_F_INVERSE) ? 'I' : '-',
(turnouts[i].flags & TURNOUT_F_ACTIVE) ? 'A' : '-',
(turnouts[i].flags & TURNOUT_F_TURNOUT) ? 'T' : '-');
jp.AddObject("turnout", _GetJSON(i));
if(network) network->ChangeListPushToAll(jp.ToString());
}
}
};
//
// this loop is important: Turnout motors needs to be deactivated after a short time
//
void Turnouts::Loop() {
int i;
struct timeval curtime;
gettimeofday(&curtime, NULL);
Lock();
for (i = 0; i < max; i++) if (turnouts[i].name[0] != 0) {
if (turnouts[i].flags & TURNOUT_F_ACTIVE) {
//
// motor still active check timeout and deactivate
int timediff = (1000 * (curtime.tv_sec - turnouts[i].activatetime.tv_sec)) +
((curtime.tv_usec - turnouts[i].activatetime.tv_usec) / 1000);
if (timediff < 0) gettimeofday (&turnouts[i].activatetime, NULL);
// debug (0, "%s:%d timediff: %d", __FILE__, __LINE__, timediff);
if (timediff > turnouts[i].activetimeout) {
int active = turnouts[i].flags & TURNOUT_F_TURNOUT;
if (turnouts[i].flags & TURNOUT_F_INVERSE)
server->interfaces.SetTurnout(&turnouts[i], !active, 0); // motor on
else
server->interfaces.SetTurnout(&turnouts[i], active, 0); // motor on
turnouts[i].flags &= ~TURNOUT_F_ACTIVE;
}
}
}
UnLock();
};

@ -0,0 +1,56 @@
#ifndef _TURNOUT_H_
#define _TURNOUT_H_
#include "modelbahn.h"
#include "server.h"
//
#define TURNOUT_F_INVERSE 0x0001 // inverse output
#define TURNOUT_F_ACTIVE 0x0002 // motor active
#define TURNOUT_F_TURNOUT 0x0004 // turnout active
#define TURNOUT_DEFAULT_ACTIVETIMEOUT 250 // active timeout default value
struct s_Turnout {
char name[REFERENCENAME_LEN]; // reference name
char ifname[REFERENCENAME_LEN]; // controllername
int addr; // address on bus
int flags; // setup of some flags;
int activetimeout; // time in ms // 0 will be set to DEFAULT
struct timeval activatetime; // set both to 0 for inactive
} typedef Turnout;
class Turnouts {
private:
Turnout *turnouts;
int max;
int changed;
pthread_mutex_t mtx;
int Lock();
int UnLock();
// not thread safe
JSONParse _GetJSON(int idx);
public:
Turnouts();
~Turnouts();
bool IsChanged() { return changed; };
void ClearChanged() { changed = 0; };
int Change(Turnout *to);
int Delete(string name);
int Set(string name, int active);
void Loop();
JSONParse GetJSON(string name);
void GetJSONAll(JSONParse *json);
Turnout GetTurnoutFromJSON(JSONParse *j);
void SetFromBus(string name, int addr, int active);
};
#endif

@ -0,0 +1,213 @@
//
//
//
var blocks = [];
//
// update or add a new element
//
function block_Update(blockdata) {
for (var i = 0; i < blocks.length; i++) {
if (blockdata.name == blocks[i].name) {
blocks[i].name = blockdata.name;
blocks[i].flags = blockdata.flags;
return;
}
}
// not found add element
//debug ("Add Interface:" + intdata.name + "(" + intdata.host + ")");
blocks.push ({
name: blockdata.name,
flags: blockdata.flags
});
};
//
// delete element from the list
// in arrays we can not delete just an element, so we create a new one
// and replace this one.
//
function block_Delete(name) {
var l = new Array();
for (var i = 0; i < blocks.length; i++) {
if (name != blocks[i].name) {
l.push (blocks[i]);
}
}
// clear list and replace list with new data.
blocks.lenght = 0;
blocks = l;
};
//
// send new element to server
//
function block_server_Add(elm) {
var request = { command: "addblock", block: elm };
serverinout (request, serverinout_defaultCallback);
};
//
// send delete element to server
//
function block_server_Del(elm) {
var request = { command: "delblock", block: elm };
serverinout (request, serverinout_defaultCallback);
};
function blockdetail_show(name) {
var win = document.getElementById("blockdetail");
debug ("blockdetail_show");
if (!win) {
debug ("blockdetail_show create window");
win = gWindowCreate("blockdetail", "Block", 400, 300, " \
<div style=\"float: left\"> \
Block Name: <input id=\"blockdet_name\" style=\"width: 100\"> \
</div> <div style=\"float: right\"> \
<button id=\"blockdet_PREV\">&lt</button> \
<button id=\"blockdet_NEXT\">&gt</button> \
</div> <br> <hr>\
<div> \
<table><tr><td><table>\
</td></tr></table> </div> <hr>\
<div align=right> \
<button id=\"blockdet_SAVE\" type=\"button\">Save</button> \
<button id=\"blockdet_DELETE\" type=\"button\">Delete</button> \
<button id=\"blockdet_CLOSE\">Close</button> \
</div> \
\
");
gAddEventListener("blockdet_CLOSE", 'click', blockdetail_cb_close);
gAddEventListener("blockdet_DELETE", 'click', blockdetail_cb_delete);
gAddEventListener("blockdet_SAVE", 'click', blockdetail_cb_save);
gAddEventListener("blockdet_NEXT", 'click', blockdetail_cb_next);
gAddEventListener("blockdet_PREV", 'click', blockdetail_cb_prev);
}
if (name) {
for (var i = 0; i < blocks.length; i++) {
if (name == blocks[i].name) blockdetail_setData(blocks[i]);
}
}
};
function blockdetail_cb_close () {
var win = document.getElementById("blockdetail");
if (win) document.body.removeChild(win);
};
//
// Callback: Delete Button
//
function blockdetail_cb_delete () {
var elm = {};
elm = blockdetail_getData();
block_Delete(elm.name);
block_server_Del(elm);
};
//
// Callback: Save Button
//
function blockdetail_cb_save () {
var elm = {};
elm = blockdetail_getData();
block_Update(elm);
block_server_Add(elm);
};
//
// Callback: Next Button
//
function blockdetail_cb_next () {
var cursel = -1;
var name = document.getElementById("blockdet_name");
for (var i = 0; i < blocks.length; i++) {
if (name.value == blocks[i].name) cursel = i;
}
cursel = cursel + 1;
if (cursel >= blocks.length) cursel = 0;
if (cursel < 0) cursel = 0;
for (var i = 0; i < blocks.length; i++) {
if (i == cursel) blockdetail_setData(blocks[i]);
}
// debug ("Cursel: " + cursel + " interfaces.lenght:" + interfaces.length);
};
//
// Callback: Prev Button
//
function blockdetail_cb_prev () {
var cursel = -1;
var name = document.getElementById("blockdet_name");
for (var i = 0; i < blocks.length; i++) {
if (name.value == blocks[i].name) cursel = i;
}
cursel = cursel - 1;
if (cursel < 0 || cursel >= blocks.length) cursel = blocks.length - 1;
for (var i = 0; i < blocks.length; i++) {
if (i == cursel) blockdetail_setData(blocks[i]);
}
// debug ("Cursel: " + cursel + " interfaces.lenght:" + interfaces.length);
};
//
// fill out all the elements on the dialogbox
//
function blockdetail_setData(elm) {
var name = document.getElementById("blockdet_name");
var flags = document.getElementById("blockdet_flags");
if (elm) {
if (name) name.value = elm.name;
if (flags) flags.value = elm.flags;
}
};
//
// return all elements from the dialogbox
//
function blockdetail_getData() {
var res = { name: "", flags:0 };
var name = document.getElementById("blockdet_name");
var flags = document.getElementById("blockdet_flags");
if (name) res.name = name.value;
if (flags) res.flags = flags.value;
return res;
};

@ -0,0 +1,30 @@
.GUIwindow {
float:left;
border: 2px solid black;
margin: 1px;
padding: 0px;
position: absolute;
}
.GUIwindowHead {
border: 1px solid black;
background: blue;
color: white;
margin: 0px;
padding: 5px;
cursor: move;
text-align: center;
}
.GUIwindowClient {
overflow: auto;
padding: 5px;
background-color: white;
}
.GUIbutton {
}

@ -0,0 +1,21 @@
//
// init all variables with the class givin
$(document).ready(function() {
// debug ("init");
$(".GUIwindow").each( function (i) {
gWindowDragElement(this);
});
});
//
//
function gAddEventListener (id, eventname, callback) {
var obj = document.getElementById(id);
if (obj) obj.addEventListener(eventname, callback);
};

@ -0,0 +1,9 @@
function debug (t) {
var pre = document.getElementById("debug");
var div = document.getElementById("debug_div");
if (pre) pre.innerHTML = pre.innerHTML + "<br>" + t;
if (div) div.scrollTop = div.scrollHeight;
};

@ -0,0 +1,137 @@
function gWindowGetClient(elmnt) {
var notes = null;
var result = null;
for (var i = 0; i < elmnt.childNodes.length; i++) {
if (elmnt.childNodes[i].className == "GUIwindowClient") {
result = elmnt.childNodes[i];
break;
}
}
return result;
}
function gWindowGetHead(elmnt) {
var notes = null;
var result = null;
for (var i = 0; i < elmnt.childNodes.length; i++) {
if (elmnt.childNodes[i].className == "GUIwindowHead") {
result = elmnt.childNodes[i];
break;
}
}
return result;
};
function gWindowDragElement(elmnt) {
var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
if (gWindowGetHead(elmnt) != null) {
// if present, the header is where you move the DIV from:
gWindowGetHead(elmnt).onmousedown = dragMouseDown;
} else {
// otherwise, move the DIV from anywhere inside the DIV:
elmnt.onmousedown = dragMouseDown;
}
function dragMouseDown(e) {
e = e || window.event;
e.preventDefault();
// get the mouse cursor position at startup:
pos3 = e.clientX;
pos4 = e.clientY;
document.onmouseup = closeDragElement;
// call a function whenever the cursor moves:
document.onmousemove = elementDrag;
}
function elementDrag(e) {
e = e || window.event;
e.preventDefault();
// calculate the new cursor position:
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
// set the element's new position:
elmnt.style.top = (elmnt.offsetTop - pos2 - 1) + "px";
elmnt.style.left = (elmnt.offsetLeft - pos1 - 1) + "px";
}
function closeDragElement() {
// stop moving when mouse button is released:
document.onmouseup = null;
document.onmousemove = null;
}
}
function gWindowCreate(id, title, sizex, sizey, clientHTML) {
var win = document.getElementById(id);
if (!win) {
debug("create Title:" + title);
var head = document.createElement("div");
head.setAttribute("id", id+"Head");
head.setAttribute("class", "GUIwindowHead");
head.innerHTML = title;
var client = document.createElement("div");
client.setAttribute("id", id+"Client");
client.setAttribute("class", "GUIwindowClient");
client.setAttribute("style", "max-height:"+sizey+"px;max-width:"+sizex+"px;");
client.innerHTML = clientHTML;
win = document.createElement("div");
win.setAttribute("id", id);
win.setAttribute("class", "GUIwindow");
win.appendChild (head);
win.appendChild (client);
document.body.appendChild(win);
gWindowDragElement(win);
debug ("move to 100px from top");
win.style.top = "100px";
}
return win;
}
function gWindowCreateSize(id, title, sizex, sizey) {
var win = document.getElementById(id);
if (!win) {
debug("create Title:" + title);
var head = document.createElement("div");
head.setAttribute("id", id+"Head");
head.setAttribute("class", "GUIwindowHead");
head.innerHTML = title;
var client = document.createElement("div");
client.setAttribute("id", id+"Client");
client.setAttribute("class", "GUIwindowClient");
client.setAttribute("style", "max-height:"+sizey+"px;max-width:"+sizex+"px;");
client.style.height = sizey+"px";
client.style.width = sizex+"px";
win = document.createElement("div");
win.setAttribute("id", id);
win.setAttribute("class", "GUIwindow");
win.appendChild (head);
win.appendChild (client);
document.body.appendChild(win);
gWindowDragElement(win);
debug ("move to 100px from top");
win.style.top = "100px";
}
return win;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 365 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 365 B

@ -0,0 +1,107 @@
<html>
<head>
<title>Modelbahn</title>
<script src="jquery-3.1.0.min.js"></script>
<script src="gui/guidebug.js"></script>
<script src="gui/guiwindow.js"></script>
<script src="gui/gui.js"></script>
<script src="serverinout.js"></script>
<script src="interface.js"></script>
<script src="railways.js"></script>
<script src="locomotive.js"></script>
<script src="sensor.js"></script>
<script src="track.js"></script>
<script src="side.js"></script>
<script src="block.js"></script>
<script src="testwindow.js"></script>
<link rel="stylesheet" href="gui/gui.css">
<link rel="stylesheet" href="layout.css">
</head>
<body>
<div class="navbar">
<a href="#home">Home</a>
<div class="dropdown">
<button class="dropbtn">Server
<i class="fa fa-caret-down"></i>
</button>
<div class="dropdown-content">
<a href="#" onclick="serverinout_Save(this);">Save</a>
<a href="#" onclick="debug_Enable(this);">Debug</a>
</div>
</div>
<div class="dropdown">
<button class="dropbtn">Configuration
<i class="fa fa-caret-down"></i>
</button>
<div class="dropdown-content">
<a href="#" onclick="side_Display(SIDE_DISPLAY_EDITTRACK);">Track</a>
<a href="#" onclick="intdetail_show(this);">Interfaces</a>
<a href="#" onclick="turndetail_show(this);">Turnouts</a>
<a href="#" onclick="locodetail_show(this);">Locomotives</a>
<a href="#" onclick="sensordetail_show(this);">Sensors</a>
<a href="#" onclick="blockdetail_show(this);">Block</a>
</div>
</div>
<div id="infoserverstatus" style="float:right; padding: 8px 8px;">notconnected</div>
<div id="infoline" style="float:right; padding: 8px 8px;">editinfo</div>
</div>
<div class="page_side">
<button class="side_btn" id="btn-onoff" onclick="sideBtnOnOffClick(this);" value="0">
<img src="images/btnonoff.png"></button>
<br><br>
<div class="page_side_edit" id="side_trackedit" style="display: none;">
<button class="side_btn_mode side_btn_selected" id="mode-none" onclick="sideBtnModeClick(this);" value="none">
<img src="images/btnarrow.png"></button>
<button class="side_btn_mode" id="mode-move" onclick="sideBtnModeClick(this);" value="move">
<img src="images/btnmove.png"></button>
<button class="side_btn_mode" id="mode-delete" onclick="sideBtnModeClick(this);" value="delete">
<img src="images/btndelete.png"></button>
<button class="side_btn_mode" id="mode-rail" onclick="sideBtnModeClick(this);" value="rail">
<img src="images/btnrail.png"></button>
<button class="side_btn_mode" id="mode-turn" onclick="sideBtnModeClick(this);" value="turnout">
<img src="images/btnturnout.png"></button>
<br><br>
</div>
</div>
<div class="page_main" id="page_main"></div>
<div class="page_bottom" id="debug_div">
<pre id="debug"></pre>
</div>
<script>
function debug_Enable() {
var x = document.getElementById("debug_div");
if (x.style.display === "none") {
$(':root').css('--bottom-height', '256px');
x.style.display = "block";
} else {
$(':root').css('--bottom-height', '0px');
x.style.display = "none";
}
}
$(document).ready(function() {
var client = document.getElementById("page_main");
var canvas = trackCreate(5, 5);
var text = document.getElementById("infoline");
text.innerHTML = "---";
client.appendChild (canvas);
trackDraw();
serverinout_AutoUpdate();
});
</script>
</body>
</html>

@ -0,0 +1,273 @@
//
//
//
var interfaces = [];
//
// update or add a new element
//
function interface_Update(intdata) {
for (var i = 0; i < interfaces.length; i++) {
if (intdata.name == interfaces[i].name) {
//debug ("Update Interface:" + interfaces[i].name + "(" + interfaces[i].host + ") with Interface:" + intdata.name + "(" + intdata.host + ")");
if (!(intdata.flags & 0x0001)) sideBtnOnOffMode (3); // not connected
else if ((intdata.flags & 0x0010)) sideBtnOnOffMode (3); // programming mode
else if ((intdata.flags & 0x0008)) sideBtnOnOffMode (3); // short circuit
else if ((intdata.flags & 0x0004)) sideBtnOnOffMode (2); // stop
else if (!(intdata.flags & 0x0002)) sideBtnOnOffMode (1); // power on
else sideBtnOnOffMode (0);
interfaces[i].name = intdata.name;
interfaces[i].host = intdata.host;
interfaces[i].flags = intdata.flags;
interfaces[i].type = intdata.type;
return;
}
}
// not found add element
//debug ("Add Interface:" + intdata.name + "(" + intdata.host + ")");
interfaces.push ({
name: intdata.name,
host: intdata.host,
flags: intdata.flags,
type: intdata.type
});
};
//
// delete element from the list
// in arrays we can not delete just an element, so we create a new one
// and replace this one.
//
function interface_Delete(name) {
var l = new Array();
for (var i = 0; i < interfaces.length; i++) {
if (name != interfaces[i].name) {
l.push (interfaces[i]);
}
}
// clear list and replace list with new data.
interfaces.lenght = 0;
interfaces = l;
};
//
// send new element to server
//
function interface_server_Add(elm) {
var request = { command: "addinterface", interface: elm };
serverinout (request, serverinout_defaultCallback);
};
//
// send delete element to server
//
function interface_server_Del(elm) {
var request = { command: "delinterface", interface: elm };
serverinout (request, serverinout_defaultCallback);
};
function intdetail_show(intname) {
var win = document.getElementById("intdetail");
debug ("intdetail_show");
if (!win) {
debug ("intdetail_show create window");
win = gWindowCreate("intdetail", "Interface", 400, 300, " \
<div style=\"float: left\"> \
Interface Name: <input id=\"intdet_name\" style=\"width: 100\"> \
</div> <div style=\"float: right\"> \
<button id=\"intdet_PREV\">&lt</button> \
<button id=\"intdet_NEXT\">&gt</button> \
</div> <br> <hr>\
<div> \
<table><tr><td><table>\
<tr><td>Host:</td><td><input id=\"intdet_host\" style=\"width: 200\"></td></tr> \
<tr><td>Flags:</td><td><input id=\"intdet_flags\" style=\"width: 50\"></td></tr> \
</table></td><td> \
<fieldset><legend>Type</legend> \
Type: <input id=\"intdet_type\" style=\"width: 25\"><br> \
<label><input type=\"radio\" id=\"intdet_typeunknown\" name=\"Type\" value=\"0\">Unkown</label><br> \
<label><input type=\"radio\" id=\"intdet_typez21\" name=\"Type\" value=\"1\">Z21</label><br> \
</fieldset> \
</td></tr></table> </div> <hr>\
<div align=right> \
<button id=\"intdet_SAVE\" type=\"button\">Save</button> \
<button id=\"intdet_DELETE\" type=\"button\">Delete</button> \
<button id=\"intdet_CLOSE\">Close</button> \
</div> \
\
");
gAddEventListener("intdet_type", 'change', intdetail_cb_typechange);
gAddEventListener("intdet_typeunknown", 'click', intdetail_cb_typeselector);
gAddEventListener("intdet_typez21", 'click', intdetail_cb_typeselector);
gAddEventListener("intdet_CLOSE", 'click', intdetail_cb_close);
gAddEventListener("intdet_DELETE", 'click', intdetail_cb_delete);
gAddEventListener("intdet_SAVE", 'click', intdetail_cb_save);
gAddEventListener("intdet_NEXT", 'click', intdetail_cb_next);
gAddEventListener("intdet_PREV", 'click', intdetail_cb_prev);
}
if (intname) {
for (var i = 0; i < interfaces.length; i++) {
if (intname == interfaces[i].name) intdetail_setData(interfaces[i]);
}
}
};
function intdetail_cb_close () {
var win = document.getElementById("intdetail");
if (win) document.body.removeChild(win);
};
//
// Callback: Delete Button
//
function intdetail_cb_delete () {
var elm = {};
elm = intdetail_getData();
interface_Delete(elm.name);
interface_server_Del(elm);
};
//
// Callback: Save Button
//
function intdetail_cb_save () {
var elm = {};
elm = intdetail_getData();
interface_Update(elm);
interface_server_Add(elm);
};
//
// Callback: Next Button
//
function intdetail_cb_next () {
var cursel = -1;
var if_name = document.getElementById("intdet_name");
for (var i = 0; i < interfaces.length; i++) {
if (if_name.value == interfaces[i].name) cursel = i;
}
cursel = cursel + 1;
if (cursel >= interfaces.length) cursel = 0;
if (cursel < 0) cursel = 0;
for (var i = 0; i < interfaces.length; i++) {
if (i == cursel) intdetail_setData(interfaces[i]);
}
// debug ("Cursel: " + cursel + " interfaces.lenght:" + interfaces.length);
};
//
// Callback: Prev Button
//
function intdetail_cb_prev () {
var cursel = -1;
var if_name = document.getElementById("intdet_name");
for (var i = 0; i < interfaces.length; i++) {
if (if_name.value == interfaces[i].name) cursel = i;
}
cursel = cursel - 1;
if (cursel < 0 || cursel >= interfaces.length) cursel = interfaces.length - 1;
for (var i = 0; i < interfaces.length; i++) {
if (i == cursel) intdetail_setData(interfaces[i]);
}
// debug ("Cursel: " + cursel + " interfaces.lenght:" + interfaces.length);
};
//
// fill out all the elements on the dialogbox
//
function intdetail_setData(elm) {
var if_name = document.getElementById("intdet_name");
var if_host = document.getElementById("intdet_host");
var if_flags = document.getElementById("intdet_flags");
var if_type = document.getElementById("intdet_type");
if (elm) {
if (if_name) if_name.value = elm.name;
if (if_host) if_host.value = elm.host;
if (if_flags) if_flags.value = elm.flags;
if (if_flags) if_type.value = elm.type;
}
intdetail_cb_typechange();
};
function intdetail_cb_typechange () {
var type = document.getElementById("intdet_type");
var typez21 = document.getElementById("intdet_typez21");
var typeunknown = document.getElementById("intdet_typeunknown");
switch(Number(type.value)) {
case 0: typeunknown.checked = true; break;
case 1: typez21.checked = true; break;
default:
type.value = 0;
typeunknown.checked = true;
break;
}
};
function intdetail_cb_typeselector () {
var type = document.getElementById("intdet_type");
type.value = this.value;
};
//
// return all elements from the dialogbox
//
function intdetail_getData() {
var res = { name: "", host: "", flags:0, type:0 };
var if_name = document.getElementById("intdet_name");
var if_host = document.getElementById("intdet_host");
var if_flags = document.getElementById("intdet_flags");
var if_type = document.getElementById("intdet_type");
if (if_name) res.name = if_name.value;
if (if_host) res.host = if_host.value;
if (if_flags) res.flags = if_flags.value;
if (if_type) res.type = if_type.value;
return res;
};

File diff suppressed because one or more lines are too long

@ -0,0 +1,158 @@
body {
margin: 0px;
}
:root {
--top-height: 34px;
--side-width: 37px;
--bottom-height: 32px;
--menu-bg-color: #333;
--menu-fg-color: linen;
}
.page_side {
position: absolute;
top: var(--top-height);
left: 0px;
width: var(--side-width);
height: calc(100vh - var(--top-height) - var(--bottom-height));
max-height: calc(100vh - var(--top-height) - var(--bottom-height));
color: var(--menu-fg-color);
background: var(--menu-bg-color);
}
.side_btn_mode {
background-color: lightgray;
padding: 0px 0px;
margin: 0px 0px;
}
.side_btn {
background-color: lightgray;
padding: 0px 0px;
margin: 0px 0px;
}
.side_btn_selected{
background-color: gray;
padding: 0px 0px;
margin: 0px 0px;
}
.side_btn_poweron{
background-color: lightgreen;
padding: 0px 0px;
margin: 0px 0px;
}
.side_btn_poweroff{
background-color: lightgray;
padding: 0px 0px;
margin: 0px 0px;
}
.side_btn_shortcircuit{
background-color: red;
padding: 0px 0px;
margin: 0px 0px;
}
.side_btn_stop{
background-color: yellow;
padding: 0px 0px;
margin: 0px 0px;
}
.side_btn:hover {
background-color: white;
}
.side_btn_mode:hover {
background-color: white;
}
.page_main {
position: absolute;
top: var(--top-height);
left: var(--side-width);
width: calc(100vw - var(--side-width));
height: calc(100vh - var(--top-height) - var(--bottom-height));
background: lightgray;
overflow: auto;
}
.page_bottom {
position: absolute;
top: calc(100vh - var(--bottom-height));
left: 0px;
height: var(--bottom-height);
width: 100vw;
background: var(--menu-bg-color);
color: var(--menu-fg-color);
overflow: auto;
}
.navbar {
overflow: hidden;
color: var(--menu-fg-color);
background: var(--menu-bg-color);
}
.navbar a {
float: left;
font-size: 16px;
color: var(--menu-fg-color);
text-align: center;
padding: 8px 8px;
text-decoration: none;
}
.dropdown {
float: left;
overflow: hidden;
}
.dropdown .dropbtn {
font-size: 16px;
border: none;
outline: none;
color: var(--menu-fg-color);
padding: 8px 8px;
background-color: inherit;
font-family: inherit;
margin: 0;
}
.navbar a:hover, .dropdown:hover .dropbtn {
background-color: blue;
}
.dropdown-content {
display: none;
position: absolute;
background-color: #f9f9f9;
min-width: 160px;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
z-index: 1;
}
.dropdown-content a {
float: none;
color: black;
padding: 8px 16px;
text-decoration: none;
display: block;
text-align: left;
}
.dropdown-content a:hover {
background-color: #ddd;
}
.dropdown:hover .dropdown-content {
display: block;
}

@ -0,0 +1,360 @@
//
//
//
var locomotives = [];
//
// update or add a new element
//
function locomotive_Update(data) {
for (var i = 0; i < locomotives.length; i++) {
if (data.name == locomotives[i].name) {
debug ("Update Locomotive:" + locomotives[i].name + " with Locomotive:" + data.name);
locomotives[i].name = data.name;
locomotives[i].ifname = data.ifname;
locomotives[i].addr = data.addr;
locomotives[i].steps = data.steps;
locomotives[i].speed = data.speed;
locomotives[i].vmin = data.vmin;
locomotives[i].vslow = data.vslow;
locomotives[i].vmid = data.vmid;
locomotives[i].vfast = data.vfast;
locomotives[i].vmax = data.vmax;
locomotives[i].flags = data.flags;
return;
}
}
// not found add element
debug ("Add Locomotive:" + data.name);
locomotives.push ({
name: data.name,
ifname: data.ifname,
addr: data.addr,
steps: data.steps,
vmin: data.vmin,
vslow: data.vslow,
speed: data.speed,
vmid: data.vmid,
vfast: data.vfast,
vmax: data.vmax,
flags: data.flags
});
};
//
// delete element from the list
// in arrays we can not delete just an element, so we create a new one
// and replace this one.
//
function locomotive_Delete(name) {
var l = new Array();
for (var i = 0; i < locomotives.length; i++) {
if (name != locomotives[i].name) {
l.push (locomotives[i]);
}
}
// clear list and replace list with new data.
locomotives.lenght = 0;
locomotives = l;
};
//
// send new element to server
//
function locomotive_server_Add(elm) {
var request = { command: "addlocomotive", locomotive: elm };
serverinout (request, serverinout_defaultCallback);
};
//
// send delete element to server
//
function locomotive_server_Del(elm) {
var request = { command: "dellocomotive", locomotive: elm };
serverinout (request, serverinout_defaultCallback);
};
//
// send delete element to server
//
function locomotive_server_Set(elm) {
var request = { command: "setlocomotive", locomotive: elm };
serverinout (request, serverinout_defaultCallback);
};
function locodetail_show(loconame) {
var win = document.getElementById("locodetail");
debug ("locodetail_show");
if (!win) {
debug ("loco_showdetail create window");
win = gWindowCreate("locodetail", "Locomotive", 400, 500, " \
<div style=\"float: left\"> \
Name: <input id=\"locodet_name\" style=\"width: 100\"> \
</div> <div style=\"float: right\"> \
<button id=\"locodet_PREV\">&lt</button> \
<button id=\"locodet_NEXT\">&gt</button> \
</div> <br> <hr>\
<div> \
Interface: <input id=\"locodet_ifname\" style=\"width: 50\"> \
Adress: <input id=\"locodet_addr\" style=\"width: 50\"> \
Flags: <input id=\"locodet_flags\" style=\"width: 50\"> \
</div> <hr>\
<div> <table><tr><td> \
<fieldset><legend>Speed</legend> <table>\
<tr><td></td><td>Stop</td><td><button id=\"locodet_btnvstop\" type=\"button\" value=\"vstop\">X</button> </td></tr>\
<tr><td>Vmin:</td><td><input id=\"locodet_vmin\" style=\"width: 50\"></td><td><button id=\"locodet_btnvmin\" type=\"button\" value=\"vmin\">X</button> </td></tr>\
<tr><td>Vslow:</td><td><input id=\"locodet_vslow\" style=\"width: 50\"></td><td><button id=\"locodet_btnvslow\" type=\"button\" value=\"vslow\">X</button> </td></tr> \
<tr><td>Vmid:</td><td><input id=\"locodet_vmid\" style=\"width: 50\"></td><td><button id=\"locodet_btnvmid\" type=\"button\" value=\"vmid\">X</button> </td></tr> \
<tr><td>Vfast:</td><td><input id=\"locodet_vfast\" style=\"width: 50\"></td><td><button id=\"locodet_btnvfast\" type=\"button\" value=\"vfast\">X</button> </td></tr> \
<tr><td>Vmax:</td><td><input id=\"locodet_vmax\" style=\"width: 50\"></td><td><button id=\"locodet_btnvmax\" type=\"button\" value=\"vmax\">X</button> </td></tr> \
</table></td></fieldset> \
<td> \
Steps: <input id=\"locodet_steps\" style=\"width: 50\"><br> \
Speed: <input id=\"locodet_speed\" style=\"width: 50\"><br> \
<label><input id=\"locodet_reverse\" type=\"checkbox\" value=\"\"> Reverse</label> \
</td></tr></table></div> <hr>\
<div align=right> \
<button id=\"locodet_SAVE\" type=\"button\">Save</button> \
<button id=\"locodet_DELETE\" type=\"button\">Delete</button> \
<button id=\"locodet_CLOSE\" type=\"button\">Close</button> \
</div> \
\
");
gAddEventListener("locodet_btnvstop", 'click', locodetail_cb_btnmove);
gAddEventListener("locodet_btnvmin", 'click', locodetail_cb_btnmove);
gAddEventListener("locodet_btnvslow", 'click', locodetail_cb_btnmove);
gAddEventListener("locodet_btnvmid", 'click', locodetail_cb_btnmove);
gAddEventListener("locodet_btnvfast", 'click', locodetail_cb_btnmove);
gAddEventListener("locodet_btnvmax", 'click', locodetail_cb_btnmove);
gAddEventListener("locodet_reverse", 'click', locodetail_cb_reverse);
gAddEventListener("locodet_CLOSE", 'click', locodetail_cb_close);
gAddEventListener("locodet_DELETE", 'click', locodetail_cb_delete);
gAddEventListener("locodet_SAVE", 'click', locodetail_cb_save);
gAddEventListener("locodet_NEXT", 'click', locodetail_cb_next);
gAddEventListener("locodet_PREV", 'click', locodetail_cb_prev);
}
//
// load default values
var res = { name: "", ifname: "", addr: "", flags: 0, steps: "",
vmin: "20", vslow: "40", vmid:"60", vfast:"80", vmax:"100" };
locodetail_setData(res);
if (loconame) {
for (var i = 0; i < locomotives.length; i++) {
if (loconame == locomotives[i].name) locodetail_setData(locomotives[i]);
}
}
};
//
// reverse selected, setup flags
function locodetail_cb_reverse () {
var cbreverse = document.getElementById("locodet_reverse");
var flags = document.getElementById("locodet_flags");
if (cbreverse.checked) {
flags.value = Number(flags.value) | 1;
}
else {
flags.value = Number(flags.value) & (0xFFFF-1);
}
};
function locodetail_cb_btnmove () {
var win = document.getElementById("locodetail");
var loco_name = document.getElementById("locodet_name");
var loco_vmin = document.getElementById("locodet_vmin");
var loco_vslow = document.getElementById("locodet_vslow");
var loco_vmid = document.getElementById("locodet_vmid");
var loco_vfast = document.getElementById("locodet_vfast");
var loco_vmax = document.getElementById("locodet_vmax");
var loco_reverse = document.getElementById("locodet_reverse");
var speed = 0;
if (this.value == "vmin") speed = Number(loco_vmin.value);
if (this.value == "vslow") speed = Number(loco_vslow.value);
if (this.value == "vmid") speed = Number(loco_vmid.value);
if (this.value == "vfast") speed = Number(loco_vfast.value);
if (this.value == "vmax") speed = Number(loco_vmax.value);
if (loco_reverse.checked) {
speed = 0 - speed;
}
locomotive_server_Set ({name: loco_name.value, speed: speed});
debug ("Locomotive: '" + loco_name.value +"' Speed: " + speed);
};
function locodetail_cb_close () {
var win = document.getElementById("locodetail");
if (win) document.body.removeChild(win);
};
//
// Callback: Delete Button
//
function locodetail_cb_delete () {
var elm = {};
elm = locodetail_getData();
locomotive_Delete(elm.name);
locomotive_server_Del(elm);
};
//
// Callback: Save Button
//
function locodetail_cb_save () {
var elm = {};
elm = locodetail_getData();
locomotive_Update(elm);
locomotive_server_Add(elm);
};
//
// Callback: Next Button
//
function locodetail_cb_next () {
var cursel = -1;
var loconame = document.getElementById("locodet_name");
for (var i = 0; i < locomotives.length; i++) {
if (loconame.value == locomotives[i].name) cursel = i;
}
cursel = cursel + 1;
if (cursel >= locomotives.length) cursel = 0;
if (cursel < 0) cursel = 0;
for (var i = 0; i < locomotives.length; i++) {
if (i == cursel) locodetail_setData(locomotives[i]);
}
debug ("Cursel: " + cursel + " locomotives.lenght:" + locomotives.length);
};
//
// Callback: Prev Button
//
function locodetail_cb_prev () {
var cursel = -1;
var loconame = document.getElementById("locodet_name");
for (var i = 0; i < locomotives.length; i++) {
if (loconame.value == locomotives[i].name) cursel = i;
}
cursel = cursel - 1;
if (cursel < 0 || cursel >= locomotives.length) cursel = locomotives.length - 1;
for (var i = 0; i < locomotives.length; i++) {
if (i == cursel) locodetail_setData(locomotives[i]);
}
debug ("Cursel: " + cursel + " locomotives.lenght:" + locomotives.length);
};
//
// fill out all the elements on the dialogbox
//
function locodetail_setData(elm) {
var loco_name = document.getElementById("locodet_name");
var loco_ifname = document.getElementById("locodet_ifname");
var loco_addr = document.getElementById("locodet_addr");
var loco_speed = document.getElementById("locodet_speed");
var loco_flags = document.getElementById("locodet_flags");
var loco_steps = document.getElementById("locodet_steps");
var loco_vmin = document.getElementById("locodet_vmin");
var loco_vslow = document.getElementById("locodet_vslow");
var loco_vmid = document.getElementById("locodet_vmid");
var loco_vfast = document.getElementById("locodet_vfast");
var loco_vmax = document.getElementById("locodet_vmax");
var loco_reverse = document.getElementById("locodet_reverse");
if (elm) {
if (loco_name) loco_name.value = elm.name;
if (loco_ifname) loco_ifname.value = elm.ifname;
if (loco_flags) loco_flags.value = elm.flags;
if (loco_addr) loco_addr.value = elm.addr;
if (loco_speed) loco_speed.value = elm.speed;
if (loco_steps) loco_steps.value = elm.steps;
if (loco_vmin) loco_vmin.value = elm.vmin;
if (loco_vslow) loco_vslow.value = elm.vslow;
if (loco_vmid) loco_vmid.value = elm.vmid;
if (loco_vfast) loco_vfast.value = elm.vfast;
if (loco_vmax) loco_vmax.value = elm.vmax;
if (loco_reverse) {
if (Number(elm.flags) & 1) loco_reverse.checked = true;
else loco_reverse.checked = false;
}
}
};
//
// return all elements from the dialogbox
//
function locodetail_getData() {
var res = { name: "", ifname: "", addr: "", flags: 0, steps: "",
vmin: "20", vslow: "40", vmid:"60", vfast:"80", vmax:"100" };
var loco_name = document.getElementById("locodet_name");
var loco_ifname = document.getElementById("locodet_ifname");
var loco_flags = document.getElementById("locodet_flags");
var loco_addr = document.getElementById("locodet_addr");
var loco_steps = document.getElementById("locodet_steps");
var loco_vmin = document.getElementById("locodet_vmin");
var loco_vslow = document.getElementById("locodet_vslow");
var loco_vmid = document.getElementById("locodet_vmid");
var loco_vfast = document.getElementById("locodet_vfast");
var loco_vmax = document.getElementById("locodet_vmax");
if (loco_name) res.name = loco_name.value;
if (loco_ifname) res.ifname = loco_ifname.value;
if (loco_flags) res.flags = loco_flags.value;
if (loco_addr) res.addr = loco_addr.value;
if (loco_steps) res.steps = loco_steps.value;
if (loco_vmin) res.vmin = loco_vmin.value;
if (loco_vslow) res.vslow = loco_vslow.value;
if (loco_vmid) res.vmid = loco_vmid.value;
if (loco_vfast) res.vfast = loco_vfast.value;
if (loco_vmax) res.vmax = loco_vmax.value;
return res;
};

@ -0,0 +1,524 @@
//
//
//
const TURNOUT_F_INVERSE = 1;
const TURNOUT_F_MOTORACTIVE = 2;
const TURNOUT_F_TURNOUT = 4;
function rwdetail_requestcallback(response) {
debug ("rwdetail_requestcallback");
};
function rwdetail_request(posx, posy) {
var request = {locoid: locoID, parameter1: -1, name:"Otto"};
debug ("rwdetail_request");
serverinout(request, rwdetail_requestcallback);
};
function rwdetail_show(posx, posy) {
var win = document.getElementById("rwdetail");
debug ("rwdetail_show");
if (!win) {
debug ("rwdetail_show create window");
win = gWindowCreate("rwdetail", "Railway", 400, 500, " \
<div style=\"float: left\"> \
Position: <input id=\"rwdet_X\" style=\"width: 50\" value=\"" + posx +"\"> , <input id=\"rwdet_Y\" style=\"width: 50\" value=\"" + posy +"\"> \
</div> <br> <hr>\
<div> <table><tr><td><table> \
<tr><td>Ref.Name:</td><td><input id=\"rwdet_name\" style=\"width: 100\"></td></tr> \
<tr><td>Dir:</td><td><input id=\"rwdet_dir\" style=\"width: 25\"></td></tr> \
<tr><td>Alt Dir:</td><td><input id=\"rwdet_altdir\" style=\"width: 25\"></td></tr> \
</table><br> \
<center><canvas id=\"rwdet_canvas\" height=\""+track.scale+"\" width=\""+track.scale+"\" style=\"border:1px solid #000000;\"></center> <br>\
</td><td> \
<fieldset><legend>Type</legend> \
Type: <input id=\"rwdet_type\" style=\"width: 25\"><br> \
<label><input type=\"radio\" id=\"rwdet_typenothing\" name=\"Type\" value=\"0\">Nothing</label><br> \
<label><input type=\"radio\" id=\"rwdet_typenormal\" name=\"Type\" value=\"1\">Normal</label><br> \
<label><input type=\"radio\" id=\"rwdet_typecrossing\" name=\"Type\" value=\"2\">Crossing</label><br> \
<label><input type=\"radio\" id=\"rwdet_typeturnout\" name=\"Type\" value=\"3\">Turnout</label><br> \
<label><input type=\"radio\" id=\"rwdet_typesensor\" name=\"Type\" value=\"4\">Sensor</label><br> \
<label><input type=\"radio\" id=\"rwdet_typeconnector\" name=\"Type\" value=\"5\">Connector</label><br> \
<label><input type=\"radio\" id=\"rwdet_typebutton\" name=\"Type\" value=\"6\">Button</label><br> \
<label><input type=\"radio\" id=\"rwdet_typetext\" name=\"Type\" value=\"7\">Text</label><br> \
<label><input type=\"radio\" id=\"rwdet_typeblock\" name=\"Type\" value=\"8\">Block</label><br> \
</fieldset> \
\
</td></tr></table></div> <hr>\ \
\
<div align=right> \
<button id=\"rwdet_SAVE\" type=\"button\">Save</button> \
<button id=\"rwdet_CLOSE\">Close</button> \
</div> \
\
");
gAddEventListener("rwdet_dir", 'change', rwdetail_cb_dirchange);
gAddEventListener("rwdet_type", 'change', rwdetail_cb_typechange);
gAddEventListener("rwdet_altdir", 'change', rwdetail_cb_dirchange);
gAddEventListener("rwdet_typenothing", 'click', rwdetail_cb_typeselector);
gAddEventListener("rwdet_typenormal", 'click', rwdetail_cb_typeselector);
gAddEventListener("rwdet_typecrossing", 'click', rwdetail_cb_typeselector);
gAddEventListener("rwdet_typeturnout", 'click', rwdetail_cb_typeselector);
gAddEventListener("rwdet_typesensor", 'click', rwdetail_cb_typeselector);
gAddEventListener("rwdet_typeconnector", 'click', rwdetail_cb_typeselector);
gAddEventListener("rwdet_typebutton", 'click', rwdetail_cb_typeselector);
gAddEventListener("rwdet_typetext", 'click', rwdetail_cb_typeselector);
gAddEventListener("rwdet_typeblock", 'click', rwdetail_cb_typeselector);
gAddEventListener("rwdet_SAVE", 'click', rwdetail_cb_save);
gAddEventListener("rwdet_CLOSE", 'click', rwdetail_cb_close);
}
else {
var x = document.getElementById("rwdet_X");
var y = document.getElementById("rwdet_Y");
x.value = posx;
y.value = posy;
}
rwdetail_setData({type: 0, x: posx, y: posy, dir: 0, altdir: 0, name: ""});
if (track.elements[posx + posy * track.size.x]) {
rwdetail_setData(track.elements[posx + posy * track.size.x]);
}
};
function rwdetail_cb_dirchange () {
rwdetail_DrawSample ();
};
function rwdetail_cb_typechange () {
var type = document.getElementById("rwdet_type");
var typenothing = document.getElementById("rwdet_typenothing");
var typenormal = document.getElementById("rwdet_typenormal");
var typecrossing = document.getElementById("rwdet_typecrossing");
var typeturnout = document.getElementById("rwdet_typeturnout");
var typesensor = document.getElementById("rwdet_typesensor");
var typeconnector = document.getElementById("rwdet_typeconnector");
var typebutton = document.getElementById("rwdet_typebutton");
var typetext = document.getElementById("rwdet_typetext");
var typeblock = document.getElementById("rwdet_typeblock");
switch(Number(type.value)) {
case 0: typenothing.checked = true; break;
case 1: typenormal.checked = true; break;
case 2: typecrossing.checked = true; break;
case 3: typeturnout.checked = true; break;
case 4: typesensor.checked = true; break;
case 5: typeconnector.checked = true; break;
case 6: typebutton.checked = true; break;
case 7: typetext.checked = true; break;
case 8: typeblock.checked = true; break;
default:
type.value = 0;
typenothing.checked = true;
break;
}
rwdetail_DrawSample();
};
function rwdetail_cb_typeselector () {
var type = document.getElementById("rwdet_type");
type.value = this.value;
rwdetail_DrawSample ();
};
function rwdetail_cb_save () {
var name = document.getElementById("rwdet_name");
var type = document.getElementById("rwdet_type");
var dir = document.getElementById("rwdet_dir");
var altdir = document.getElementById("rwdet_altdir");
var x = document.getElementById("rwdet_X");
var y = document.getElementById("rwdet_Y");
var rws = new Array();
rws.length = 0;
rws.push({name: name.value, x: Number(x.value), y: Number(y.value), type: type.value,
dir: dir.value, altdir: altdir.value});
serverinout_addTrack(rws);
trackAddElement(rws[0]);
};
function rwdetail_cb_close () {
var win = document.getElementById("rwdetail");
if (win) document.body.removeChild(win);
};
function rwdetail_DrawSample() {
var type = document.getElementById("rwdet_type");
var dir = document.getElementById("rwdet_dir");
var altdir = document.getElementById("rwdet_altdir");
var c = document.getElementById("rwdet_canvas");
var ctx = c.getContext("2d");
ctx.fillRect(0, 0, track.scale, track.scale);
trackDrawElement(ctx, { type: type.value, x: 0, y: 0,
dir: dir.value, altdir: altdir.value }, 0);
};
function rwdetail_setData(rw) {
var name = document.getElementById("rwdet_name");
var type = document.getElementById("rwdet_type");
var dir = document.getElementById("rwdet_dir");
var altdir = document.getElementById("rwdet_altdir");
if (rw) {
name.value = rw.name;
type.value = rw.type;
dir.value = rw.dir;
altdir.value = rw.altdir;
}
rwdetail_cb_typechange();
rwdetail_DrawSample();
};
function rw_Click(x,y) {
var idx = x + y * track.size.x;
if (track.elements[idx]) {
//
// Button or Turnout?
if (track.elements[idx].type == RAILWAY_TURNOUT || track.elements[idx].type == RAILWAY_BUTTON) {
if (track.elements[idx].name != "") {
if (turnout_IsActive(track.elements[idx].name)) {
turnout_server_Activate(track.elements[idx].name, 0);
}
else {
turnout_server_Activate(track.elements[idx].name, 1);
}
}
}
//
// Sensor
if (track.elements[idx].type == RAILWAY_SENSOR) {
if (track.elements[idx].name != "") {
}
}
}
};
// *******************************************************************************************************
// * *
// * TURNOUTS *
// * *
// *******************************************************************************************************
var turnouts = [];
//
// update or add a new element
//
function turnout_Update(data) {
for (var i = 0; i < turnouts.length; i++) {
if (data.name == turnouts[i].name) {
debug ("Update turnout:" + turnouts[i].name + " with turnout:" + data.name);
turnouts[i].name = data.name;
turnouts[i].ifname = data.ifname;
turnouts[i].addr = data.addr;
turnouts[i].flags = data.flags;
return;
}
}
// not found add element
debug ("Add turnout:" + data.name);
turnouts.push ({
name: data.name,
ifname: data.ifname,
addr: data.addr,
flags: data.flags
});
};
//
// return if the turnout is active or not
//
function turnout_IsActive(name) {
for (var i = 0; i < turnouts.length; i++) {
if (name == turnouts[i].name) {
if (turnouts[i].flags & TURNOUT_F_TURNOUT) return 1
else return 0;
}
}
return 0;
};
//
// delete element from the list
// in arrays we can not delete just an element, so we create a new one
// and replace this one.
//
function turnout_Delete(name) {
var l = new Array();
for (var i = 0; i < turnouts.length; i++) {
if (name != turnouts[i].name) {
l.push (turnouts[i]);
}
}
// clear list and replace list with new data.
turnouts.lenght = 0;
turnouts = l;
};
//
// send new element to server
//
function turnout_server_Add(elm) {
var request = { command: "addturnout", turnout: elm };
serverinout (request, serverinout_defaultCallback);
};
//
// send delete element to server
//
function turnout_server_Del(elm) {
var request = { command: "delturnout", turnout: elm };
serverinout (request, serverinout_defaultCallback);
};
//
// send activate to the server (onoff must be 0 or 1)
//
function turnout_server_Activate(name, onoff) {
var request = { command: "setturnout", name: name, activate: onoff };
serverinout (request, serverinout_defaultCallback);
};
function turndetail_show(turnname) {
var win = document.getElementById("turndetail");
debug ("turndetail_show");
if (!win) {
debug ("turndetail_show create window");
win = gWindowCreate("turndetail", "Turnout", 400, 500, " \
<div style=\"float: left\"> \
Name: <input id=\"turndet_name\" style=\"width: 100\"> \
</div> <div style=\"float: right\"> \
<button id=\"turndet_PREV\">&lt</button> \
<button id=\"turndet_NEXT\">&gt</button> \
</div> <br> <hr>\
<div> <table><tr><td><table> \
<tr><td>Interface:</td><td><input id=\"turndet_ifname\" style=\"width: 50\"></td></tr> \
<tr><td>Adress:</td><td><input id=\"turndet_addr\" style=\"width: 50\"></td></tr> \
<tr><td>Flags:</td><td><input id=\"turndet_flags\" style=\"width: 50\"></td></tr> \
</table> </td><td> \
<label><input id=\"turndet_inverse\" type=\"checkbox\" value=\"\"> Inverse</label><br>\
<label><input id=\"turndet_active\" type=\"checkbox\" value=\"\"><dfn title=\"save first\"> Active</dfn></label>\
\
</td></tr></table></div> <hr>\
<div align=right> \
<button id=\"turndet_SAVE\" type=\"button\">Save</button> \
<button id=\"turndet_DELETE\" type=\"button\">Delete</button> \
<button id=\"turndet_CLOSE\" type=\"button\">Close</button> \
</div> \
\
");
gAddEventListener("turndet_inverse", 'click', turndetail_cb_inverse);
gAddEventListener("turndet_active", 'click', turndetail_cb_active);
gAddEventListener("turndet_CLOSE", 'click', turndetail_cb_close);
gAddEventListener("turndet_DELETE", 'click', turndetail_cb_delete);
gAddEventListener("turndet_SAVE", 'click', turndetail_cb_save);
gAddEventListener("turndet_NEXT", 'click', turndetail_cb_next);
gAddEventListener("turndet_PREV", 'click', turndetail_cb_prev);
}
if (turnname) {
for (var i = 0; i < turnouts.length; i++) {
if (turnname == turnouts[i].name) turndetail_setData(turnouts[i]);
}
}
};
//
// inverse selected, setup flags
function turndetail_cb_inverse () {
var cbinverse = document.getElementById("turndet_inverse");
var flags = document.getElementById("turndet_flags");
if (cbinverse.checked) {
flags.value = Number(flags.value) | TURNOUT_F_INVERSE;
}
else {
flags.value = Number(flags.value) & (0xFFFF-TURNOUT_F_INVERSE);
}
};
//
// activate turnout
function turndetail_cb_active () {
var cbact = document.getElementById("turndet_active");
var flags = document.getElementById("turndet_flags");
var name = document.getElementById("turndet_name");
if (cbact.checked) {
flags.value = Number(flags.value) | TURNOUT_F_TURNOUT;
turnout_server_Activate(name.value, 1);
}
else {
flags.value = Number(flags.value) & (0xFFFF-TURNOUT_F_TURNOUT);
turnout_server_Activate(name.value, 0);
}
};
function turndetail_cb_close () {
var win = document.getElementById("turndetail");
if (win) document.body.removeChild(win);
};
//
// Callback: Delete Button
//
function turndetail_cb_delete () {
var elm = {};
elm = turndetail_getData();
turnout_Delete(elm.name);
turnout_server_Del(elm);
};
//
// Callback: Save Button
//
function turndetail_cb_save () {
var elm = {};
elm = turndetail_getData();
turnout_Update(elm);
turnout_server_Add(elm);
};
//
// Callback: Next Button
//
function turndetail_cb_next () {
var cursel = -1;
var turnname = document.getElementById("turndet_name");
for (var i = 0; i < turnouts.length; i++) {
if (turnname.value == turnouts[i].name) cursel = i;
}
cursel = cursel + 1;
if (cursel >= turnouts.length) cursel = 0;
if (cursel < 0) cursel = 0;
for (var i = 0; i < turnouts.length; i++) {
if (i == cursel) turndetail_setData(turnouts[i]);
}
// debug ("Cursel: " + cursel + " turnouts.lenght:" + turnouts.length);
};
//
// Callback: Prev Button
//
function turndetail_cb_prev () {
var cursel = -1;
var turnname = document.getElementById("turndet_name");
for (var i = 0; i < turnouts.length; i++) {
if (turnname.value == turnouts[i].name) cursel = i;
}
cursel = cursel - 1;
if (cursel < 0 || cursel >= turnouts.length) cursel = turnouts.length - 1;
for (var i = 0; i < turnouts.length; i++) {
if (i == cursel) turndetail_setData(turnouts[i]);
}
// debug ("Cursel: " + cursel + " turnouts.lenght:" + turnouts.length);
};
//
// fill out all the elements on the dialogbox
//
function turndetail_setData(elm) {
var turnname = document.getElementById("turndet_name");
var turnifname = document.getElementById("turndet_ifname");
var turnaddr = document.getElementById("turndet_addr");
var turnflags = document.getElementById("turndet_flags");
var cbinverse = document.getElementById("turndet_inverse");
var cbactive = document.getElementById("turndet_active");
if (elm) {
if (turnname) turnname.value = elm.name;
if (turnifname) turnifname.value = elm.ifname;
if (turnaddr) turnaddr.value = elm.addr;
if (turnflags) turnflags.value = elm.flags;
if (cbinverse) {
if (Number(elm.flags) & 1) cbinverse.checked = true;
else cbinverse.checked = false;
}
if (cbactive) {
if (Number(elm.flags) & 2) cbactive.checked = true;
else cbactive.checked = false;
}
}
};
//
// return all elements from the dialogbox
//
function turndetail_getData() {
var res = { name: "", ifname: "", addr: "", flags: 0 };
var turnname = document.getElementById("turndet_name");
var turnifname = document.getElementById("turndet_ifname");
var turnaddr = document.getElementById("turndet_addr");
var turnflags = document.getElementById("turndet_flags");
if (turnname) res.name = turnname.value;
if (turnifname) res.ifname = turnifname.value;
if (turnaddr) res.addr = turnaddr.value;
if (turnflags) res.flags = turnflags.value;
return res;
};

@ -0,0 +1,248 @@
//
//
//
const SENSOR_F_INVERSE = 1;
const SENSOR_F_ACTIVE = 2;
var sensors = [];
//
// update or add a new element
//
function sensor_Update(sdata) {
for (var i = 0; i < sensors.length; i++) {
if (sdata.name == sensors[i].name) {
debug ("Update Sensor:" + sensors[i].name + " with Sensor:" + sdata.name);
sensors[i].name = sdata.name;
sensors[i].ifname = sdata.ifname;
sensors[i].addr = sdata.addr;
sensors[i].flags = sdata.flags;
return;
}
}
// not found add element
debug ("Add Sensor:" + sdata.name);
sensors.push ({
name: sdata.name,
ifname: sdata.ifname,
addr: sdata.addr,
flags: sdata.flags
});
};
//
// delete element from the list
// in arrays we can not delete just an element, so we create a new one
// and replace this one.
//
function sensor_Delete(name) {
var l = new Array();
for (var i = 0; i < sensors.length; i++) {
if (name != sensors[i].name) {
l.push (sensors[i]);
}
}
// clear list and replace list with new data.
sensors.lenght = 0;
sensors = l;
};
//
// send new element to server
//
function sensor_server_Add(elm) {
var request = { command: "addsensor", sensor: elm };
serverinout (request, serverinout_defaultCallback);
};
//
// send delete element to server
//
function sensor_server_Del(elm) {
var request = { command: "delsensor", sensor: elm };
serverinout (request, serverinout_defaultCallback);
};
//
// return if the sensor is active or not
//
function sensor_IsActive(name) {
for (var i = 0; i < sensors.length; i++) {
if (name == sensors[i].name) {
if (sensors[i].flags & SENSOR_F_ACTIVE) return 1
else return 0;
}
}
return 0;
};
function sensordetail_show(name) {
var win = document.getElementById("sensordetail");
debug ("sensordetail_show");
if (!win) {
debug ("sensordetail_show create window");
win = gWindowCreate("sensordetail", "Sensor", 400, 300, " \
<div style=\"float: left\"> \
Sensor Name: <input id=\"sensor_name\" style=\"width: 100\"> \
</div> <div style=\"float: right\"> \
<button id=\"sensor_PREV\">&lt</button> \
<button id=\"sensor_NEXT\">&gt</button> \
</div> <br> <hr>\
<div> \
Interface: <input id=\"sensor_ifname\" style=\"width: 200\"> \
Addr: <input id=\"sensor_addr\" style=\"width: 50\"> \
Flags: <input id=\"sensor_flags\" style=\"width: 50\"> \
</div> <hr>\
<div align=right> \
<button id=\"sensor_SAVE\" type=\"button\">Save</button> \
<button id=\"sensor_DELETE\" type=\"button\">Delete</button> \
<button id=\"sensor_CLOSE\">Close</button> \
</div> \
\
");
gAddEventListener("sensor_CLOSE", 'click', sensordetail_cb_close);
gAddEventListener("sensor_DELETE", 'click', sensordetail_cb_delete);
gAddEventListener("sensor_SAVE", 'click', sensordetail_cb_save);
gAddEventListener("sensor_NEXT", 'click', sensordetail_cb_next);
gAddEventListener("sensor_PREV", 'click', sensordetail_cb_prev);
}
if (name) {
for (var i = 0; i < sensors.length; i++) {
if (name == sensors[i].name) sensordetail_setData(sensors[i]);
}
}
};
function sensordetail_cb_close () {
var win = document.getElementById("sensordetail");
if (win) document.body.removeChild(win);
};
//
// Callback: Delete Button
//
function sensordetail_cb_delete () {
var elm = {};
elm = sensordetail_getData();
sensor_Delete(elm.name);
sensor_server_Del(elm);
};
//
// Callback: Save Button
//
function sensordetail_cb_save () {
var elm = {};
elm = sensordetail_getData();
sensor_Update(elm);
sensor_server_Add(elm);
};
//
// Callback: Next Button
//
function sensordetail_cb_next () {
var cursel = -1;
var name = document.getElementById("sensor_name");
for (var i = 0; i < sensors.length; i++) {
if (name.value == sensors[i].name) cursel = i;
}
cursel = cursel + 1;
if (cursel >= sensors.length) cursel = 0;
if (cursel < 0) cursel = 0;
for (var i = 0; i < sensors.length; i++) {
if (i == cursel) sensordetail_setData(sensors[i]);
}
debug ("Cursel: " + cursel + " sensors.lenght:" + sensors.length);
};
//
// Callback: Prev Button
//
function sensordetail_cb_prev () {
var cursel = -1;
var name = document.getElementById("sensor_name");
for (var i = 0; i < sensors.length; i++) {
if (name.value == sensors[i].name) cursel = i;
}
cursel = cursel - 1;
if (cursel < 0 || cursel >= sensors.length) cursel = sensors.length - 1;
for (var i = 0; i < sensors.length; i++) {
if (i == cursel) sensordetail_setData(sensors[i]);
}
debug ("Cursel: " + cursel + " sensors.lenght:" + sensors.length);
};
//
// fill out all the elements on the dialogbox
//
function sensordetail_setData(elm) {
var name = document.getElementById("sensor_name");
var ifname = document.getElementById("sensor_ifname");
var addr = document.getElementById("sensor_addr");
var flags = document.getElementById("sensor_flags");
if (elm) {
if (name) name.value = elm.name;
if (ifname) ifname.value = elm.ifname;
if (addr) addr.value = elm.addr;
if (flags) flags.value = elm.flags;
}
};
//
// return all elements from the dialogbox
//
function sensordetail_getData() {
var res = { name: "", ifname: "", addr: 0, flags:0 };
var name = document.getElementById("sensor_name");
var ifname = document.getElementById("sensor_ifname");
var addr = document.getElementById("sensor_addr");
var flags = document.getElementById("sensor_flags");
if (name) res.name = name.value;
if (ifname) res.ifname = ifname.value;
if (flags) res.flags = flags.value;
if (addr) res.addr = addr.value;
return res;
};

@ -0,0 +1,218 @@
//
//
// mit jedem Paket eine eindeutige ID generieren.
// (ein Teil generiert der Server ein anderer die Anwendung)
//
var sessionID = "";
var randomID = "";
var request_running = 0;
function serverinout_getRand(min, max) {
return Math.floor(Math.random() * (max - min) + min);
};
function serverinout(request, callback) {
//
// generate random ID
if (randomID == "") {
randomID = serverinout_getRand(10000000, 99999999);
}
request.sid = sessionID;
request.rid = randomID;
// e.preventDefault();
senddata = JSON.stringify(request);
// debug('serverinout: send request:' + senddata);
$.ajax({
type: "POST",
// url: '/modelbahn/serverinout.php',
url: '/cgi-bin/modelbahn-cgi',
data: senddata,
success: function(response)
{
var jsonData = {};
// ENABLE LATER try {
jsonData = JSON.parse(response);
if (jsonData.success == "1")
{
var serverstatus = document.getElementById("infoserverstatus");
serverstatus.innerHTML = "connected";
callback(jsonData);
}
else
{
var serverstatus = document.getElementById("infoserverstatus");
serverstatus.innerHTML = "-trying-";
}
// ENABLE LATER } catch(err) {
// ENABLE LATER var serverstatus = document.getElementById("infoserverstatus");
// ENABLE LATER serverstatus.innerHTML = "-error-";
// ENABLE LATER }
},
error: function(error) {
var serverstatus = document.getElementById("infoserverstatus");
serverstatus.innerHTML = "ajax error";
}
});
};
function serverinout_Power(onoff) {
var request = {};
if (onoff) request = {command: "poweron"};
else request = {command: "poweroff"};
serverinout (request, serverinout_defaultCallback);
}
function serverinout_addTrack(tracks) {
var request = { command: "addrailway", rail: tracks };
serverinout (request, serverinout_defaultCallback);
};
function serverinout_delTrack(tracks) {
var request = { command: "delrailway", rail: tracks };
serverinout (request, serverinout_defaultCallback);
};
function serverinout_Save(data) {
var request = { command: "save" };
serverinout (request, serverinout_defaultCallback);
};
function serverinout_defaultCallback(data) {
// if (data.changes) if (data.changes.length > 0) debug ("From Server :" + JSON.stringify(data));
if (data.info) {
}
if (data.sid && data.rid) {
//
// valid data from server
if (data.rid == randomID && data.sid == sessionID) {
//
// loop through all the changes
if (data.changes) {
for (var i = 0; i < data.changes.length; i++) {
//
// infoline
if (data.changes[i].infoline) {
var text = document.getElementById("infoline");
text.innerHTML = data.changes[i].infoline;
}
//
// railway changes
if (data.changes[i].track) {
trackSetup(data.changes[i].track);
}
if (data.changes[i].railway) {
trackAddElement(data.changes[i].railway);
}
//
// Interface changes
if (data.changes[i].interfaces) {
for (var j = 0; j < data.changes[i].interfaces.length; j++) {
interface_Update(data.changes[i].interfaces[j]);
}
}
if (data.changes[i].interface) {
interface_Update(data.changes[i].interface);
}
if (data.changes[i].interfacedelete) {
interface_Delete(data.changes[i].interfacedelete);
}
//
// Locomotives changes
if (data.changes[i].locomotives) {
for (var j = 0; j < data.changes[i].locomotives.length; j++) {
locomotive_Update(data.changes[i].locomotives[j]);
}
}
if (data.changes[i].locomotive) {
locomotive_Update(data.changes[i].locomotive);
}
if (data.changes[i].locomotivedelete) {
locomotive_Delete(data.changes[i].locomotivedelete);
}
//
// Turnout changes
if (data.changes[i].turnouts) {
for (var j = 0; j < data.changes[i].turnouts.length; j++) {
turnout_Update(data.changes[i].turnouts[j]);
}
}
if (data.changes[i].turnout) {
turnout_Update(data.changes[i].turnout);
}
if (data.changes[i].turnoutdelete) {
turnout_Delete(data.changes[i].turnoutdelete);
}
//
// Sensor changes
if (data.changes[i].sensors) {
for (var j = 0; j < data.changes[i].sensors.length; j++) {
sensor_Update(data.changes[i].sensors[j]);
}
}
if (data.changes[i].sensor) {
sensor_Update(data.changes[i].sensor);
}
if (data.changes[i].sensordelete) {
sensor_Delete(data.changes[i].sensordelete);
}
}
trackDraw();
}
}
//
// new session?
else if (data.rid == randomID && data.sid > 0) {
// we got a new sessionID --> request track data
if (sessionID == 0) {
debug ("setup session ID");
sessionID = data.sid;
serverinout( {command: "getall" }, serverinout_defaultCallback);
}
}
else {
debug ("ERROR: serverinout_defaultCallback");
}
}
else {
debug ("ERROR: serverinout_defaultCallback no data.sid && data.rid");
}
};
//
// starte timeout timer fuer Updates on 1/sec
function serverinout_AutoUpdateCallback(data) {
serverinout_defaultCallback(data);
request_running = 0;
};
function serverinout_AutoUpdate() {
// debug ("serverinout_AutoUpdate");
if (request_running == 0) {
request_running = 0;
serverinout({}, serverinout_defaultCallback);
}
setTimeout(function() { serverinout_AutoUpdate() },1000);
}

@ -0,0 +1,71 @@
<?php
session_start();
//
// work around for globals registers.
// we will not use them at all
//
if (ini_get('register_globals'))
{
foreach ($_SESSION as $key=>$value)
{
if (isset($GLOBALS[$key]))
unset($GLOBALS[$key]);
}
}
function debug($text) {
$f = fopen("/tmp/modelbahn-php.log",'a+');
fwrite($f, $text.PHP_EOL);
fclose($f);
};
function readsend_data() {
$data = "";
$input = "";
debug ("opening connection");
$fp = stream_socket_client("unix:///tmp/modelbahn.socket", $errno, $errstr, 30);
if (stream_set_blocking ($fp, FALSE) == FALSE) debug ("stream_set_blocking error");
else debug ("stream_set_blocking ok");
if (!$fp) {
debug ($errstr . " " . $errno);
} else {
$json = file_get_contents("php://input");
debug("Send to Server :".$json);
fwrite($fp, $json);
$timestart = gettimeofday();
$deltatime = 0;
while (!feof($fp) && $deltatime < 1000) {
$input = fgets($fp, 1024);
$data = $data.$input;
$timeend = gettimeofday();
if ($timeend["usec"] < $timestart["usec"]) $deltatime = 1000 + ($timeend["usec"] - $timestart["usec"])/1000;
else $deltatime = ($timeend["usec"] - $timestart["usec"])/1000;
$deltatime = $deltatime + ($timeend["sec"] - $timestart["sec"]) * 1000;
}
fclose($fp);
debug ("got: $data");
debug ("connection closed");
return $data;
}
return $data;
};
$timerstart = microtime(true);
debug("*** php script called");
$request = 0;
$resultdata = array();
echo readsend_data();
$timeused = microtime(true) - $timerstart;
debug ("*** php script finished ********************************* ".$timeused."ms");

@ -0,0 +1,94 @@
//
//
//
const SIDE_DISPLAY_NONE = 1;
const SIDE_DISPLAY_EDITTRACK = 2;
function side_Display(type) {
var x = document.getElementById("side_trackedit");
var btn = document.getElementById("mode-none");
debug ("side_display(" + type +")");
if (type == SIDE_DISPLAY_EDITTRACK) {
x.style.display = "block";
}
else {
sideBtnModeClick(btn);
x.style.display = "none";
}
}
//
// on off switch
//
// value 0-off, 1-on, 2-stop, 3-shortciruit
function sideBtnOnOffClick (obj) {
side_Display(SIDE_DISPLAY_NONE);
if (obj.value == 0) {
serverinout_Power(1);
}
else {
serverinout_Power(0);
}
};
//
// if the power status changed, this function will be called.
function sideBtnOnOffMode (mode) {
var btn = document.getElementById("btn-onoff");
btn.value = mode;
if (mode == 1) {
btn.className = "side_btn_poweron";
btn.value = 1;
}
else if (mode == 2) {
btn.className = "side_btn_stop";
btn.value = 2;
}
else if (mode == 3) {
btn.className = "side_btn_shortcircuit";
btn.value = 3;
}
else {
btn.className = "side_btn_poweroff";
btn.value = 0;
}
};
//
// deselect all elements and select new one
//
function sideBtnModeClick (obj) {
$('.side_btn_mode').each(function(i, objiter) {
if (obj.id == objiter.id) {
objiter.className = "side_btn_mode side_btn_selected";
}
else {
objiter.className = "side_btn_mode";
}
});
};
function sideBtnModeGet () {
var i, selected = "";
i = 0;
$('.side_btn_mode').each(function(j, objiter) {
i++;
if (objiter.className == "side_btn_mode side_btn_selected") selected = objiter.id;
});
// debug ("Selected: '" + selected + "'");
return selected;
};

@ -0,0 +1,9 @@
//
//
//
function testwinbtnclick() {
debug ("Test Button");
locodetail_show("");
}

@ -0,0 +1,662 @@
//
// draw track
//
const RAILWAY_NOTHING = 0;
const RAILWAY_NORMAL = 1;
const RAILWAY_CROSSING = 2;
const RAILWAY_TURNOUT = 3;
const RAILWAY_SENSOR = 4;
const RAILWAY_CONNECTOR = 5;
const RAILWAY_BUTTON = 6;
const RAILWAY_TEXT = 7;
const RAILWAY_BLOCK = 8;
var track = {
size: {x: -1, y: -1},
scale: 24,
dbuf: {},
elements: []
};
var trackMouse = {
pos: {x: -1, y: -1, subx: 0.5, suby: 0.5},
down: {x :-1 , y: -1, subx: 0.5, suby: 0.5}
};
//
// mode: 0 = normal
// mode: 1 = selected
// mode: 2 = mouseover
//
// direktion (object type)
//
// +---+ +---+ +---+ +---+
// | | | | | | | |/ |
// 0| | 1| | | 2|---| 3| |
// | | | | | | | | |
// +---+ +---+ +---+ +---+
//
// +---+ +---+ +---+ +---+
// | \| | | | | | |
// 4| | 5| | 6| | 7| |
// | | | /| |\ | | |
// +---+ +---+ +---+ +---+
//
// direction (arrow type)
//
// 0
// 7 | 1
// \|/
// 6--+--2
// /|\
// 5 | 3
// 4
//
function trackDrawTrack(ctx, pos, dirtype) {
var s = { x:0 , y:0 };
var e = { x:0 , y:0 };
if (dirtype == 1) {
s = { x: 0.5, y: 0.0 };
e = { x: 0.5, y: 1.0 };
}
else if (dirtype == 2) {
s = { x: 0.0, y: 0.5 };
e = { x: 1.0, y: 0.5 };
}
else if (dirtype == 3) {
s = { x: 0.5, y: 0.0 };
e = { x: 0.0, y: 0.5 };
}
else if (dirtype == 4) {
s = { x: 0.5, y: 0.0 };
e = { x: 1.0, y: 0.5 };
}
else if (dirtype == 5) {
s = { x: 0.5, y: 1.0 };
e = { x: 1.0, y: 0.5 };
}
else if (dirtype == 6) {
s = { x: 0.5, y: 1.0 };
e = { x: 0.0, y: 0.5 };
}
else {
s = { x: 0, y: 0 };
e = { x: 0, y: 0 };
}
if (s.x != e.x || s.y != e.x) {
ctx.beginPath();
ctx.moveTo(0.5+(pos.x+s.x) * track.scale, 0.5+(pos.y+s.y) * track.scale);
ctx.lineTo(0.5+(pos.x+e.x) * track.scale, 0.5+(pos.y+e.y) * track.scale);
ctx.stroke();
}
}
function trackDrawElement(ctx, element, mode) {
var dx = 0, dy = 0;
var modcol = "#B0B0B0";
if (element) {
if (mode == 1) modcol = "#FF0000";
if (element.type) {
if (element.type == RAILWAY_TURNOUT) {
//
// turnout
var dir = element.dir;
var altdir = element.altdir;
if (element.name != "") if (turnout_IsActive(element.name)) {
// debug ("draw element:" + element.name + " isActive:" + turnout_IsActive(element.name));
altdir = element.dir;
dir = element.altdir;
}
ctx.lineWidth = 4;
ctx.setLineDash([0,0]);
ctx.strokeStyle = modcol;
trackDrawTrack (ctx, {x: element.x, y: element.y}, dir);
ctx.setLineDash([0,0]);
if (element.name == "") ctx.strokeStyle = "DeepPink";
else ctx.strokeStyle = "DimGray";
trackDrawTrack (ctx, {x: element.x, y: element.y}, altdir);
}
else if (element.type == RAILWAY_NORMAL || element.type == RAILWAY_SENSOR) {
//
// normal
ctx.lineWidth = 2;
ctx.setLineDash([0,0]);
ctx.strokeStyle = modcol;
trackDrawTrack (ctx, {x: element.x, y: element.y}, element.dir);
}
else if (element.type == RAILWAY_CROSSING) {
//
// crossing
ctx.lineWidth = 2;
ctx.setLineDash([0,0]);
ctx.strokeStyle = modcol;
trackDrawTrack (ctx, {x: element.x, y: element.y}, element.dir);
trackDrawTrack (ctx, {x: element.x, y: element.y}, element.altdir);
}
else if (element.type == RAILWAY_TEXT) {
ctx.font = "14px Arial";
ctx.textAlign = "left";
if (element.name != "") {
ctx.fillStyle = "lightblue";
ctx.fillText(element.name, (element.x+0.5) * track.scale, 6+(element.y+0.5) * track.scale);
}
else {
ctx.fillStyle = "DeepPink";
ctx.fillText("------", (element.x+0.5) * track.scale, (element.y+0.5) * track.scale);
}
}
else if (element.type == RAILWAY_CONNECTOR) {
ctx.font = "bold 12px Arial";
ctx.textAlign = "center";
if (element.name != "") {
ctx.fillStyle = "#FFFFFF";
ctx.fillText(element.name, (element.x+0.5) * track.scale, 6+(element.y+0.5) * track.scale);
}
else {
ctx.fillStyle = "DeepPink";
ctx.fillText("------", (element.x+0.5) * track.scale, (element.y+0.5) * track.scale);
}
}
else if (element.type != RAILWAY_NOTHING) {
ctx.beginPath();
ctx.lineWidth = 2;
ctx.setLineDash([0,0]);
ctx.strokeStyle = "DeepPink";
ctx.moveTo((element.x+0.25) * track.scale, (element.y+0.25) * track.scale);
ctx.lineTo((element.x+0.75) * track.scale, (element.y+0.75) * track.scale);
ctx.moveTo((element.x+0.75) * track.scale, (element.y+0.25) * track.scale);
ctx.lineTo((element.x+0.25) * track.scale, (element.y+0.75) * track.scale);
ctx.stroke();
}
//
// additional draing of elements
if (element.type == RAILWAY_SENSOR) {
ctx.beginPath();
ctx.arc((element.x+0.5) * track.scale, (element.y+0.5) * track.scale, track.scale*0.25, 0, 2 * Math.PI);
if (element.name == "") ctx.fillStyle = 'DeepPink';
else if (sensor_IsActive(element.name)) ctx.fillStyle = 'LightGreen';
else ctx.fillStyle = 'DimGray';
ctx.fill();
ctx.lineWidth = 2;
ctx.strokeStyle = "#000000";
ctx.stroke();
}
//
// draw ref. name
if (element.name && sideBtnModeGet() == "mode-turn") {
if (element.type != RAILWAY_TEXT && element.type != RAILWAY_CONNECTOR) {
ctx.font = "10px Arial";
ctx.textAlign = "left";
if (element.name != "") {
ctx.fillStyle = "black";
ctx.fillText(element.name, (element.x) * track.scale -1, 6+(element.y+0.5) * track.scale );
ctx.fillText(element.name, (element.x) * track.scale +1, 6+(element.y+0.5) * track.scale );
ctx.fillText(element.name, (element.x) * track.scale , 6+(element.y+0.5) * track.scale -1);
ctx.fillText(element.name, (element.x) * track.scale , 6+(element.y+0.5) * track.scale +1);
ctx.fillText(element.name, (element.x) * track.scale +1, 6+(element.y+0.5) * track.scale +1);
ctx.fillText(element.name, (element.x) * track.scale -1, 6+(element.y+0.5) * track.scale -1);
ctx.fillText(element.name, (element.x) * track.scale +1, 6+(element.y+0.5) * track.scale -1);
ctx.fillText(element.name, (element.x) * track.scale +1, 6+(element.y+0.5) * track.scale +1);
ctx.fillText(element.name, (element.x) * track.scale -1, 6+(element.y+0.5) * track.scale +1);
ctx.fillStyle = "white";
ctx.fillText(element.name, (element.x) * track.scale , 6+(element.y+0.5) * track.scale );
}
}
}
}
}
};
function trackDraw() {
var ctx = track.dbuf.getContext("2d");
var elm = {};
// debug ("trackDraw pos: " + trackMouse.pos.x + "," + trackMouse.pos.y +
// " down: " + trackMouse.down.x + "," + trackMouse.down.y);
//
// clear screen
ctx.fillStyle = "#808080";
ctx.fillRect(0, 0, track.size.x * track.scale, track.size.y * track.scale);
//
//
for (var i = 0; i < track.elements.length; i++) {
trackDrawElement(ctx, track.elements[i], 0);
}
//
// draw down position
if (trackMouse.down.x >= 0) {
var p1 = {};
var p2 = {};
if (trackMouse.pos.x < trackMouse.down.x) {
p1.x = trackMouse.pos.x;
p2.x = trackMouse.down.x;
}
else {
p1.x = trackMouse.down.x;
p2.x = trackMouse.pos.x;
}
if (trackMouse.pos.y < trackMouse.down.y) {
p1.y = trackMouse.pos.y;
p2.y = trackMouse.down.y;
}
else {
p1.y = trackMouse.down.y;
p2.y = trackMouse.pos.y;
}
ctx.lineWidth = 1;
ctx.lineJoin="round";
ctx.miterLimit = 1;
ctx.setLineDash([2,2]);
ctx.strokeStyle = "#0000FF";
ctx.beginPath();
ctx.moveTo(0.5+(p1.x + 0) * track.scale, 0.5+(p1.y+0) * track.scale);
ctx.lineTo(0.5+(p2.x + 1) * track.scale, 0.5+(p1.y+0) * track.scale);
ctx.lineTo(0.5+(p2.x + 1) * track.scale, 0.5+(p2.y+1) * track.scale);
ctx.lineTo(0.5+(p1.x + 0) * track.scale, 0.5+(p2.y+1) * track.scale);
ctx.lineTo(0.5+(p1.x + 0) * track.scale, 0.5+(p1.y+0) * track.scale);
ctx.stroke();
}
//
// draw mouse cursor
ctx.lineWidth = 1;
ctx.lineJoin="round";
ctx.miterLimit = 1;
ctx.setLineDash([2,2]);
ctx.strokeStyle = "#000000";
ctx.beginPath();
ctx.moveTo(0.5+(trackMouse.pos.x + 0) * track.scale, 0.5+(trackMouse.pos.y+0) * track.scale);
ctx.lineTo(0.5+(trackMouse.pos.x + 1) * track.scale, 0.5+(trackMouse.pos.y+0) * track.scale);
ctx.lineTo(0.5+(trackMouse.pos.x + 1) * track.scale, 0.5+(trackMouse.pos.y+1) * track.scale);
ctx.lineTo(0.5+(trackMouse.pos.x + 0) * track.scale, 0.5+(trackMouse.pos.y+1) * track.scale);
ctx.lineTo(0.5+(trackMouse.pos.x + 0) * track.scale, 0.5+(trackMouse.pos.y+0) * track.scale);
ctx.stroke();
//
// draw mouseselection
var mode = sideBtnModeGet();
if ((mode == "mode-rail" || mode == "mode-delete") &&
trackMouse.down.x >= 0 && trackMouse.down.y >= 0) {
var selected = new Array();
selected.length = 0;
selected = trackGetElementList(trackMouse.down, trackMouse.pos, trackMouse.direction);
for (i = 0; i < selected.length; i++) {
trackDrawElement(ctx, selected[i], 1);
}
selected.length = 0;
var infoline = document.getElementById("infoline");
if (infoline) infoline.innerHTML = trackMouse.direction;
}
//
// draw possible turnout
else if (mode == "mode-turn") {
var t = trackGetTurnout({x: trackMouse.pos.x, y: trackMouse.pos.y});
}
//
// flip screen - double buffering
trackFlipScreen();
};
function trackFlipScreen() {
var c = document.getElementById("TrackCanvas");
var ctx = c.getContext("2d");
ctx.drawImage(track.dbuf, 0, 0);
};
////////////////////////////////////////////////////////////////////////
//
// callback functionen
//
function trackMouseGetPos(event) {
var p = { x: 0, y: 0, subx: 0.5, suby: 0.5 };
var x, dx;
var y, dy;
p.x = Math.floor((event.pageX - 1 - $('#TrackCanvas').offset().left) / track.scale);
p.y = Math.floor((event.pageY - 1 - $('#TrackCanvas').offset().top) / track.scale);
x = (event.pageX - 1 - $('#TrackCanvas').offset().left) / track.scale;
y = (event.pageY - 1 - $('#TrackCanvas').offset().top) / track.scale;
p.subx = x - p.x;
p.suby = y - p.y;
return p;
}
function trackMousemove(event) {
var mode = sideBtnModeGet();
trackMouse.pos = trackMouseGetPos(event);
// debug ("trackMousemove pos: " + trackMouse.pos.x + "," + trackMouse.pos.y +
// " subx: " + trackMouse.pos.subx + " suby:" + trackMouse.pos.suby);
trackDraw();
};
function trackMousedown(event) {
var mode = sideBtnModeGet();
var tmp = trackMouseGetPos(event);
if (mode == "mode-rail" || mode == "mode-delete") {
trackMouse.down = trackMouseGetPos(event);
}
// debug ("trackMousedown pos: " + tmp.x + "," + tmp.y + " subx: " + tmp.subx + " suby:" + tmp.suby);
trackDraw();
};
//
//
//
function trackMouseup(event) {
var mode = sideBtnModeGet();
var tmp = trackMouseGetPos(event);
debug ("trackMouseup tmp: " + tmp.x + "," + tmp.y + " mode:" + mode);
debug ("trackMouseup down: " + trackMouse.down.x + "," + trackMouse.down.y);
debug ("trackMouseup pos: " + trackMouse.pos.x + "," + trackMouse.pos.y);
if (mode == "mode-rail") {
var pos = {};
var selected = new Array();
selected.length = 0;
selected = trackGetElementList(trackMouse.down, trackMouse.pos, trackMouse.direction);
serverinout_addTrack(selected);
selected.length = 0;
}
else if (mode == "mode-turn") {
rwdetail_show(trackMouse.pos.x, trackMouse.pos.y);
}
else if (mode == "mode-delete") {
var pos = {};
var xs, ys, xe ,ye;
var selected = new Array();
selected.length = 0;
//
// determine which direction to count.
if (trackMouse.down.x <= trackMouse.pos.x) {
xs = trackMouse.down.x;
xe = trackMouse.pos.x;
}
else {
xs = trackMouse.pos.x;
xe = trackMouse.down.x;
}
if (trackMouse.down.y <= trackMouse.pos.y) {
ys = trackMouse.down.y;
ye = trackMouse.pos.y;
}
else {
ys = trackMouse.pos.y;
ye = trackMouse.down.y;
}
debug ("delete: xs: " + xs + " , " + ys + " xe: " + xe + " , " + ye);
for (x = xs; x <= xe; x++) for (y = ys; y <= ye; y++) {
selected.push({x: x, y: y, dir: 1, type: 0});
}
serverinout_delTrack(selected);
selected.length = 0;
}
else {
rw_Click(trackMouse.pos.x, trackMouse.pos.y);
}
trackMouse.down.x = -1;
trackMouse.down.y = -1;
trackMouse.step = 0;
trackDraw();
};
//
// add/or delete element
// fill in element
function trackAddElement(elm) {
//debug ("trackAddElement: pos:" + elm.x + "," + elm.y + " d: " + elm.dir + " name:" + elm.name + " type:" + elm.type);
if (track.size.x > 0 && track.size.y > 0) {
track.elements[elm.x + track.size.y * elm.y] = elm;
}
};
////////////////////////////////////////////////////////////////////////
//
// init the track view screen
// bind callback functions
function trackCreate(sizex, sizey) {
debug ("trackCreate " + sizex + " , " + sizey);
var canvas = document.createElement("canvas");
track.size.x = sizex;
track.size.y = sizey;
canvas.setAttribute("id", "TrackCanvas");
canvas.height = track.size.y * track.scale;
canvas.width = track.size.x * track.scale;
canvas.addEventListener('mousemove', trackMousemove);
canvas.addEventListener('mousedown', trackMousedown);
canvas.addEventListener('mouseup', trackMouseup);
track.dbuf = document.createElement("canvas");
track.dbuf.setAttribute("id", "TrackCanvasDBUF");
track.dbuf.height = track.size.y * track.scale;
track.dbuf.width = track.size.x * track.scale;
track.array = new Array();
return canvas;
};
//
// get the size fo track from server, setup all data
function trackSetup(tracksetup) {
debug ("trackSetup");
var client = document.getElementById("page_main");
var oldc = document.getElementById("TrackCanvas");
var olddbuf = document.getElementById("TrackCanvasDBUF");
if (oldc) oldc.remove();
if (olddbuf) olddbuf.remove();
// client.removeChild(oldc);
var canvas = trackCreate (tracksetup.width, tracksetup.height);
client.appendChild (canvas);
trackDraw();
};
//
// return a new list of railways elements
// this list can be added to the server or deleted from.
// or useing this list for drawing preview
// START: will contain the position with the sub position (clockwise: 0, 3, 6, 9)
//
function trackGetElementList(start, end) {
var list = new Array();
var pstart;
var pend;
if (start.x > end.x) {
pstart = {x: end.x, y:end.y, subx: end.subx, suby: end.suby};
pend = {x: start.x, y:start.y, subx: start.subx, suby: start.suby};
}
else {
pstart = {x: start.x, y:start.y, subx: start.subx, suby: start.suby};
pend = {x: end.x, y:end.y, subx: end.subx, suby: end.suby};
}
//
// clicked on a singlke field.
if (pstart.x == pend.x && pstart.y == pend.y) {
x = pstart.x;
y = pstart.y;
if (pstart.subx <= 0.33 && pstart.suby <= 0.33)
list.push ({x: x, y: y, dir: 3, type: 1});
if (pstart.subx <= 0.33 && pstart.suby >= 0.66)
list.push ({x: x, y: y, dir: 6, type: 1});
if (pstart.subx >= 0.66 && pstart.suby <= 0.33)
list.push ({x: x, y: y, dir: 4, type: 1});
if (pstart.subx >= 0.66 && pstart.suby >= 0.66)
list.push ({x: x, y: y, dir: 5, type: 1});
if (pstart.subx >= 0.33 && pstart.subx <= 0.66 && (pstart.suby <= 0.33 || pstart.suby >= 0.66))
list.push ({x: x, y: y, dir: 1, type: 1});
if (pstart.suby >= 0.33 && pstart.suby <= 0.66 && (pstart.subx <= 0.33 || pstart.subx >= 0.66))
list.push ({x: x, y: y, dir: 2, type: 1});
}
else {
var dx = pend.x - pstart.x;
var dy = pend.y - pstart.y;
var m, x, y;
if (dx != 0 && dy != 0) m = dy / dx;
else if (dx == 0) m = 100; // Y
else m = 0; // X
x = pstart.x;
y = pstart.y;
if (Math.abs(m) > 0.5 && Math.abs(m) < 2) {
var elm; // save last element
if (m < 0) { // "/"
// select starting point
if (pstart.suby < 0.5) elm = 3;
else elm = 5;
for (; x <= pend.x && y >= pend.y;) {
if (elm == 3) {
list.push ({x: x, y: y, dir: elm, type: 1});
elm = 5;
y = y - 1;
}
else if (elm == 5) {
list.push ({x: x, y: y, dir: elm, type: 1});
elm = 3;
x = x + 1;
}
else {
// should not possible but somehow we ended up here
debug ("trackGetElementList: ERROR elm 3 or 5 expected");
break;
}
}
}
else { // "\"
// select starting point
if (pstart.suby < 0.5) elm = 4;
else elm = 6;
for (; x <= pend.x && y <= pend.y;) {
if (elm == 4) {
list.push ({x: x, y: y, dir: elm, type: 1});
elm = 6;
x = x + 1;
}
else if (elm == 6) {
list.push ({x: x, y: y, dir: elm, type: 1});
elm = 4;
y = y + 1;
}
else {
// should not possible but somehow we ended up here
debug ("trackGetElementList: ERROR elm 4 or 6 expected");
break;
}
}
}
}
else if (Math.abs(m) >= 2) {
if (m > 0) {
dirtype = 1;
for (; y <= pend.y; y++)
list.push ({x: x, y: y, dir: dirtype, type: 1});
}
else {
dirtype = 1;
for (; y >= pend.y; y--)
list.push ({x: x, y: y, dir: dirtype, type: 1});
}
}
else { // m < 0.5
if (m >= 0) {
dirtype = 2;
for (; x <= pend.x; x++)
list.push ({x: x, y: y, dir: dirtype, type: 1});
}
else {
dirtype = 2;
for (; x >= pend.x; x--)
list.push ({x: x, y: y, dir: dirtype, type: 1});
}
}
}
return list;
};
//
// returns the element at the givin point
function trackGetTurnout(mousepos) {
var pos = {x: mousepos.x, y: mousepos.y};
var elm = {};
for (var i = 0; i < track.elements.length; i++) {
if (track.elements[i]) {
if (track.elements[i].x == pos.x && track.elements[i].y == pos.y) elm = track.elements[i];
}
}
return elm;
};
Loading…
Cancel
Save