/************************************************************************ MeOS - Orienteering Software Copyright (C) 2009-2017 Melin Software HB This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Melin Software HB - software@melin.nu - www.melin.nu Eksoppsvägen 16, SE-75646 UPPSALA, Sweden ************************************************************************/ #include "stdafx.h" #include #include #include "oFreePunch.h" #include "oEvent.h" #include "Table.h" #include "meos_util.h" #include "Localizer.h" #include "intkeymapimpl.hpp" #include "socket.h" #include "meosdb/sqltypes.h" #include "gdioutput.h" bool oFreePunch::disableHashing = false; oFreePunch::oFreePunch(oEvent *poe, int card, int time, int type): oPunch(poe) { Id=oe->getFreePunchId(); CardNo = card; Time = time; Type = type; iHashType = 0; tRunnerId = 0; } oFreePunch::oFreePunch(oEvent *poe, int id): oPunch(poe) { Id=id; oe->qFreePunchId = max(id, oe->qFreePunchId); iHashType = 0; tRunnerId = 0; } oFreePunch::~oFreePunch(void) { } bool oFreePunch::Write(xmlparser &xml) { if (Removed) return true; xml.startTag("Punch"); xml.write("CardNo", CardNo); xml.write("Time", Time); xml.write("Type", Type); xml.write("Id", Id); xml.write("Updated", Modified.getStamp()); xml.endTag(); return true; } void oFreePunch::Set(const xmlobject *xo) { xmlList xl; xo->getObjects(xl); xmlList::const_iterator it; for(it=xl.begin(); it != xl.end(); ++it){ if (it->is("CardNo")){ CardNo=it->getInt(); } if (it->is("Type")){ Type=it->getInt(); } if (it->is("Time")){ Time=it->getInt(); } else if (it->is("Id")){ Id=it->getInt(); } else if (it->is("Updated")){ Modified.setStamp(it->getRaw()); } } } bool oFreePunch::setCardNo(int cno, bool databaseUpdate) { if (cno != CardNo) { pRunner r1 = oe->getRunner(tRunnerId, 0); int oldControlId = tMatchControlId; //oe->punchIndex[itype].remove(CardNo); // Remove from index // Remove ourself from index oEvent::PunchIndexType &pi = oe->punchIndex[iHashType]; oEvent::PunchConstIterator it = pi.find(CardNo); while (it != pi.end() && it->first == CardNo) { if (it->second == this) { pi.erase(it); break; } ++it; } oe->removeFromPunchHash(CardNo, Type, Time); rehashPunches(*oe, CardNo, 0); CardNo = cno; oe->insertIntoPunchHash(CardNo, Type, Time); rehashPunches(*oe, CardNo, this); pRunner r2 = oe->getRunner(tRunnerId, 0); if (r1 && oldControlId > 0) r1->markClassChanged(oldControlId); if (r2 && iHashType > 0) r2->markClassChanged(tMatchControlId); if (!databaseUpdate) updateChanged(); return true; } return false; } void oFreePunch::remove() { if (oe) oe->removeFreePunch(Id); } bool oFreePunch::canRemove() const { return true; } Table *oEvent::getPunchesTB()//Table mode { if (tables.count("punch") == 0) { Table *table=new Table(this, 20, L"Stämplingar", "punches"); table->addColumn("Id", 70, true, true); table->addColumn("Ändrad", 150, false); table->addColumn("Bricka", 70, true); table->addColumn("Kontroll", 70, true); table->addColumn("Tid", 70, false); table->addColumn("Löpare", 170, false); table->addColumn("Lag", 170, false); tables["punch"] = table; table->addOwnership(); } tables["punch"]->update(); return tables["punch"]; } void oEvent::generatePunchTableData(Table &table, oFreePunch *addPunch) { if (addPunch) { addPunch->addTableRow(table); return; } synchronizeList(oLPunchId); oFreePunchList::iterator it; oe->setupCardHash(false); table.reserve(punches.size()); for (it = punches.begin(); it != punches.end(); ++it){ if (!it->isRemoved()){ it->addTableRow(table); } } oe->setupCardHash(true); } void oFreePunch::addTableRow(Table &table) const { oFreePunch &it = *pFreePunch(this); table.addRow(getId(), &it); int row = 0; table.set(row++, it, TID_ID, itow(getId()), false, cellEdit); table.set(row++, it, TID_MODIFIED, getTimeStamp(), false, cellEdit); table.set(row++, it, TID_CARD, itow(getCardNo()), true, cellEdit); table.set(row++, it, TID_CONTROL, getType(), true, cellEdit); table.set(row++, it, TID_TIME, getTime(), true, cellEdit); pRunner r = 0; if (CardNo > 0) r = oe->getRunnerByCardNo(CardNo, Time, false); table.set(row++, it, TID_RUNNER, r ? r->getName() : L"?", false, cellEdit); if (r && r->getTeam()) table.set(row++, it, TID_TEAM, r->getTeam()->getName(), false, cellEdit); else table.set(row++, it, TID_TEAM, L"", false, cellEdit); } bool oFreePunch::inputData(int id, const wstring &input, int inputId, wstring &output, bool noUpdate) { synchronize(false); switch(id) { case TID_CARD: setCardNo(_wtoi(input.c_str())); synchronize(true); output = itow(CardNo); break; case TID_TIME: setTime(input); synchronize(true); output = getTime(); break; case TID_CONTROL: setType(input); synchronize(true); output = getType(); } return false; } void oFreePunch::fillInput(int id, vector< pair > &out, size_t &selected) { } void oFreePunch::setTimeInt(int t, bool databaseUpdate) { if (t != Time) { oe->removeFromPunchHash(CardNo, Type, Time); Time = t; oe->insertIntoPunchHash(CardNo, Type, Time); rehashPunches(*oe, CardNo, 0); if (!databaseUpdate) updateChanged(); } } bool oFreePunch::setType(const wstring &t, bool databaseUpdate) { int type = _wtoi(t.c_str()); int ttype = 0; if (type>0 && type<10000) ttype = type; else { if (t == lang.tl("Check")) ttype = oPunch::PunchCheck; else if (t == lang.tl("Mål")) ttype = oPunch::PunchFinish; if (t == lang.tl("Start")) ttype = oPunch::PunchStart; } if (ttype > 0 && ttype != Type) { oe->removeFromPunchHash(CardNo, Type, Time); Type = ttype; oe->insertIntoPunchHash(CardNo, Type, Time); int oldControlId = tMatchControlId; rehashPunches(*oe, CardNo, 0); pRunner r = oe->getRunner(tRunnerId, 0); if (r) { r->markClassChanged(tMatchControlId); if (oldControlId > 0) r->markClassChanged(oldControlId); } if (!databaseUpdate) updateChanged(); return true; } return false; } void oFreePunch::rehashPunches(oEvent &oe, int cardNo, pFreePunch newPunch) { if (disableHashing || (cardNo == 0 && !oe.punchIndex.empty()) || oe.punches.empty()) return; vector fp; if (oe.punchIndex.empty()) { // Rehash all punches. Ignore cardNo and newPunch (will be included automatically) fp.reserve(oe.punches.size()); for (oFreePunchList::iterator pit = oe.punches.begin(); pit != oe.punches.end(); ++pit) { if (pit->isRemoved()) continue; fp.push_back(&(*pit)); } sort(fp.begin(), fp.end(), FreePunchComp()); disableHashing = true; try { for (size_t j = 0; j < fp.size(); j++) { pFreePunch punch = fp[j]; punch->iHashType = oe.getControlIdFromPunch(punch->Time, punch->Type, punch->CardNo, true, *punch); oEvent::PunchIndexType &card2Punch = oe.punchIndex[punch->iHashType]; card2Punch.insert(make_pair(punch->CardNo, punch)); } } catch(...) { disableHashing = false; throw; } disableHashing = false; return; } map::iterator it; fp.reserve(oe.punchIndex.size() + 1); // Get all punches for the specified card. for(it = oe.punchIndex.begin(); it != oe.punchIndex.end(); ++it) { pair res = it->second.equal_range(cardNo); oEvent::PunchConstIterator pIter = res.first; while(pIter != res.second) { pFreePunch punch = pIter->second; assert(punch && punch->CardNo == cardNo); if (!punch->isRemoved()) { fp.push_back(punch); } ++pIter; } it->second.erase(res.first, res.second); } if (newPunch) fp.push_back(newPunch); sort(fp.begin(), fp.end(), FreePunchComp()); for (size_t j = 0; j < fp.size(); j++) { if (j>0 && fp[j-1] == fp[j]) continue; //Skip duplicates pFreePunch punch = fp[j]; punch->iHashType = oe.getControlIdFromPunch(punch->Time, punch->Type, cardNo, true, *punch); oEvent::PunchIndexType &card2Punch = oe.punchIndex[punch->iHashType]; card2Punch.insert(make_pair(punch->CardNo, punch)); } } //const int legHashConstant = 100000; int oFreePunch::getControlHash(int courseControlId, int race) { int newId = courseControlId + race*100000000; return newId; } int oFreePunch::getControlIdFromHash(int hash, bool courseControlId) { int r = (hash%100000000); if (courseControlId) return r; else return oControl::getIdIndexFromCourseControlId(r).first; } int oEvent::getControlIdFromPunch(int time, int type, int card, bool markClassChanged, oFreePunch &punch) { pRunner r = getRunnerByCardNo(card, time); punch.tRunnerId = -1; punch.tMatchControlId = type; if (r!=0) { punch.tRunnerId = r->Id; } int race = 0; if (type!=oPunch::PunchFinish) { pCourse c = r ? r->getCourse(false): 0; if (c!=0) { race = r->getRaceNo(); for (int k=0; knControls; k++) { pControl ctrl=c->getControl(k); if (ctrl && ctrl->hasNumber(type)) { int courseControlId = c->getCourseControlId(k); pFreePunch p = getPunch(r->getId(), courseControlId, card); if (!p || (p && abs(p->Time-time)<60)) { ctrl->tHasFreePunchLabel = true; punch.tMatchControlId = ctrl->getId(); punch.tIndex = k; int newId = oFreePunch::getControlHash(courseControlId, race); if (newId != punch.iHashType && markClassChanged && r) { r->markClassChanged(ctrl->getId()); if (punch.iHashType > 0) r->markClassChanged(oFreePunch::getControlIdFromHash(punch.iHashType, false)); } //Code controlId and runner race number into code return newId; } } } } } int newId = oFreePunch::getControlHash(type, 0); if (newId != punch.iHashType && markClassChanged && r) { r->markClassChanged(type); if (punch.iHashType > 0) r->markClassChanged(oFreePunch::getControlIdFromHash(punch.iHashType, false)); } return newId; } void oEvent::getFreeControls(set &controlId) const { controlId.clear(); for (map::const_iterator it = punchIndex.begin(); it != punchIndex.end(); ++it) { int id = oFreePunch::getControlIdFromHash(it->first, false); controlId.insert(id); } } //set< pair > readPunchHash; void oEvent::insertIntoPunchHash(int card, int code, int time) { if (time > 0) { int p1 = time * 4096 + code; int p2 = card; readPunchHash.insert(make_pair(p1, p2)); } } void oEvent::removeFromPunchHash(int card, int code, int time) { int p1 = time * 4096 + code; int p2 = card; readPunchHash.erase(make_pair(p1, p2)); } bool oEvent::isInPunchHash(int card, int code, int time) { int p1 = time * 4096 + code; int p2 = card; return readPunchHash.count(make_pair(p1, p2)) > 0; } pFreePunch oEvent::addFreePunch(int time, int type, int card, bool updateStartFinish) { if (time > 0 && isInPunchHash(card, type, time)) return 0; oFreePunch ofp(this, card, time, type); punches.push_back(ofp); pFreePunch fp=&punches.back(); oFreePunch::rehashPunches(*this, card, fp); insertIntoPunchHash(card, type, time); if (fp->getTiedRunner() && oe->isClient() && oe->getPropertyInt("UseDirectSocket", true)!=0) { SocketPunchInfo pi; pi.runnerId = fp->getTiedRunner()->getId(); pi.time = fp->getAdjustedTime(); pi.status = fp->getTiedRunner()->getStatus(); if (fp->getTypeCode() > 10) pi.iHashType = fp->getIHashType(); else pi.iHashType = fp->getTypeCode(); getDirectSocket().sendPunch(pi); } fp->updateChanged(); fp->synchronize(); // Update start/finish time if (updateStartFinish && type == oPunch::PunchStart || type == oPunch::PunchFinish) { pRunner tr = fp->getTiedRunner(); if (tr && tr->getStatus() == StatusUnknown && time > 0) { tr->synchronize(); if (type == oPunch::PunchStart) { if (tr->getClassRef() && !tr->getClassRef()->ignoreStartPunch()) tr->setStartTime(time, true, false); } else tr->setFinishTime(time); // Direct result if (type == oPunch::PunchFinish && tr->getClassRef() && tr->getClassRef()->hasDirectResult()) { if (tr->getCourse(false) == 0 && tr->getCard() == 0) { tr->setStatus(StatusOK, true, false, true); } else if (tr->getCourse(false) != 0 && tr->getCard() == 0) { pCard card = allocateCard(tr); card->setupFromRadioPunches(*tr); vector mp; card->synchronize(); tr->addPunches(card, mp); } } tr->synchronize(true); } } if (fp->getTiedRunner()) pushDirectChange(); return fp; } pFreePunch oEvent::addFreePunch(oFreePunch &fp) { insertIntoPunchHash(fp.CardNo, fp.Type, fp.Time); punches.push_back(fp); pFreePunch fpz=&punches.back(); oFreePunch::rehashPunches(*this, fp.CardNo, fpz); if (!fpz->existInDB() && HasDBConnection) { fpz->changed = true; fpz->synchronize(); } return fpz; } void oEvent::removeFreePunch(int Id) { oFreePunchList::iterator it; for (it=punches.begin(); it != punches.end(); ++it) { if (it->Id==Id) { pRunner r = getRunner(it->tRunnerId, 0); if (r && r->Class) { r->markClassChanged(it->tMatchControlId); classChanged(r->Class, true); } pFreePunch fp = &*it; if (HasDBConnection) msRemove(fp); //punchIndex[it->itype].remove(it->CardNo); PunchIndexType &ix = punchIndex[it->iHashType]; pair res = ix.equal_range(it->CardNo); while (res.first != res.second) { if (res.first->second == fp) { PunchConstIterator rm = res.first; ++res.first; ix.erase(rm); } else ++res.first; } int cardNo = fp->CardNo; removeFromPunchHash(cardNo, fp->Type, fp->Time); punches.erase(it); oFreePunch::rehashPunches(*this, cardNo, 0); dataRevision++; return; } } } pFreePunch oEvent::getPunch(int Id) const { oFreePunchList::const_iterator it; for (it=punches.begin(); it != punches.end(); ++it) { if (it->Id==Id) { if (it->isRemoved()) return 0; return const_cast(&*it); } } return 0; } pFreePunch oEvent::getPunch(int runnerId, int courseControlId, int card) const { //Lazy setup oFreePunch::rehashPunches(*oe, 0, 0); pRunner r = oe->getRunner(runnerId, 0); int runnerRace = r ? r->getRaceNo() : 0; map::const_iterator it1; int itype = oFreePunch::getControlHash(courseControlId, runnerRace); it1=punchIndex.find(itype); if (it1!=punchIndex.end()) { const oEvent::PunchIndexType &cIndex = it1->second; pair res = cIndex.equal_range(card); oEvent::PunchConstIterator pIter = res.first; while(pIter != res.second) { pFreePunch punch = pIter->second; if (!punch->isRemoved()) { assert(punch && punch->CardNo == card); if (punch->tRunnerId == runnerId || runnerId == 0) return punch; ++pIter; } } } map, oFreePunch>::const_iterator res = advanceInformationPunches.find(make_pair(itype, card)); if (res != advanceInformationPunches.end()) return (pFreePunch)&res->second; return 0; } void oEvent::getPunchesForRunner(int runnerId, vector &runnerPunches) const { runnerPunches.clear(); pRunner r = getRunner(runnerId, 0); if (r == 0) return; // Get times for when other runners used this card vector< pair > times; for (oRunnerList::const_iterator it = Runners.begin(); it != Runners.end(); ++it) { if (it->Id == runnerId) continue; if (it->Card && it->CardNo == r->CardNo) { pair t = it->Card->getTimeRange(); if (it->getStartTime() > 0) t.first = min(it->getStartTime(), t.first); if (it->getFinishTime() > 0) t.second = max(it->getFinishTime(), t.second); times.push_back(t); } } for (oFreePunchList::const_iterator it = punches.begin(); it != punches.end(); ++it) { if (it->CardNo == r->CardNo) { bool other = false; int t = it->Time; for (size_t k = 0; k= times[k].first && t <= times[k].second) other = true; } if (!other) runnerPunches.push_back(pFreePunch(&*it)); } } // XXX Advance punches... } bool oEvent::advancePunchInformation(const vector &gdi, vector &pi, bool fetchPunch, bool fetchFinish) { if (pi.empty()) return false; bool m = false; for (size_t k = 0; k < pi.size(); k++) { pRunner r = getRunner(pi[k].runnerId, 0); if (!r) continue; if (pi[k].iHashType == oPunch::PunchFinish && fetchFinish) { if (r->getStatus() == StatusUnknown && r->getFinishTime() <= 0 && !r->isChanged()) { r->FinishTime = pi[k].time; r->tStatus = RunnerStatus(pi[k].status); r->status = RunnerStatus(pi[k].status); // Will be overwritten (do not set isChanged flag) if (r->Class) { r->markClassChanged(oPunch::PunchFinish); classChanged(r->Class, false); } m = true; } } else if (fetchPunch) { // controlId is already the encoded format including index and race if (getPunch(pi[k].runnerId, pi[k].iHashType, r->getCardNo()) == 0) { oFreePunch fp(this, 0, pi[k].time, pi[k].iHashType); fp.tRunnerId = pi[k].runnerId; fp.iHashType = pi[k].iHashType; fp.tIndex = 0; fp.tMatchControlId = oFreePunch::getControlIdFromHash(fp.iHashType, false); fp.changed = false; pair hc(pi[k].iHashType, r->getCardNo()); advanceInformationPunches.insert(make_pair(hc,fp)); if (r->Class) { r->markClassChanged(oFreePunch::getControlIdFromHash(pi[k].iHashType, false)); classChanged(r->Class, true); } m = true; } } } if (m) { dataRevision++; for (size_t k = 0; kmakeEvent("DataUpdate", "autosync", 0, 0, false); } } return m; } void oEvent::getLatestPunches(int firstTime, vector &punchesOut) const { for (map< pair, oFreePunch>::const_iterator it = advanceInformationPunches.begin(); it != advanceInformationPunches.end(); ++it) { int time = it->second.getModificationTime(); if (time >= firstTime) punchesOut.push_back(&it->second); } for (oFreePunchList::const_iterator it = punches.begin(); it != punches.end(); ++it) { int time = it->getModificationTime(); if (time >= firstTime) punchesOut.push_back(&*it); } } pRunner oFreePunch::getTiedRunner() const { return oe->getRunner(tRunnerId, 0); } void oFreePunch::changedObject() { pRunner r = getTiedRunner(); if (r && tMatchControlId>0) r->markClassChanged(tMatchControlId); }