#include "modelbahn.h" #include "server.h" #include "debug.h" Railways::Railways() { debug (0, "* Railways Constructor"); height = 0; width = 0; railways = NULL; pthread_mutex_init(&mtx, NULL); 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; }; // // data must be mutex looked before use struct s_findway_data Railways::NextPos(struct s_findway_data pos, int dirtype) { struct s_findway_data np = { x: -1, y: -1, enterfrom: -1, oldx: pos.x, oldy:pos.y, oldenterfrom: pos.enterfrom, dist: pos.dist + 1, way: pos.way }; // newpos if (dirtype == 1) { if (pos.enterfrom == EF_NORTH) { np.x = pos.x; np.y = pos.y + 1 ; np.enterfrom = EF_NORTH; return np; } else if (pos.enterfrom == EF_SOUTH) { np.x = pos.x; np.y = pos.y - 1; np.enterfrom = EF_SOUTH; return np; } } else if (dirtype == 2) { if (pos.enterfrom == EF_WEST) { np.x = pos.x + 1; np.y = pos.y; np.enterfrom = EF_WEST; return np; } else if (pos.enterfrom == EF_EAST) { np.x = pos.x - 1; np.y = pos.y; np.enterfrom = EF_EAST; return np; } } else if (dirtype == 3) { if (pos.enterfrom == EF_WEST) { np.x = pos.x; np.y = pos.y - 1; np.enterfrom = EF_SOUTH; return np; } else if (pos.enterfrom == EF_NORTH){ np.x = pos.x - 1; np.y = pos.y; np.enterfrom = EF_EAST; return np; } } else if (dirtype == 4) { if (pos.enterfrom == EF_NORTH) { np.x = pos.x + 1; np.y = pos.y; np.enterfrom = EF_WEST; return np; } else if (pos.enterfrom == EF_EAST){ np.x = pos.x; np.y = pos.y - 1; np.enterfrom = EF_SOUTH; return np; } } else if (dirtype == 5) { if (pos.enterfrom == EF_SOUTH) { np.x = pos.x + 1; np.y = pos.y; np.enterfrom = EF_WEST; return np; } else if (pos.enterfrom == EF_EAST){ np.x = pos.x; np.y = pos.y + 1; np.enterfrom = EF_NORTH; return np; } } else if (dirtype == 6) { if (pos.enterfrom == EF_WEST) { np.x = pos.x; np.y = pos.y + 1; np.enterfrom = EF_NORTH; return np; } else if (pos.enterfrom == EF_SOUTH){ np.x = pos.x - 1; np.y = pos.y; np.enterfrom = EF_EAST; return np; } } else { debug (0, "Railways::NextPos dirtype unknown:%d", dirtype); return np; } return np; }; // // 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 text = ""; 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); text = railways[idx].lockedby; json.AddObject("lockedby", text); text = railways[idx].name; json.AddObject("name", text); 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 text; Railway r = { type: RAILWAY_NOTHING, x: 0, y: 0, dir: 0, altdir: 0, maxspeed: -1, flags: 0, name: { 0 } }; r.name[0] = 0; r.lockedby[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", &text); strncpy (r.name, text.c_str(), REFERENCENAME_LEN); j->GetValue("lockedby", &text); strncpy (r.lockedby, text.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 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; // // 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; r->lockedby[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; r->lockedby[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; }; void Railways::ClearLockedby(string name) { int x, y; JSONParse jp; Lock(); for (x = 0; x < width; x++) for (y = 0; y < height; y++) { if (name.compare(railways[GetRIdx(x,y)].lockedby) == 0) { railways[GetRIdx(x,y)].lockedby [0] = 0; changed = 1; jp.AddObject("railway",_GetJSONRailway(x,y)); if(network) network->ChangeListPushToAll(jp.ToString()); } } UnLock(); } int Railways::_FindReference(int *x, int *y, string name, int cnt) { int found = 0; int c; // counter of occurance if (x == NULL || y == NULL) return 0; for (c = 0, *y = 0; found == 0 && *y < height; (*y)++) { for (*x = 0; found == 0 && *x < width; (*x)++) { if (strncmp (railways[GetRIdx(*x, *y)].name, name.c_str(), REFERENCENAME_LEN) == 0) if ((c++) == cnt) { found = 1; break; } } if (found) break; } if (*x < width && *y < height) return 1; return 0; }; int Railways::FindReference(int *x, int *y, string name, int cnt) { int res; Lock(); res = _FindReference (x, y, name, cnt); UnLock(); return res; }; void Railways::DebugPrintFindWay(struct s_findway_map *fw) { string c; int x, y; int idx; char chr; printf (" "); for (x = 0; x < width && x < 80; x++) printf ("%c", (char) ((x % 10) + '0')); printf ("\n"); for (y = 0; y < height && y < 40; y++) { printf ("%c ", (char) ((y % 10) + '0')); for (x = 0; x < width && x < 80; x++) { idx = GetRIdx(x, y); if (railways[idx].dir == 1) c = "│"; if (railways[idx].dir == 2) c = "─"; if (railways[idx].dir == 3) c = "╯"; if (railways[idx].dir == 4) c = "╰"; if (railways[idx].dir == 5) c = "╭"; if (railways[idx].dir == 6) c = "╮"; if (railways[idx].type == RAILWAY_CROSSING) c = "+"; if (railways[idx].type == RAILWAY_TURNOUT) c = "t"; if (railways[idx].type == RAILWAY_CONNECTOR) c = "◯"; if (railways[idx].type == RAILWAY_BLOCK && railways[idx].dir == 1) c = "┃"; if (railways[idx].type == RAILWAY_BLOCK && railways[idx].dir == 2) c = "━"; if (fw != NULL) { if ((fw[idx].e & 15) > 0) { chr = (char)('0' + (fw[idx].e & 15)); if (chr > '9') chr = chr - ('0' + 10) + 'A'; c = chr; } else if (fw[idx].e != 0) { c = "?"; printf ("---%d---", fw[idx].e); } } printf ("%s", c.c_str()); c = " "; } printf ("\n"); } for (x = 0; x < width && x < 80; x++) printf ("%c", (char) ((x % 10) + '0')); printf ("\n"); printf ("Debug Finished\n"); } /*************************************************************************** * GetDestBlock: check if the destination is a split block and return * the final destination block. */ string Railways::GetDestBlock(int locoflags, string blockend) { int block_f; string block; string blockp, blockn; // blockend[pos,neg] name blockp = blockend.substr(2, string::npos); block_f = server->blocks.GetFlags(blockp); if ((block_f & BLOCK_F_SPLIT) && !(locoflags & LOCO_F_SHORTTRAIN)) { blockn = server->blocks.GetFlags(blockp); // direction and position if (block_f & BLOCK_F_SPLITPOS) { blockn = server->blocks.GetSecondBlock(blockp); } else { blockn = blockp; blockp = server->blocks.GetSecondBlock(blockn); } if (blockend[0] == '+') block = "+:" + blockn; else block = "-:" + blockp; } else { block = blockend; } debug (0, "Railway::GetDestBlock Final split:%d loco short:%d blockend:%s -> block:%s" , (block_f & BLOCK_F_SPLIT) ? 1 : 0 , (locoflags & LOCO_F_SHORTTRAIN) ? 1: 0 , blockend.c_str() , block.c_str()); return block; } /* * insert the element in an ordered list */ void FDListPush (std::list *list, struct s_findway_data *element) { std::list::iterator iter; for (iter = list->begin(); iter != list->end(); iter++) if (element->dist < iter->dist) { // printf ("%s:%d Push dist list:%d dist element:%d (%d,%d E:%d)\n", __FILE__, __LINE__, iter->dist, element->dist, element->x, element->y, element->enterfrom); list->insert(iter, *element); return; } // printf ("%s:%d Push dist list:--- dist element:%d (%d,%d E:%d)\n", __FILE__, __LINE__, element->dist, element->x, element->y, element->enterfrom); list->push_back(*element); } /**************************************************************************** * FindWay: will be in two phases to decide where to go and how * - check if endblock is valid on SPLIT and long trains. * - check all possible ways in respect of (out of service blocks) * and the ONLYCARGO/PASSENGER flags * return 0 if no way found */ int Railways::FindWay(string org_blockstart, string org_blockend, string lockedfor, string *next) { // direction 0 ... RIGHT and DOWN // direction 1 ... LEFT and UP struct s_findway_data fd_pos; struct s_findway_data fd_tmp; struct s_findway_data fd_start; struct s_findway_data fd_end; Railway *rpos, *rnext; std::list fd_list; struct s_findway_map *fd_data = NULL; string blockstart, blockend; string lockedby; int x ,y; int found = 0; int locoflags = server->locomotives.GetFlags(lockedfor); debug (0, "Railway::FindWay blockstart:%s blockend:%s lockedfor:%s", org_blockstart.c_str(), org_blockend.c_str(), lockedfor.c_str()); // // check blocklen if (org_blockstart.length() <= 3 || org_blockend.length() <= 3) return 0; // // setup blockstart and blockend depending on train type // long trains need to get special attentions on the destination blockstart = org_blockstart; // no check needed blockend = GetDestBlock(locoflags, org_blockend); // // allocate and clear memory for checked data fd_data = (struct s_findway_map *) malloc (sizeof(struct s_findway_map) * width * height); if (fd_data == NULL) { printf ("%s:%d (%s) FATAL ERROR: could not allocate enough memory\n", __FILE__, __LINE__, __FUNCTION__); exit (1); } memset (fd_data, 0x0, sizeof(struct s_findway_map) * width*height); // // find start and endpoint mark on fd_data, also startpoint to fd_list fd_end.x = fd_start.x = -1; fd_end.y = fd_start.y = -1; fd_end.enterfrom = fd_start.enterfrom = -1; fd_end.dist = fd_start.dist = 0; if (FindReference(&fd_start.x, &fd_start.y, blockstart.substr(2, string::npos),0)) { debug (0, "Railway::FindWay found startblock (%s).", blockstart.c_str()); if (Get(fd_start.x, fd_start.y).dir == 1) { if (blockstart[0] == '+') fd_start.enterfrom = 0; else fd_start.enterfrom = 2; } else { if (blockstart[0] == '+') fd_start.enterfrom = 3; else fd_start.enterfrom = 1; } fd_start.way = "b:" + blockstart; fd_data[GetRIdx(fd_start.x, fd_start.y)].e = (1 << fd_start.enterfrom); fd_data[GetRIdx(fd_start.x, fd_start.y)].score = 1; } else { debug (0, "Railway::FindWay could not find startblock (%s).", blockstart.c_str()); free (fd_data); return 0; } if (FindReference(&fd_end.x, &fd_end.y, blockend.substr(2, string::npos), 0)) { debug (0, "Railway::FindWay found endblock (%s).", blockend.c_str()); if (Get(fd_end.x, fd_end.y).dir == 1) { if (blockend[0] == '+') fd_end.enterfrom = 0; else fd_end.enterfrom = 2; } else { if (blockend[0] == '+') fd_end.enterfrom = 3; else fd_end.enterfrom = 1; } fd_end.way = blockend; fd_data[GetRIdx(fd_end.x, fd_end.y)].e = (1 << fd_end.enterfrom); } else { debug (0, "Railway::FindWay could not find endblock (%s).", blockend.c_str()); free (fd_data); return 0; } fd_pos = NextPos(fd_start, Get(fd_start.x, fd_start.y).dir); Lock(); // // loop through the map to find all possible ways to the destination // this will be done in respect of lockedby blocks and ways. // // DebugPrintFindWay(fd_data); while ((fd_list.size() > 0 || fd_pos.enterfrom >= 0) && found == 0) { // std::list::iterator iter; // for (iter = fd_list.begin(); iter != fd_list.end(); iter++) // printf ("List: Dist:%-4d Pos (%d,%d)\n", iter->dist, iter->x, iter->y); // DebugPrintFindWay(fd_data); if (fd_pos.enterfrom < 0) { std::list::iterator iter; iter = fd_list.begin(); if (iter != fd_list.end()) { fd_pos = *iter; fd_list.pop_front(); // printf ("%s:%d pop_front -> fd_pos (%d,%d e:%d) dist:%d\n", __FILE__, __LINE__, fd_pos.x, fd_pos.y, fd_pos.enterfrom, fd_pos.dist); } } if (fd_pos.enterfrom < 0 && fd_pos.enterfrom > 3) { fd_pos.enterfrom = -1; fd_pos.x = -1; fd_pos.y = -1; fd_pos.way = ""; } else if (fd_pos.enterfrom >= 0 && fd_end.x == fd_pos.x && fd_pos.y == fd_end.y && fd_pos.enterfrom == fd_end.enterfrom) { // destination found found = 1; *next = fd_pos.way + ",b:" + blockend; fd_pos.enterfrom = -1; fd_pos.x = -1; fd_pos.y = -1; fd_pos.way = ""; debug (0, "Railway::FindWay %s:%d found way. [%s]", __FILE__, __LINE__, next->c_str()); } else if (fd_pos.enterfrom >= 0 && fd_start.x == fd_pos.x && fd_start.y == fd_pos.y && fd_start.enterfrom == fd_pos.enterfrom) { // start found fd_pos.enterfrom = -1; fd_pos.x = -1; fd_pos.y = -1; fd_pos.way = ""; } else if (fd_data[GetRIdx(fd_pos.x, fd_pos.y)].e & (1 << fd_pos.enterfrom)) { // old entry found fd_pos.enterfrom = -1; fd_pos.x = -1; fd_pos.y = -1; fd_pos.way = ""; } else if (fd_pos.enterfrom >= 0) { rpos = &railways[GetRIdx(fd_pos.x, fd_pos.y)]; fd_data[GetRIdx(fd_pos.x, fd_pos.y)].e |= (1 << fd_pos.enterfrom); if (rpos->type == RAILWAY_NORMAL || rpos->type == RAILWAY_SENSOR) { fd_pos = NextPos(fd_pos, rpos->dir); rnext = &railways[GetRIdx(fd_pos.x, fd_pos.y)]; if (rnext->lockedby[0] != 0 || !_NextPosIsValid(lockedfor, locoflags, &fd_pos)) { fd_pos.enterfrom = -1; fd_pos.x = -1; fd_pos.y = -1; fd_pos.way = ""; } } else if (rpos->type == RAILWAY_TURNOUT) { fd_tmp = NextPos(fd_pos, rpos->altdir); fd_tmp.way += (string)",t:" + (string)rpos->name + (string)":1"; if (fd_tmp.enterfrom != -1) FDListPush(&fd_list, &fd_tmp); fd_tmp = NextPos(fd_pos, rpos->dir); fd_tmp.way += (string)",t:" + (string)rpos->name + (string)":0"; if (fd_tmp.enterfrom != -1) FDListPush(&fd_list, &fd_tmp); fd_pos.enterfrom = -1; fd_pos.x = -1; fd_pos.y = -1; fd_pos.way = ""; } else if (rpos->type == RAILWAY_CONNECTOR) { int conector_found = 0; x = -1; y = -1; int cnt = 0; while (conector_found == 0 && _FindReference(&x, &y, rpos->name, (cnt++)) == 1) { if (fd_pos.x != x || fd_pos.y != y) { fd_pos.oldx = fd_pos.x; fd_pos.oldy = fd_pos.y; fd_pos.oldenterfrom = fd_pos.enterfrom; fd_pos.x = x; fd_pos.y = y; rpos = &railways[GetRIdx(fd_pos.x, fd_pos.y)]; fd_pos = NextPos(fd_pos, rpos->dir); conector_found = 1; break; } } if (conector_found == 0 || !_NextPosIsValid(lockedfor, locoflags, &fd_pos)) { fd_pos.enterfrom = -1; fd_pos.x = -1; fd_pos.y = -1; fd_pos.way = ""; } } else if (rpos->type == RAILWAY_BLOCK) { int blflags = server->blocks.GetFlags(rpos->name); debug (0, "Railways::FindWay %s:%d found block: %s", __FILE__, __LINE__, rpos->name); // // check if block is available for entering if ((server->blocks.IsOff(rpos->name) || ((blflags & BLOCK_F_ENDSTATION) && !(locoflags & LOCO_F_CANREVERSE)) || ((blflags & BLOCK_F_SHORT) && !(locoflags & LOCO_F_SHORTTRAIN) && !_SplitBlockIsFreeAndAllowed(lockedfor, locoflags, rpos->name)) || ((blflags & BLOCK_F_ONLYCARGO) && !(locoflags & LOCO_F_CARGO)) || ((blflags & BLOCK_F_ONLYPASSENGER) && (locoflags & LOCO_F_CARGO)))) { fd_pos.enterfrom = -1; fd_pos.x = -1; fd_pos.y = -1; fd_pos.way = ""; } else { // lockedby = server->blocks.GetLockedby(rpos->name); lockedby = server->blocks.GetLockedby(rpos->name); if (lockedby.length() == 0 || lockedby.compare(lockedfor) == 0) { if (fd_pos.enterfrom == 0 || fd_pos.enterfrom == 3) fd_pos.way += (string)",b:+:" + (string)rpos->name; else fd_pos.way += (string)",b:-:" + (string)rpos->name; fd_pos = NextPos(fd_pos, rpos->dir); if (!_NextPosIsValid(lockedfor, locoflags, &fd_pos)) { fd_pos.enterfrom = -1; fd_pos.x = -1; fd_pos.y = -1; fd_pos.way = ""; } } else { fd_pos.enterfrom = -1; fd_pos.x = -1; fd_pos.y = -1; fd_pos.way = ""; } } } else { // finished fd_pos.enterfrom = -1; fd_pos.x = -1; fd_pos.y = -1; fd_pos.way = ""; } } } if (found == 0) *next = ""; else { // // do final checks: // // // check if way found a block. size_t npos; if ((npos = next->rfind(",b:")) != string::npos) *next = next->substr(0, next->find(",", npos+1)); else // nothing found? *next = ""; // // check that a way is not set twice // if (!FindWayCheckIfNoDoubleTurnouts (next)) { debug (0, "FindWayCheckIfNoDoubleTurnouts way [%s] is not valid.", next->c_str()); *next = ""; } } UnLock(); free (fd_data); debug (0, "Railway::FindWay Result (%s) -> (%s) Found:%d Next:%s", blockstart.c_str(), blockend.c_str(), found, next->c_str()); return found; }; int Railways::FindWayCheckIfNoDoubleTurnouts (string *way) { size_t pos1, pos2; // block positions size_t tp1, tp2; // turnouts int wayset; string s; // search string (should not been found) for (pos1 = 0, pos2 = way->find(",b:"); pos2 != string::npos; pos1 = pos2, pos2 = way->find(",b:", pos1+1)) { tp1 = pos1; for (tp1 = way->find(",t:", tp1+1); tp1 != string::npos; tp1 = way->find(",t:", tp1+1)) { if ((*way)[way->find(":", tp1+4)+1] == '0') s = way->substr(tp1, way->find(":", tp1+4)-tp1) + ":1"; else s = way->substr(tp1, way->find(":", tp1+4)-tp1) + ":0"; tp2 = way->find (s, tp1+4); if (tp2 == string::npos) continue; // not found if (tp2 < pos2) return 0; // found ... return 0 } } return 1; } /* * check if the position in fwd.x|y is valid for the loco. The check will only done on * RAILWAY_BLOCK types. It will also check if the block is free. */ int Railways::_NextPosIsValid(string loconame, int locoflags, struct s_findway_data *fwd) { int bflags = -1; Railway *rpos = NULL; rpos = &railways[GetRIdx(fwd->x, fwd->y)]; bflags = server->blocks.GetFlags(rpos->name); if (rpos->type != RAILWAY_BLOCK) return 1; if (server->blocks.IsOff(rpos->name)) return 0; if ((bflags & BLOCK_F_ENDSTATION) && !(locoflags & LOCO_F_CANREVERSE)) return 0; if ((bflags & BLOCK_F_SHORT) && !(locoflags & LOCO_F_SHORTTRAIN) && !_SplitBlockIsFreeAndAllowed(loconame, locoflags, rpos->name)) return 0; if ((bflags & BLOCK_F_ONLYCARGO) && !(locoflags & LOCO_F_CARGO)) return 0; if ((bflags & BLOCK_F_ONLYPASSENGER) && (locoflags & LOCO_F_CARGO)) return 0; return 1; }; /* * check if this is a split block, and if it can be entered * all needed elements must be unlocked to return 1. * if the loco is a short train return 0 */ int Railways::_SplitBlockIsFreeAndAllowed(string loconame, int locoflags, string block) { string pblock; string nblock; Railway *rpos = NULL; int bflags = server->blocks.GetFlags(block); int nblockflags = 0; int pblockflags = 0; int x1, y1, x2, y2, x = 0, y = 0; debug (0, "Railway::_SplitBlockIsFreeAndAllowed loconame:%s flags:%d block:%s", loconame.c_str(), locoflags, block.c_str()); if (locoflags & LOCO_F_SHORTTRAIN) return 0; if ((locoflags & !LOCO_F_SHORTTRAIN) && (bflags & !BLOCK_F_SPLIT)) return 0; // // read negative and positive block flags if (bflags & BLOCK_F_SPLITPOS) { pblockflags = bflags; pblock = block; nblock = server->blocks.GetSecondBlock(block); if (nblock.length() == 0) return 0; nblockflags = server->blocks.GetFlags(nblock); } else { nblockflags = bflags; nblock = block; pblock = server->blocks.GetSecondBlock(block); if (pblock.length() == 0) return 0; pblockflags = server->blocks.GetFlags(pblock); } // // check if blockflags are setup right and the blocks are free if (!(nblockflags & BLOCK_F_SPLIT) && (nblockflags & BLOCK_F_SPLITPOS)) return 0; if (!(pblockflags & BLOCK_F_SPLIT) && !(pblockflags & BLOCK_F_SPLITPOS)) return 0; if (server->blocks.GetLockedby(nblock).length() != 0) return 0; if (server->blocks.GetLockedby(pblock).length() != 0) return 0; // check the way between the blocks if (_FindReference(&x1, &y1, pblock, 0) == 0) return 0; if (_FindReference(&x2, &y2, nblock, 0) == 0) return 0; debug (0, "Railway::_SplitBlockIsFreeAndAllowed p pos(%d,%d) n pos(%d,%d)", x1, y1, x2, y2); x = x1; y = y1; do { if ( railways[GetRIdx(x,y)].lockedby[0] != 0) return 0; if (x1 == x2) y++; // block is going from down to up else x++; // block is going from left to right } while (x != x2 || y != y2); return 1; } int Railways::FindRandomWay(string blockstart, string lockedfor, string *next) { // direction 0 ... RIGHT and DOWN // direction 1 ... LEFT and UP struct s_findway_data fd_pos; struct s_findway_data fd_tmp; struct s_findway_data fd_start; struct s_findway_data fd_end; Railway *rpos, *rnext; std::list fd_list; struct s_findway_map *fd_data = NULL; string lockedby; int x ,y; int found = 0; int locoflags = server->locomotives.GetFlags(lockedfor); debug (0, "Railway::FindRandomWay - disabled for now - needs to be rewritten"); return 0; } /* * try to lock or unlock the way for the loco(lockedby) * this lock will only go from one to the next block. * Will also do the check for long trains and split blocks. */ int Railways::LockWay (string way, string lockedby, int lockonoff) { int res = 0; size_t pos1, pos2; size_t curwaypos; struct s_findway_data start; struct s_findway_data end; struct s_findway_data pos; string startblock, startblock2, startblockfinal, endblock, endblock2,endblockfinal; Railway *r; int finished = 0; JSONParse jp; int locoflags = server->locomotives.GetFlags(lockedby); int blockendflags = 0; int blockstartflags = 0; debug (0, "* LockWay Way:'%s' for '%s' lockonoff:%d", way.c_str(), lockedby.c_str(), lockonoff); end.x = start.x = -1; end.y = start.y = -1; end.enterfrom = start.enterfrom = -1; if ((pos1 = way.find("b:")) == string::npos) return 0; if ((pos2 = way.find(",", pos1+1)) == string::npos) return 0; startblock = way.substr (pos1+2, pos2-pos1-2); if ((pos1 = way.find(",b:",pos2)) == string::npos) return 0; if ((pos2 = way.find(",", pos1+1)) == string::npos) endblock = way.substr (pos1+3, pos2); else endblock = way.substr (pos1+3, pos2-pos1-3); // // take care of split blocks and long trains. // if the second block is not needed endblock2 will remain empty blockendflags = server->blocks.GetFlags(endblock.substr(2, string::npos)); if (!(locoflags & LOCO_F_SHORTTRAIN) && (blockendflags & BLOCK_F_SPLIT)) endblock2 = server->blocks.GetSecondBlock(endblock.substr(2, string::npos)); else endblock2 = ""; blockstartflags = server->blocks.GetFlags(startblock.substr(2, string::npos)); if (!(locoflags & LOCO_F_SHORTTRAIN) && (blockstartflags & BLOCK_F_SPLIT)) startblock2 = server->blocks.GetSecondBlock(startblock.substr(2, string::npos)); else startblock2 = ""; debug (0, "%s:%d LockWay Way: '%s' Startblock: '%s' Startblock2: '%s' Endblock: '%s' Endblock2: '%s'", __FILE__, __LINE__, way.c_str(), startblock.c_str(), startblock2.c_str(), endblock.c_str(), endblock2.c_str()); // // find start position // if (startblock2.length() > 0) { if ((startblock[0] == '+' && !(blockstartflags & BLOCK_F_SPLITPOS)) || (startblock[0] == '-' && (blockstartflags & BLOCK_F_SPLITPOS)) ) { startblockfinal = startblock2; } else { startblockfinal = startblock.substr(2, string::npos); } } else { startblockfinal = startblock.substr(2, string::npos); } // // retrieve final start position, in case of split blocks if (FindReference(&start.x, &start.y, startblockfinal, 0)) { if (Get(start.x, start.y).dir == 1) { if (startblock[0] == '+') start.enterfrom = 0; else start.enterfrom = 2; } else { if (startblock[0] == '+') start.enterfrom = 3; else start.enterfrom = 1; } } else { debug (0, "Railway::LockWay could not find start (%s).", startblock.c_str()); return 0; } // // find end position // // // retrieve final end position, in case of split blocks if (endblock2.length() > 0) { if (lockonoff) { if ((endblock[0] == '+' && (blockendflags & BLOCK_F_SPLITPOS)) || (endblock[0] == '-' && !(blockendflags & BLOCK_F_SPLITPOS)) ) { endblockfinal = endblock2; } else { endblockfinal = endblock.substr(2, string::npos); } } else { if ((endblock[0] == '-' && (blockendflags & BLOCK_F_SPLITPOS)) || (endblock[0] == '+' && !(blockendflags & BLOCK_F_SPLITPOS)) ) { endblockfinal = endblock2; } else { endblockfinal = endblock.substr(2, string::npos); } } } else { endblockfinal = endblock.substr(2, string::npos); } // get the final end position if (!FindReference(&end.x, &end.y, endblockfinal, 0)) { debug (0, "Railway::FindWay could not find endblock (%s).", endblockfinal.c_str()); return 0; } if (Get(end.x, end.y).dir == 1) { if (endblock[0] == '+') end.enterfrom = 0; else end.enterfrom = 2; } else { if (endblock[0] == '+') end.enterfrom = 3; else end.enterfrom = 1; } printf ("%s:%d LockWay Way:'%s' start [%d,%d -> %d] to [%d,%d -> %d] LockOnOff:%d\n", __FILE__, __LINE__, way.c_str(), start.x, start.y, start.enterfrom, end.x, end.y, end.enterfrom, lockonoff); // // lock way depending on Way Lock(); pos.x = start.x; pos.y = start.y; pos.enterfrom = start.enterfrom; curwaypos = 0; // position in string // find first turnout curwaypos = way.find(",t:", curwaypos); if (curwaypos != string::npos) curwaypos++; finished = 0; do { r = &railways[GetRIdx(pos.x, pos.y)]; debug (0, "LockWay Position [%d,%d] Name:%s LockedBy:'%s'", pos.x, pos.y, r->name, r->lockedby); // check if railway is free or locked by me if (r->lockedby[0] != 0 && lockedby.compare(r->lockedby) != 0 && !(lockonoff == 0 && pos.x == start.x && pos.y == start.y && r->type == RAILWAY_BLOCK)) { debug (0, "LockWay: Railway currently locked by '%s'.", r->lockedby); UnLock(); return 0; } // lock start and endpoint... unlock only start if (r->type == RAILWAY_BLOCK) { if ((strcmp(endblock.c_str()+2,r->name) == 0) || (endblock2.length() > 0 && (endblock2.compare(r->name) == 0))) { UnLock(); if (lockonoff) { strncpy (r->lockedby, lockedby.c_str(), REFERENCENAME_LEN); server->blocks.SetLockedby(r->name, lockedby, lockonoff); } Lock(); } else if ((strcmp(startblock.c_str()+2, r->name) == 0) || (startblock2.length() > 0 && (startblock2.compare(r->name) == 0))) { UnLock(); server->blocks.SetLockedby(r->name, lockedby, lockonoff); if (lockonoff) strncpy (r->lockedby, lockedby.c_str(), REFERENCENAME_LEN); else r->lockedby[0] = 0; Lock(); } else { debug (0, "*** ERROR *** RailWay BLOCK not start and end (r->name:%s) startblock:%s [%s] endblock:%s [%s]" , r->name, startblock.c_str(), startblock2.c_str(), endblock.c_str(), endblock2.c_str()); server->PowerOnOff(0); } jp.Clear(); jp.AddObject("railway",_GetJSONRailway(pos.x, pos.y)); if(network) network->ChangeListPushToAll(jp.ToString()); } else { if (lockonoff) strncpy (r->lockedby, lockedby.c_str(), REFERENCENAME_LEN); else r->lockedby[0] = 0; jp.Clear(); jp.AddObject("railway",_GetJSONRailway(pos.x, pos.y)); if(network) network->ChangeListPushToAll(jp.ToString()); } if (pos.x < 0 || pos.y < 0 || pos.x >= width || pos.y >= height) { server->interfaces.PowerOnOff(false); debug (0, "Railway::LockWay %s:%d Error: routing out of surface [%d,%d]", __FILE__, __LINE__, pos.x, pos.y); finished = 1; break; } // // to prevent unlocking split blocks on enter, check if the start and the end are different blocks. // ignore this if startblock and endblock are the same split block. if (locoflags & LOCO_F_SHORTTRAIN) { if (pos.x == end.x && pos.y == end.y) { finished = 1; break; } } else { // freeing way on stop: abort if block is part of the split endblock if ((pos.x == end.x && pos.y == end.y) || (lockonoff == 0 && (endblock.compare(startblock.substr(0,2) + startblock2) != 0) && ((endblock.compare(r->name) == 0) || (endblock2.length() > 0 && endblock2.compare(r->name) == 0)))) { finished = 1; break; } } // go to next position if (r->type == RAILWAY_TURNOUT) { if (curwaypos == string::npos) { // we should have a turnout debug (0, "LockWay turnout '%s' not on Way '%s'", r->name, way.c_str()); UnLock(); return 0; } pos1 = way.find(":", curwaypos+2); if (pos1 == string::npos) { debug (0, "LockWay turnout could not find parameter 'active'" ); UnLock(); return 0; } // // in case this turnout is not in the way list. It is might in between the splitblocks if (way.substr(curwaypos+2, pos1-curwaypos-2).compare(r->name) != 0) { int d = server->turnouts.Get(r->name); debug (0, "LockWay tournout %s in between start or endblock? Active: %d", r->name, d); pos = NextPos(pos, d ? r->altdir : r->dir); } else { if (way[pos1+1] == '0') pos = NextPos(pos, r->dir); else pos = NextPos(pos, r->altdir); // next turnout? curwaypos = way.find(",t:", curwaypos); if (curwaypos != string::npos) curwaypos++; } } else if (r->type == RAILWAY_CONNECTOR) { int found = 0; int x, y, cnt; printf ("%s:%d connector found Reference:'%s'\n", __FILE__, __LINE__, r->name); x = -1; y = -1; while (found == 0 && _FindReference(&x, &y, r->name, (cnt++)) == 1) { if (pos.x != x || pos.y != y) { printf ("%s:%d found %d,%d\n", __FILE__, __LINE__, x, y); pos.oldx = pos.x; pos.oldy = pos.y; pos.oldenterfrom = pos.enterfrom; pos.x = x; pos.y = y; r = &railways[GetRIdx(pos.x, pos.y)]; pos = NextPos(pos, r->dir); found = 1; break; } } if (found == 0) { debug (0, "LockWay connector '%s' did not find second end.", r->name ); UnLock(); return 0; } } else if (r->type == RAILWAY_BLOCK || r->type == RAILWAY_SENSOR || r->type == RAILWAY_NORMAL) { pos = NextPos(pos, r->dir); } } while (finished == 0); if (pos.x == end.x && pos.y == end.y) res = 1; UnLock(); return res; }; int Railways::SetLockedby (int x, int y, string name, int locked) { int res = 1; Railway *r; JSONParse jp; if (x < 0 || y < 0 || x >= width || y >= height) return 0; Lock(); r = &railways[GetRIdx(x,y)]; if (r->lockedby[0] != 0) if (name.compare(r->lockedby) != 0) res = 0; if (res) { if (locked) strncpy (r->lockedby, name.c_str(), REFERENCENAME_LEN); else r->lockedby[0] = 0; if (railways[GetRIdx(x, y)].type != RAILWAY_NOTHING) { jp.AddObject("railway", _GetJSONRailway(x, y)); if(network) network->ChangeListPushToAll(jp.ToString()); } } UnLock(); return res; };