diff --git a/code/RestService.cpp b/code/RestService.cpp index 53893cb..49a2797 100644 --- a/code/RestService.cpp +++ b/code/RestService.cpp @@ -2,13 +2,15 @@ #include "RestService.h" #include "meos_util.h" #include "restserver.h" +#include "meosexception.h" + +#include int AutomaticCB(gdioutput *gdi, int type, void *data); -RestService::RestService() : AutoMachine("RestService"), port(-1) { +RestService::RestService() : AutoMachine("Informationsserver"), port(-1) { } - RestService::~RestService() { if (server) { server->stop(); @@ -17,14 +19,17 @@ RestService::~RestService() { } void RestService::save(oEvent &oe, gdioutput &gdi) { - if (!server) + if (!server) { server = RestServer::construct(); - - int port = gdi.getTextNo("Port"); - if (port > 0 && port < 65536) - server->startService(port); - else - throw std::exception("Invalid port number"); + + int xport = gdi.getTextNo("Port"); + if (xport > 0 && xport < 65536) { + port = xport; + server->startService(port); + } + else + throw meosException("Invalid port number"); + } } void RestService::settings(gdioutput &gdi, oEvent &oe, bool created) { @@ -35,7 +40,7 @@ void RestService::settings(gdioutput &gdi, oEvent &oe, bool created) { startCancelInterval(gdi, "Save", created, IntervalNone, L""); if (!server) - gdi.addInput("Port", itow(port), 10, 0, L"Port:", L"Testa genom http://localhost:[PORT]/meos"); + gdi.addInput("Port", itow(port), 10, 0, L"Port:", L"#http://localhost:[PORT]/meos"); else gdi.addString("", 0, "Server startad på X#" + itos(port)); @@ -46,29 +51,21 @@ void RestService::settings(gdioutput &gdi, oEvent &oe, bool created) { void RestService::status(gdioutput &gdi) { gdi.pushX(); gdi.addString("", 1, name); - /*if (!baseFile.empty()) { - gdi.fillRight(); - gdi.pushX(); - gdi.addString("", 0, L"Destination: X#" + baseFile); - - if (interval>0) { - gdi.popX(); - gdi.dropLine(1); - gdi.addString("", 0, "Säkerhetskopierar om: "); - gdi.addTimer(gdi.getCY(), gdi.getCX(), timerIgnoreSign, (GetTickCount() - timeout) / 1000); - } - - gdi.popX(); - }*/ if (server) { - gdi.addString("", 0, "Server startad på port X#" + itos(port)); + gdi.addString("", 0, "Server startad på X#" + itos(port)); RestServer::Statistics rs; server->getStatistics(rs); - gdi.addString("", 0, "Antal förfrågningar: X#" + itos(rs.numRequests)); - gdi.addString("", 0, "Genomsnittlig svarstid: X ms#" + itos(rs.averageResponseTime)); - gdi.addString("", 0, "Längsta svarstid: X ms#" + itos(rs.maxResponseTime)); + gdi.addString("", 0, "Antal förfrågningar: X.#" + itos(rs.numRequests)); + gdi.addString("", 0, "Genomsnittlig svarstid: X ms.#" + itos(rs.averageResponseTime)); + gdi.addString("", 0, "Längsta svarstid: X ms.#" + itos(rs.maxResponseTime)); + + gdi.dropLine(0.6); + gdi.addButton("Update", "Uppdatera").setHandler(this); + gdi.dropLine(0.6); + gdi.addString("", 1, "Testa servern:"); + gdi.addString("link", 0, "#http://localhost:" + itos(port) + "/meos").setHandler(this); } gdi.dropLine(2); @@ -87,6 +84,12 @@ void RestService::process(gdioutput &gdi, oEvent *oe, AutoSyncType ast) { void RestService::handle(gdioutput &gdi, BaseInfo &info, GuiEventType type) { if (type == GUI_BUTTON) { ButtonInfo &bi = static_cast(info); - + if (bi.id == "Update") { + gdi.getTabs().get(TAutoTab)->loadPage(gdi); + } + } + else if (type == GUI_LINK) { + wstring url = L"http://localhost:" + itow(port) + L"/meos"; + ShellExecute(NULL, L"open", url.c_str(), NULL, NULL, SW_SHOWNORMAL); } } \ No newline at end of file diff --git a/code/StdAfx.h b/code/StdAfx.h index 864ac4c..db921f9 100644 --- a/code/StdAfx.h +++ b/code/StdAfx.h @@ -23,13 +23,12 @@ #include #include -#include +#include #include #include #include #include -#include using namespace std; bool getUserFile(wchar_t *fileNamePath, const wchar_t *fileName); diff --git a/code/TabAuto.cpp b/code/TabAuto.cpp index 3e91537..57fec99 100644 --- a/code/TabAuto.cpp +++ b/code/TabAuto.cpp @@ -671,8 +671,11 @@ void AutoMachine::startCancelInterval(gdioutput &gdi, char *startCommand, bool c else if (type == IntervalSecond) gdi.addInput("Interval", intervalIn, 7, 0, L"Tidsintervall (sekunder):"); gdi.dropLine(1); - gdi.addButton(startCommand, "Starta automaten", AutomaticCB).setExtra(getId()); - gdi.addButton(created ? "Stop":"Cancel", "Avbryt", AutomaticCB).setExtra(getId()); + gdi.addButton(startCommand, created ? "Starta automaten" : "OK", AutomaticCB).setExtra(getId()); + if (!created) + gdi.addButton("Cancel", "Avbryt", AutomaticCB).setExtra(getId()); + + gdi.addButton("Stop", created ? "Avbryt" : "Stoppa automaten", AutomaticCB).setExtra(getId()); gdi.popX(); gdi.fillDown(); diff --git a/code/TabClass.cpp b/code/TabClass.cpp index dc55ead..7b2ba07 100644 --- a/code/TabClass.cpp +++ b/code/TabClass.cpp @@ -1325,6 +1325,7 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) return false; DWORD cid=ClassId; + pClass pc = oe->getClass(cid); DrawMethod method = DrawMethod(gdi.getSelectedItem("Method").first); int interval = 0; @@ -1339,6 +1340,8 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) if (gdi.hasField("Leg")) { leg = gdi.getSelectedItem("Leg").first; } + else if (pc && pc->getParentClass() != 0) + leg = -1; wstring bib; bool doBibs = false; @@ -1536,6 +1539,19 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) setMultiDayClass(gdi, gdi.isChecked(bi.id), lastDrawMethod); } + else if (bi.id == "QualificationFinal") { + save(gdi, true); + pClass pc = oe->getClass(ClassId); + if (!pc) + throw std::exception("Class not found"); + + vector< pair > ext; + ext.push_back(make_pair(L"Qualfication/Final", L"*.xml")); + wstring fileName = gdi.browseForOpen(ext, L"xml"); + pc->loadQualificationFinalScheme(fileName); + pc->updateFinalClasses(0, true); + loadPage(gdi); + } else if (bi.id=="Bibs") { save(gdi, true); if (!checkClassSelected(gdi)) @@ -1572,7 +1588,7 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) gdi.selectItemByData("BibSettings", bt); wstring bib = pc->getDCI().getString("Bib"); - if (pc->getNumDistinctRunners() > 1) { + if (pc->getNumDistinctRunners() > 1 || pc->getQualificationFinal()) { bibTeamOptions.push_back(make_pair(lang.tl("Oberoende"), BibFree)); bibTeamOptions.push_back(make_pair(lang.tl("Samma"), BibSame)); bibTeamOptions.push_back(make_pair(lang.tl("Ökande"), BibAdd)); @@ -1620,16 +1636,16 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) AutoBibType bt = AutoBibType(gdi.getSelectedItem("BibSettings").first); - pair teamBib = gdi.getSelectedItem("TeamBib"); + pair teamBib = gdi.getSelectedItem("BibTeam"); if (teamBib.second) { pc->setBibMode(BibMode(teamBib.first)); } pc->getDI().setString("Bib", getBibCode(bt, gdi.getText("Bib"))); pc->synchronize(); - + int leg = pc->getParentClass() ? -1 : 0; if (bt == AutoBibManual) { - oe->addBib(cid, 0, gdi.getText("Bib")); + oe->addBib(cid, leg, gdi.getText("Bib")); } else { oe->setBibClassGap(gdi.getTextNo("BibGap")); @@ -1644,7 +1660,7 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data) par.selection.insert(cid); oListInfo info; par.listCode = EStdStartList; - par.setLegNumberCoded(0); + par.setLegNumberCoded(leg); oe->generateListInfo(par, gdi.getLineHeight(), info); oe->generateList(gdi, false, info, true); @@ -2223,6 +2239,10 @@ void TabClass::selectClass(gdioutput &gdi, int cid) if (gdi.hasField("DirectResult")) gdi.check("DirectResult", false); + if (gdi.hasField("LockStartList")) { + gdi.check("LockStartList", false); + gdi.setInputStatus("LockStartList", false); + } gdi.check("NoTiming", false); ClassId=cid; @@ -2274,6 +2294,11 @@ void TabClass::selectClass(gdioutput &gdi, int cid) if (gdi.hasField("DirectResult")) gdi.check("DirectResult", pc->hasDirectResult()); + if (gdi.hasField("LockStartList")) { + bool active = pc->getParentClass() != 0; + gdi.setInputStatus("LockStartList", active); + gdi.check("LockStartList", active && pc->lockedClassAssignment()); + } ClassId=cid; if (pc->hasTrueMultiCourse()) { @@ -2344,9 +2369,10 @@ void TabClass::selectClass(gdioutput &gdi, int cid) pCourse pcourse = pc->getCourse(); gdi.selectItemByData("Courses", pcourse ? pcourse->getId():-2); } + if (gdi.hasField("QualificationFinal")) + gdi.setInputStatus("QualificationFinal", pc->getParentClass() == 0); gdi.selectItemByData("Classes", cid); - ClassId=cid; EditChanged=false; } @@ -2672,6 +2698,12 @@ void TabClass::save(gdioutput &gdi, bool skipReload) pc->setDirectResult(withDirect); } + if (gdi.hasField("LockStartList")) { + bool locked = gdi.isChecked("LockStartList"); + if (pc->getParentClass()) + pc->lockedClassAssignment(locked); + } + int crs = gdi.getSelectedItem("Courses").first; if (crs==0) { @@ -2893,11 +2925,28 @@ bool TabClass::loadPage(gdioutput &gdi) gdi.addCheckbox("DirectResult", "Resultat vid målstämpling", 0, false, "help:DirectResult"); - } gdi.dropLine(2); gdi.popX(); + { + vector pcls; + oe->getClasses(pcls, false); + bool hasCF = false; + for (pClass pc : pcls) { + if (pc->getQualificationFinal()) { + hasCF = true; + break; + } + } + if (hasCF) { + gdi.addCheckbox("LockStartList", "Lås startlista", 0, false, + "help:LockStartList"); + + gdi.dropLine(2); + gdi.popX(); + } + } vector func; if (oe->getMeOSFeatures().hasFeature(MeOSFeatures::DrawStartList)) func.push_back(ButtonData("Draw", "Lotta / starttider...", false)); @@ -2960,6 +3009,9 @@ bool TabClass::loadPage(gdioutput &gdi) func.push_back(ButtonData("QuickSettings", "Snabbinställningar", true)); + if (oe->getMeOSFeatures().hasFeature(MeOSFeatures::MultipleRaces)) + func.push_back(ButtonData("QualificationFinal", "Kval-Final-Schema", false)); + RECT funRect; funRect.right = gdi.getCX() - 7; funRect.top = gdi.getCY() - 2; @@ -3216,7 +3268,7 @@ void TabClass::drawDialog(gdioutput &gdi, DrawMethod method, const oClass &pc) { if (method != DMSimultaneous) gdi.addInput("Interval", formatTime(interval), 10, 0, L"Startintervall (min):").setSynchData(&lastInterval); - if (method == DMRandom || method == DMSOFT || method == DMClumped) + if (method == DMRandom || method == DMSOFT || method == DMClumped && pc.getParentClass() == 0) gdi.addInput("Vacanses", itow(vac), 10, 0, L"Antal vakanser:").setSynchData(&lastNumVac); if ((method == DMRandom || method == DMSOFT || method == DMSeeded) && pc.getNumStages() > 1 && pc.getClassType() != oClassPatrol) { @@ -3765,7 +3817,7 @@ void TabClass::getClassSettingsTable(gdioutput &gdi, GUICALLBACK cb) { else gdi.setText("Bib"+ id, bib); - if (useTeam && it->getNumDistinctRunners() > 1) { + if (useTeam && (it->getNumDistinctRunners() > 1 || it->getQualificationFinal())) { gdi.addSelection(et, cyp, "BibTeam" + id, 80, 100, 0, L"", L"Ange relation mellan lagets och deltagarnas nummerlappar."); gdi.addItem("BibTeam" + id, bibTeamOptions); gdi.selectItemByData("BibTeam" + id, it->getBibMode()); diff --git a/code/TabClub.cpp b/code/TabClub.cpp index 5bfa4ae..f70334b 100644 --- a/code/TabClub.cpp +++ b/code/TabClub.cpp @@ -528,7 +528,7 @@ int TabClub::clubCB(gdioutput &gdi, int type, void *data) else fee = di.getInt("Fee"); - wstring info = filtered[k]->getClass() + L", " + filtered[k]->getCompleteIdentification(); + wstring info = filtered[k]->getClass(false) + L", " + filtered[k]->getCompleteIdentification(); gdi.addStringUT(0, info + L" (" + oe->formatCurrency(fee) + L")"); if (count % 5 == 0) diff --git a/code/TabCompetition.cpp b/code/TabCompetition.cpp index a98d155..182c50d 100644 --- a/code/TabCompetition.cpp +++ b/code/TabCompetition.cpp @@ -110,22 +110,23 @@ bool TabCompetition::save(gdioutput &gdi, bool write) bool oldLT = oe->useLongTimes(); wstring oldDate = oe->getDate(); - if ((newZT != oldZT || - longTimes != oldLT || - (longTimes && date != oldDate)) && oe->classHasResults(0)) { + if ((newZT != oldZT || + longTimes != oldLT || + (longTimes && date != oldDate)) && oe->classHasResults(0)) { if (!gdi.ask(L"warn:changedtimezero")) { gdi.setText("ZeroTime", oe->getZeroTime()); gdi.check("LongTimes", oe->useLongTimes()); gdi.setText("Date", oe->getDate()); return 0; } - bool updateTimes = newZT != oldZT && oe->getNumRunners() > 0 && gdi.ask(L"ask:updatetimes"); +} + bool updateTimes = newZT != oldZT && oe->getNumRunners() > 0 && gdi.ask(L"ask:updatetimes"); - if (updateTimes) { - int delta = oldZT - newZT; - oe->updateStartTimes(delta); - } + if (updateTimes) { + int delta = oldZT - newZT; + oe->updateStartTimes(delta); } + oe->setDate(date); oe->useLongTimes(longTimes); oe->setName(gdi.getText("Name")); @@ -3228,7 +3229,7 @@ void TabCompetition::welcomeToMeOS(gdioutput &gdi) { void TabCompetition::displayRunners(gdioutput &gdi, const vector &changedClass) const { for (size_t k = 0; kgetName() + L" (" + changedClass[k]->getClass() + L", " + + gdi.addStringUT(0, changedClass[k]->getName() + L" (" + changedClass[k]->getClass(true) + L", " + changedClass[k]->getStartTimeS() + L")"); } } @@ -3991,7 +3992,7 @@ void TabCompetition::checkReadyForResultExport(gdioutput &gdi, const set &c int numVacant = 0; for (pRunner r : runners) { - if (!classFilter.empty() && !classFilter.count(r->getClassId())) + if (!classFilter.empty() && !classFilter.count(r->getClassId(false))) continue; if (r->isVacant()) diff --git a/code/TabList.cpp b/code/TabList.cpp index 3e6e86c..94db12a 100644 --- a/code/TabList.cpp +++ b/code/TabList.cpp @@ -601,7 +601,6 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) lastSplitState = par.showSplitTimes; lastLargeSize = par.useLargeSize; - oe->generateListInfo(par, gdi.getLineHeight(), currentList); generateList(gdi); @@ -635,10 +634,8 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) oListParam par; ClassConfigInfo cnf; oe->getClassConfigurationInfo(cnf); + getResultIndividual(par, cnf); cnf.getIndividual(par.selection); - par.listCode = EStdResultList; - par.showInterTimes = true; - par.setLegNumberCoded(-1); par.pageBreak = gdi.isChecked("PageBreak"); par.splitAnalysis = gdi.isChecked("SplitAnalysis"); @@ -667,12 +664,10 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) else if (bi.id=="StartIndividual") { oe->sanityCheck(gdi, false); oListParam par; - par.listCode = EStdStartList; - par.setLegNumberCoded(-1); - par.pageBreak = gdi.isChecked("PageBreak"); ClassConfigInfo cnf; oe->getClassConfigurationInfo(cnf); - cnf.getIndividual(par.selection); + getStartIndividual(par, cnf); + par.pageBreak = gdi.isChecked("PageBreak"); oe->generateListInfo(par, gdi.getLineHeight(), currentList); currentList.setCallback(openRunnerTeamCB); generateList(gdi); @@ -681,33 +676,22 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) else if (bi.id=="StartClub") { oe->sanityCheck(gdi, false); oListParam par; - par.listCode = EStdClubStartList; + getStartClub(par); par.pageBreak = gdi.isChecked("PageBreak"); - par.setLegNumberCoded(-1); - ClassConfigInfo cnf; - oe->getClassConfigurationInfo(cnf); - //cnf.getIndividual(par.selection); - //cnf.getPatrol(par.selection); - - // oListInfo foo = currentList; oe->generateListInfo(par, gdi.getLineHeight(), currentList); currentList.setCallback(openRunnerTeamCB); - //currentList.addList(foo); generateList(gdi); gdi.refresh(); } else if (bi.id=="ResultClub") { oe->sanityCheck(gdi, false); oListParam par; - par.listCode = EStdClubResultList; - par.pageBreak = gdi.isChecked("PageBreak"); - par.splitAnalysis = gdi.isChecked("SplitAnalysis"); - par.setLegNumberCoded(-1); ClassConfigInfo cnf; oe->getClassConfigurationInfo(cnf); - cnf.getIndividual(par.selection); - cnf.getPatrol(par.selection); + getResultClub(par, cnf); + par.pageBreak = gdi.isChecked("PageBreak"); + par.splitAnalysis = gdi.isChecked("SplitAnalysis"); oe->generateListInfo(par, gdi.getLineHeight(), currentList); currentList.setCallback(openRunnerTeamCB); generateList(gdi); @@ -735,12 +719,10 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) else if (bi.id=="TeamStartList") { oe->sanityCheck(gdi, false); oListParam par; - par.listCode = EStdTeamStartList; ClassConfigInfo cnf; oe->getClassConfigurationInfo(cnf); - cnf.getRelay(par.selection); + getStartTeam(par, cnf); par.pageBreak = gdi.isChecked("PageBreak"); - par.setLegNumberCoded(0); oe->generateListInfo(par, gdi.getLineHeight(), currentList); currentList.setCallback(openRunnerTeamCB); generateList(gdi); @@ -779,11 +761,10 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) else if (bi.id=="PatrolStartList") { oe->sanityCheck(gdi, false); oListParam par; - par.pageBreak = gdi.isChecked("PageBreak"); - par.listCode = EStdPatrolStartList; ClassConfigInfo cnf; oe->getClassConfigurationInfo(cnf); - cnf.getPatrol(par.selection); + getStartPatrol(par, cnf); + par.pageBreak = gdi.isChecked("PageBreak"); oe->generateListInfo(par, gdi.getLineHeight(), currentList); currentList.setCallback(openRunnerTeamCB); generateList(gdi); @@ -792,15 +773,12 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) else if (bi.id=="TeamResults") { oe->sanityCheck(gdi, true); oListParam par; - par.pageBreak = gdi.isChecked("PageBreak"); - par.splitAnalysis = gdi.isChecked("SplitAnalysis"); - par.listCode = EStdTeamResultListAll; - - par.filterMaxPer = gdi.getSelectedItem("ClassLimit").first; - ClassConfigInfo cnf; oe->getClassConfigurationInfo(cnf); - cnf.getRelay(par.selection); + getResultTeam(par, cnf); + par.pageBreak = gdi.isChecked("PageBreak"); + par.splitAnalysis = gdi.isChecked("SplitAnalysis"); + par.filterMaxPer = gdi.getSelectedItem("ClassLimit").first; oe->generateListInfo(par, gdi.getLineHeight(), currentList); generateList(gdi); gdi.refresh(); @@ -823,7 +801,7 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) oListParam par; par.pageBreak = gdi.isChecked("PageBreak"); par.splitAnalysis = gdi.isChecked("SplitAnalysis"); - int race = int(bi.getExtra()); + int race = bi.getExtraInt(); par.setLegNumberCoded(race); par.listCode = EStdIndMultiResultListLeg; ClassConfigInfo cnf; @@ -854,13 +832,11 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) else if (bi.id=="PatrolResultList") { oe->sanityCheck(gdi, false); oListParam par; - par.pageBreak = gdi.isChecked("PageBreak"); - par.splitAnalysis = gdi.isChecked("SplitAnalysis"); - par.listCode = EStdPatrolResultList; ClassConfigInfo cnf; oe->getClassConfigurationInfo(cnf); - cnf.getPatrol(par.selection); - + getResultPatrol(par, cnf); + par.pageBreak = gdi.isChecked("PageBreak"); + par.splitAnalysis = gdi.isChecked("SplitAnalysis"); par.filterMaxPer = gdi.getSelectedItem("ClassLimit").first; oe->generateListInfo(par, gdi.getLineHeight(), currentList); @@ -871,13 +847,11 @@ int TabList::listCB(gdioutput &gdi, int type, void *data) else if (bi.id=="RogainingResultList") { oe->sanityCheck(gdi, true); oListParam par; - par.pageBreak = gdi.isChecked("PageBreak"); - par.splitAnalysis = gdi.isChecked("SplitAnalysis"); - par.listCode = ERogainingInd; ClassConfigInfo cnf; oe->getClassConfigurationInfo(cnf); - cnf.getRogaining(par.selection); - + getResultRogaining(par, cnf); + par.pageBreak = gdi.isChecked("PageBreak"); + par.splitAnalysis = gdi.isChecked("SplitAnalysis"); par.filterMaxPer = gdi.getSelectedItem("ClassLimit").first; oe->generateListInfo(par, gdi.getLineHeight(), currentList); @@ -2420,3 +2394,127 @@ void TabList::setAnimationMode(gdioutput &gdi) { gdi.setAnimationMode(make_shared(gdi, par.timePerPage, par.nColumns, par.margin, par.animate)); } + +void TabList::getStartIndividual(oListParam &par, ClassConfigInfo &cnf){ + par.listCode = EStdStartList; + par.setLegNumberCoded(-1); + cnf.getIndividual(par.selection); +} + +void TabList::getStartClub(oListParam &par) { + par.listCode = EStdClubStartList; + par.setLegNumberCoded(-1); +} + +void TabList::getResultIndividual(oListParam &par, ClassConfigInfo &cnf) { + cnf.getIndividual(par.selection); + par.listCode = EStdResultList; + par.showInterTimes = true; + par.setLegNumberCoded(-1); +} + +void TabList::getResultClub(oListParam &par, ClassConfigInfo &cnf) { + par.listCode = EStdClubResultList; + par.setLegNumberCoded(-1); + cnf.getIndividual(par.selection); + cnf.getPatrol(par.selection); +} + +void TabList::getStartPatrol(oListParam &par, ClassConfigInfo &cnf) { + par.listCode = EStdPatrolStartList; + cnf.getPatrol(par.selection); +} + +void TabList::getResultPatrol(oListParam &par, ClassConfigInfo &cnf) { + par.listCode = EStdPatrolResultList; + cnf.getPatrol(par.selection); +} + +void TabList::getStartTeam(oListParam &par, ClassConfigInfo &cnf) { + par.listCode = EStdTeamStartList; + cnf.getRelay(par.selection); + par.setLegNumberCoded(0); +} + +void TabList::getResultTeam(oListParam &par, ClassConfigInfo &cnf) { + par.listCode = EStdTeamResultListAll; + cnf.getRelay(par.selection); +} + +void TabList::getResultRogaining(oListParam &par, ClassConfigInfo &cnf) { + par.listCode = ERogainingInd; + cnf.getRogaining(par.selection); +} + +void TabList::getPublicLists(oEvent &oe, vector &lists) { + lists.clear(); + + ClassConfigInfo cnf; + oe.getClassConfigurationInfo(cnf); + if (!cnf.empty()) { + if (cnf.hasIndividual()) { + lists.push_back(oListParam()); + getStartIndividual(lists.back(), cnf); + + if (oe.getMeOSFeatures().hasFeature(MeOSFeatures::Clubs)) { + lists.push_back(oListParam()); + getStartClub(lists.back()); + } + } + + if (cnf.hasRelay()) { + lists.push_back(oListParam()); + getStartTeam(lists.back(), cnf); + } + if (cnf.hasPatrol()) { + lists.push_back(oListParam()); + getStartPatrol(lists.back(), cnf); + } + + if (cnf.isMultiStageEvent()) { + //gdi.addButton("StartL:inputresult", "Input Results", ListsCB); + } + + if (cnf.hasIndividual()) { + lists.push_back(oListParam()); + getResultIndividual(lists.back(), cnf); + if (oe.getMeOSFeatures().hasFeature(MeOSFeatures::Clubs)) { + lists.push_back(oListParam()); + getResultClub(lists.back(), cnf); + } + + //gdi.addButton("ResultIndSplit", "Sträcktider", ListsCB); + + if (cnf.isMultiStageEvent()) { + //gdi.addButton("Result:stageresult", "Etappresultat", ListsCB); + + //gdi.addButton("Result:finalresult", "Slutresultat", ListsCB); + } + } + if (cnf.hasRelay()) { + lists.push_back(oListParam()); + getResultTeam(lists.back(), cnf); + } + if (cnf.hasPatrol()) { + lists.push_back(oListParam()); + getResultPatrol(lists.back(), cnf); + } + + if (cnf.hasRogaining()) { + //gdi.addButton("Result:rogainingind", "Rogaining", ListsCB).setExtra(2); + } + } + + MetaListContainer &lc = oe.getListContainer(); + + vector< pair > savedParams; + lc.getListParam(savedParams); + for (auto &p : savedParams) { + oListParam &par = lc.getParam(p.second); + lists.push_back(par); + } + + if (cnf.hasIndividual()) { + //gdi.addButton("PriceList", "Prisutdelningslista", ListsCB); + } +} diff --git a/code/TabList.h b/code/TabList.h index 5542724..2f5d249 100644 --- a/code/TabList.h +++ b/code/TabList.h @@ -87,7 +87,25 @@ private: /** Set animation mode*/ void setAnimationMode(gdioutput &gdi); + + static void getStartIndividual(oListParam &par, ClassConfigInfo &cnf); + static void getStartClub(oListParam &par); + static void getResultIndividual(oListParam &par, ClassConfigInfo &cnf); + static void getResultClub(oListParam &par, ClassConfigInfo &cnf); + + static void getStartPatrol(oListParam &par, ClassConfigInfo &cnf); + static void getResultPatrol(oListParam &par, ClassConfigInfo &cnf); + + static void getStartTeam(oListParam &par, ClassConfigInfo &cnf); + static void getResultTeam(oListParam &par, ClassConfigInfo &cnf); + + static void getResultRogaining(oListParam &par, ClassConfigInfo &cnf); + + public: + /** Returns a collection of public lists. */ + void static getPublicLists(oEvent &oe, vector &lists); + bool loadPage(gdioutput &gdi); bool loadPage(gdioutput &gdi, const string &command); diff --git a/code/TabRunner.cpp b/code/TabRunner.cpp index 5655253..9c81331 100644 --- a/code/TabRunner.cpp +++ b/code/TabRunner.cpp @@ -106,7 +106,7 @@ void TabRunner::enableControlButtons(gdioutput &gdi, bool enable, bool vacant) void TabRunner::selectRunner(gdioutput &gdi, pRunner r) { if (!r) { - runnerId=0; + runnerId = 0; gdi.setText("Name", L""); gdi.setText("Bib", L""); gdi.selectItemByData("RCourse", 0); @@ -158,7 +158,7 @@ void TabRunner::selectRunner(gdioutput &gdi, pRunner r) { gdi.enableEditControls(true); disablePunchCourse(gdi); - pRunner parent=r->getMultiRunner(0); + pRunner parent = r->getMultiRunner(0); r->synchronizeAll(); //r->apply(false); @@ -172,14 +172,14 @@ void TabRunner::selectRunner(gdioutput &gdi, pRunner r) { gdi.selectItemByData("Runners", parent->getId()); - runnerId=r->getId(); + runnerId = r->getId(); gdi.setText("Name", r->getNameRaw()); wstring bib = r->getBib(); if (gdi.hasField("Bib")) { gdi.setText("Bib", bib); - bool controlBib = r->getTeam() == 0 || (r->getClassRef() && r->getClassRef()->getBibMode() == BibFree); + bool controlBib = r->getTeam() == 0 || (r->getClassRef(true) && r->getClassRef(true)->getBibMode() == BibFree); gdi.setInputStatus("Bib", controlBib); } @@ -187,10 +187,10 @@ void TabRunner::selectRunner(gdioutput &gdi, pRunner r) { oe->fillClasses(gdi, "RClass", oEvent::extraNone, oEvent::filterNone); gdi.addItem("RClass", lang.tl("Ingen klass"), 0); - gdi.selectItemByData("RClass", r->getClassId()); + gdi.selectItemByData("RClass", r->getClassId(true)); if (gdi.hasField("EditTeam")) { - gdi.setInputStatus("EditTeam", r->getTeam()!=0); + gdi.setInputStatus("EditTeam", r->getTeam() != 0); if (r->getTeam()) { gdi.setText("Team", r->getTeam()->getName()); @@ -217,19 +217,19 @@ void TabRunner::selectRunner(gdioutput &gdi, pRunner r) { r->getLegPlacesAcc(placeAcc); wstring out; - for (size_t k = 0; k0) - out+= L" +" + getTimeMS(after[k]); + if (after[k] > 0) + out += L" +" + getTimeMS(after[k]); - if (k0) - out+= L" (+" + getTimeMS(afterAcc[k]) + L")"; + if (k < afterAcc.size() && afterAcc[k]>0) + out += L" (+" + getTimeMS(afterAcc[k]) + L")"; - if (delta[k]>0) - out+= L" B: " + getTimeMS(delta[k]); + if (delta[k] > 0) + out += L" B: " + getTimeMS(delta[k]); out += L" | "; @@ -241,18 +241,18 @@ void TabRunner::selectRunner(gdioutput &gdi, pRunner r) { #endif if (gdi.hasField("MultiR")) { - int numMulti=parent->getNumMulti(); - if (numMulti==0) { + int numMulti = parent->getNumMulti(); + if (numMulti == 0) { gdi.clearList("MultiR"); gdi.disableInput("MultiR"); - lastRace=0; + lastRace = 0; } else { gdi.clearList("MultiR"); gdi.enableInput("MultiR"); - for (int k=0;kgetRaceNo()); } @@ -264,7 +264,7 @@ void TabRunner::selectRunner(gdioutput &gdi, pRunner r) { updateNumShort(gdi, r->getCourse(false), r); int cno = parent->getCardNo(); - gdi.setText("CardNo", cno>0 ? itow(cno) : L""); + gdi.setText("CardNo", cno > 0 ? itow(cno) : L""); warnDuplicateCard(gdi, cno, r); @@ -316,9 +316,9 @@ void TabRunner::selectRunner(gdioutput &gdi, pRunner r) { gdi.setText("PointIn", r->getInputPoints()); } - pCard pc=r->getCard(); + pCard pc = r->getCard(); - pCourse pcourse=r->getCourse(true); + pCourse pcourse = r->getCourse(true); if (pc) { gdi.setTabStops("Punches", 70); @@ -337,6 +337,12 @@ void TabRunner::selectRunner(gdioutput &gdi, pRunner r) { gdi.clearList("Course"); gdi.disableInput("AddAllC"); } + + gdioutput *gdi_settings = getExtraWindow("ecosettings", false); + if (gdi_settings) { + TabRunner &dst = dynamic_cast(*gdi_settings->getTabs().get(TabType::TRunnerTab)); + dst.loadEconomy(*gdi_settings, *r); + } } int RunnerCB(gdioutput *gdi, int type, void *data) @@ -579,6 +585,12 @@ pRunner TabRunner::save(gdioutput &gdi, int runnerId, bool willExit) { if (gdi.hasField("Fee")) r->getDI().setInt("Fee", oe->interpretCurrency(gdi.getText("Fee"))); + gdioutput *gdi_settings = getExtraWindow("ecosettings", false); + if (gdi_settings) { + TabRunner &dst = dynamic_cast(*gdi_settings->getTabs().get(TabType::TRunnerTab)); + dst.getEconomyHandler(*r)->save(*gdi_settings); + } + r->setStartTimeS(gdi.getText("Start")); r->setFinishTimeS(gdi.getText("Finish")); @@ -608,14 +620,14 @@ pRunner TabRunner::save(gdioutput &gdi, int runnerId, bool willExit) { gdi.alert("För att delta i en lagklass måste deltagaren ingå i ett lag."); classId = 0; } - else if (r->getTeam()->getClassId() != classId && r->getClassId() != classId) { + else if (r->getTeam()->getClassId(true) != classId && r->getClassId(true) != classId) { gdi.alert("Deltagarens klass styrs av laget."); - classId = r->getTeam()->getClassId(); + classId = r->getTeam()->getClassId(false); } } bool readStatusIn = true; - if (r->getClassId() != classId && r->getInputStatus() != StatusNotCompetiting && r->hasInputData()) { + if (r->getClassId(true) != classId && r->getInputStatus() != StatusNotCompetiting && r->hasInputData()) { if (gdi.ask(L"Vill du sätta resultatet från tidigare etapper till ?")) { r->resetInputData(); readStatusIn = false; @@ -635,7 +647,7 @@ pRunner TabRunner::save(gdioutput &gdi, int runnerId, bool willExit) { vector mp; r->evaluateCard(true, mp, 0, true); - if (r->getClassId() != classId) { + if (r->getClassId(true) != classId) { gdi.alert("Deltagarens klass styrs av laget."); } @@ -652,11 +664,14 @@ pRunner TabRunner::save(gdioutput &gdi, int runnerId, bool willExit) { r->synchronizeAll(); - if (r->getClassRef() && r->getClassRef()->hasClassGlobalDependance()) { + if (r->getClassRef(false) && r->getClassRef(false)->hasClassGlobalDependence()) { set cls; - cls.insert(r->getClassId()); + cls.insert(r->getClassId(false)); oe->reEvaluateAll(cls, false); } + + if (r->getClassRef(false)) + r->getClassRef(false)->updateFinalClasses(r, false); } else runnerId=0; @@ -938,9 +953,11 @@ int TabRunner::runnerCB(gdioutput &gdi, int type, void *data) if (!runnerId) return 0; pRunner r = oe->getRunner(runnerId, 0); - gdioutput *settings = createExtraWindow("ecosettings", L"Economy for X#" + r->getName(), 400, 200); - - + if (getExtraWindow("ecosettings", true) == 0) { + gdioutput *settings = createExtraWindow("ecosettings", L"Economy", 550, 350); + TabRunner &dst = dynamic_cast(*settings->getTabs().get(TabType::TRunnerTab)); + dst.loadEconomy(*settings, *r); + } } else if (bi.id=="NoStart") { if (!runnerId) @@ -950,7 +967,7 @@ int TabRunner::runnerCB(gdioutput &gdi, int type, void *data) if (r && gdi.ask(L"Bekräfta att deltagaren har lämnat återbud.")) { if (r->getStartTime()>0) { - pRunner newRunner = oe->addRunnerVacant(r->getClassId()); + pRunner newRunner = oe->addRunnerVacant(r->getClassId(true)); newRunner->cloneStartTime(r); newRunner->setStartNo(r->getStartNo(), false); if (r->getCourseId()) @@ -1045,12 +1062,18 @@ int TabRunner::runnerCB(gdioutput &gdi, int type, void *data) if (gdi.isInputChanged("")) { pRunner r = oe->getRunner(runnerId, 0); bool newName = r && r->getName() != gdi.getText("Name"); - save(gdi, runnerId, true); - if (newName) fillRunnerList(gdi); } + else { + pRunner r = oe->getRunner(runnerId, 0); + gdioutput *gdi_settings = getExtraWindow("ecosettings", false); + if (gdi_settings) { + TabRunner &dst = dynamic_cast(*gdi_settings->getTabs().get(TabType::TRunnerTab)); + dst.getEconomyHandler(*r)->save(*gdi_settings); + } + } if (bi.data == -1) { fillRunnerList(gdi); @@ -1193,16 +1216,20 @@ int TabRunner::runnerCB(gdioutput &gdi, int type, void *data) } } else if (type==GUI_CLEAR) { + gdioutput *gdi_settings = getExtraWindow("ecosettings", false); + if (gdi_settings) + gdi_settings->closeWindow(); + if (runnerId>0 && currentMode == 0) save(gdi, runnerId, true); - + return true; } else if (type == GUI_LINK) { int id = static_cast(data)->getExtraInt(); oRunner *vacancy = oe->getRunner(id, 0); - if (vacancy==0 || vacancy->getClassId()==0) + if (vacancy==0 || vacancy->getClassId(false)==0) return -1; pRunner r = oe->getRunner(runnerId, 0); @@ -1215,7 +1242,7 @@ int TabRunner::runnerCB(gdioutput &gdi, int type, void *data) wchar_t bf[1024]; swprintf_s(bf, lang.tl("Bekräfta att %s byter klass till %s.").c_str(), - r->getName().c_str(), vacancy->getClass().c_str()); + r->getName().c_str(), vacancy->getClass(true).c_str()); if (gdi.ask(wstring(L"#") + bf)) { vacancy->synchronize(); @@ -1226,11 +1253,11 @@ int TabRunner::runnerCB(gdioutput &gdi, int type, void *data) temp.setTemporary(); temp.setBib(r->getBib(), 0, false, false); temp.setStartNo(r->getStartNo(), false); - temp.setClassId(r->getClassId(), true); + temp.setClassId(r->getClassId(true), true); temp.apply(false, 0, false); temp.cloneStartTime(r); - r->setClassId(vacancy->getClassId(), true); + r->setClassId(vacancy->getClassId(true), true); // Remove or create multi runners r->createMultiRunner(true, true); r->apply(false, 0, false); @@ -1243,7 +1270,7 @@ int TabRunner::runnerCB(gdioutput &gdi, int type, void *data) r->resetInputData(); } - vacancy->setClassId(temp.getClassId(), true); + vacancy->setClassId(temp.getClassId(true), true); // Remove or create multi runners vacancy->createMultiRunner(true, true); vacancy->apply(false, 0, false); @@ -1321,7 +1348,7 @@ int TabRunner::vacancyCB(gdioutput &gdi, int type, void *data) int birthYear = 0; pClub pc = oe->getClubCreate(0, club); - r->updateFromDB(name, pc->getId(), r->getClassId(), cardNo, birthYear); + r->updateFromDB(name, pc->getId(), r->getClassId(false), cardNo, birthYear); r->setName(name, true); r->setCardNo(cardNo, true); @@ -1377,7 +1404,7 @@ int TabRunner::vacancyCB(gdioutput &gdi, int type, void *data) // Print start certificate tsi.generateStartInfo(gdi, *r); - showVacancyList(gdi, "", r->getClassId()); + showVacancyList(gdi, "", r->getClassId(true)); } else if (type==GUI_INPUT) { InputInfo ii=*(InputInfo *)data; @@ -1473,7 +1500,7 @@ void TabRunner::showRunnerReport(gdioutput &gdi) for (size_t k = 0; k < runnersToReport.size(); k++) { pRunner r = oe->getRunner(runnersToReport[k].first, 0); if (r && r->getTeam()) { - pClass cls = oe->getClass(r->getClassId()); + pClass cls = r->getClassRef(true); if (cls && cls->getClassType() == oClassPatrol) continue; @@ -1511,7 +1538,7 @@ void TabRunner::showRunnerReport(gdioutput &gdi) tInfo += L" " + t->getStatusS(); } - gdi.addStringUT(fontMediumPlus, t->getClass()); + gdi.addStringUT(fontMediumPlus, t->getClass(true)); gdi.addStringUT(boldLarge, tInfo); gdi.dropLine(); @@ -1542,19 +1569,19 @@ void TabRunner::showRunnerReport(gdioutput &gdi) void TabRunner::runnerReport(gdioutput &gdi, int id, bool compact) { pRunner r = oe->getRunner(id, 0); - if (!r || ! r->getClassRef()) + if (!r || ! r->getClassRef(false)) return; gdi.pushX(); gdi.fillDown(); if (r->getTeam() == 0) { - gdi.addStringUT(fontMediumPlus, r->getClass()); + gdi.addStringUT(fontMediumPlus, r->getClass(true)); gdi.addStringUT(boldLarge, r->getCompleteIdentification()); } else { wstring s; if (r->getTeam()) - s += r->getClassRef()->getLegNumber(r->getLegNumber()); + s += r->getClassRef(false)->getLegNumber(r->getLegNumber()); s += L": " + r->getName(); gdi.addStringUT(boldText, s); @@ -1573,7 +1600,7 @@ void TabRunner::runnerReport(gdioutput &gdi, int id, bool compact) { if (r->statusOK()) { int total, finished, dns; - oe->getNumClassRunners(r->getClassId(), r->getLegNumber(), total, finished, dns); + oe->getNumClassRunners(r->getClassId(true), r->getLegNumber(), total, finished, dns); if (r->getTeam() == 0) { gdi.addString("", fontMediumPlus, L"Tid: X, nuvarande placering Y/Z.#" + str + L"#" + r->getPlaceS() + L"#" + itow(finished)); @@ -2005,7 +2032,7 @@ void TabRunner::listRunners(gdioutput &gdi, const vector &r, bool filte sprintf_s(bf, "%d.", k+1); gdi.addStringUT(yp, xp, 0, bf); gdi.addStringUT(yp, xp+40, 0, r[k]->getNameAndRace(true), 190); - gdi.addStringUT(yp, xp+200, 0, r[k]->getClass(), 140); + gdi.addStringUT(yp, xp+200, 0, r[k]->getClass(true), 140); gdi.addStringUT(yp, xp+350, 0, r[k]->getClub(), 190); int c = r[k]->getCardNo(); if (c>0) { @@ -2224,6 +2251,7 @@ int TabRunner::punchesCB(gdioutput &gdi, int type, void *data) card->synchronize(); r->synchronize(true); r->evaluateCard(true, mp); + r->hasManuallyUpdatedTimeStatus(); card->fillPunches(gdi, "Punches", pc); UpdateStatus(gdi, r); } @@ -2246,6 +2274,7 @@ int TabRunner::punchesCB(gdioutput &gdi, int type, void *data) r->synchronize(true); r->evaluateCard(true, mp); card->fillPunches(gdi, "Punches", r->getCourse(true)); + r->hasManuallyUpdatedTimeStatus(); UpdateStatus(gdi, r); } else if (bi.id=="SaveC"){ @@ -2274,6 +2303,7 @@ int TabRunner::punchesCB(gdioutput &gdi, int type, void *data) card->synchronize(); r->synchronize(); r->evaluateCard(true, mp); + r->hasManuallyUpdatedTimeStatus(); card->fillPunches(gdi, "Punches", r->getCourse(true)); UpdateStatus(gdi, r); gdi.selectItemByData("Punches", lbi.data); @@ -2380,14 +2410,17 @@ bool TabRunner::loadPage(gdioutput &gdi) } gdi.fillRight(); - gdi.addSelection("RClass", 130, 300, RunnerCB, L"Klass:"); + bool hasEco = oe->getMeOSFeatures().hasFeature(MeOSFeatures::Economy); + gdi.addSelection("RClass", hasEco ? 130 : 170, 300, RunnerCB, L"Klass:"); oe->fillClasses(gdi, "RClass", oEvent::extraNone, oEvent::filterNone); gdi.addItem("RClass", lang.tl("Ingen klass"), 0); - if (oe->getMeOSFeatures().hasFeature(MeOSFeatures::Economy)) { + if (hasEco) { + //gdi.addInput("Fee", L"", 4, 0, L"Avgift:"); gdi.fillDown(); - gdi.addInput("Fee", L"", 4, 0, L"Avgift:"); - //gdi.addButton("Economy", "@" + itos(131), RunnerCB, "Ekonomi..."); + gdi.dropLine(); + gdi.addButton("Economy", "Ekonomi...", RunnerCB, ""); + } else { gdi.fillDown(); @@ -2652,7 +2685,7 @@ void TabRunner::fillRunnerList(gdioutput &gdi) { } bool TabRunner::canSetStart(pRunner r) const { - pClass pc = r->getTeam() ? r->getTeam()->getClassRef() : r->getClassRef(); + pClass pc = r->getTeam() ? r->getTeam()->getClassRef(false) : r->getClassRef(true); if (pc && pc->getNumStages() > 0) { StartTypes st = pc->getStartType(r->getLegNumber()); @@ -2778,3 +2811,138 @@ void TabRunner::autoGrowCourse(gdioutput &gdi) { gdi.refresh(); } } + + +void TabRunner::EconomyHandler::init(oRunner &r) { + oe = r.getEvent(); + runnerId = r.getId(); +} + +oRunner &TabRunner::EconomyHandler::getRunner() const { + pRunner p = oe->getRunner(runnerId, 0); + if (!p) + throw meosException("Löpare saknas"); + return *p; +} + +TabRunner::EconomyHandler *TabRunner::getEconomyHandler(oRunner &r) { + if (!ecoHandler) + ecoHandler = make_shared(); + + ecoHandler->init(r); + return ecoHandler.get(); +} + +void TabRunner::EconomyHandler::handle(gdioutput &gdi, BaseInfo &info, GuiEventType type) { + if (type == GuiEventType::GUI_BUTTON) { + ButtonInfo &bi = dynamic_cast(info); + if (bi.id == "Close") { + save(gdi); + gdi.closeWindow(); + } + else if (bi.id == "Save") { + save(gdi); + } + else if (bi.id == "Cancel") { + TabRunner &dst = dynamic_cast(*gdi.getTabs().get(TabType::TRunnerTab)); + dst.loadEconomy(gdi, getRunner()); + } + } + else if (type == GuiEventType::GUI_INPUTCHANGE) { + InputInfo &ii = dynamic_cast(info); + if (ii.id == "Fee") { + gdi.check("ModFee", ii.changed() || getRunner().hasFlag(oAbstractRunner::FlagFeeSpecified)); + } + else if (ii.id == "PaidAmount") { + int paid = oe->interpretCurrency(ii.text); + gdi.setInputStatus("PayMode", paid != 0); + } + } +} + +void TabRunner::EconomyHandler::save(gdioutput &gdi) { + oRunner &r = getRunner(); + if (r.getTeam() == 0) { + r.getDI().setDate("EntryDate", gdi.getText("EntryDate")); + int t = convertAbsoluteTimeHMS(gdi.getText("EntryTime"), -1); + r.getDI().setInt("EntryTime", t); + } + int fee = oe->interpretCurrency(gdi.getText("Fee")); + if (r.getClassRef(true)) { + int def = r.getClassRef(true)->getEntryFee(r.getEntryDate(), r.getBirthAge()); + r.setFlag(oAbstractRunner::FlagFeeSpecified, def != fee); + } + r.getDI().setInt("Fee", fee); + int cf = oe->interpretCurrency(gdi.getText("Card")); + if (cf > 0 || cf == 0 && r.getDCI().getInt("CardFee") != -1) + r.getDI().setInt("CardFee", cf); + int paid = oe->interpretCurrency(gdi.getText("PaidAmount")); + r.getDI().setInt("Paid", paid); + + + if (paid != 0) { + int m = gdi.getSelectedItem("").first; + if (m != 1000) + r.getDI().setInt("PayMode", m); + } +} + +void TabRunner::loadEconomy(gdioutput &gdi, oRunner &r) { + gdi.clearPage(false); + gdi.fillDown(); + gdi.pushX(); + gdi.addString("", fontMediumPlus, L"Ekonomihantering, X#" + r.getCompleteIdentification()); + auto h = getEconomyHandler(r); + + gdi.fillRight(); + gdi.addInput("EntryDate", r.getEntryDate(true), 10, 0, L"Anmälningsdatum:"); + gdi.fillDown(); + gdi.addInput("EntryTime", formatTime(r.getDCI().getInt("EntryTime")), 10, 0, L"Anmälningstid:"); + gdi.setInputStatus("EntryDate", r.getTeam() == 0); + gdi.setInputStatus("EntryTime", r.getTeam() == 0); + + gdi.popX(); + gdi.dropLine(); + + gdi.fillRight(); + gdi.addInput("Fee", oe->formatCurrency(r.getDCI().getInt("Fee")), 5, 0, L"Avgift:").setHandler(h); + int cf = r.getDCI().getInt("CardFee"); + if (cf == -1) // Borrowed, zero fee + cf = 0; + gdi.addInput("Card", oe->formatCurrency(cf), 5, 0, L"Brickhyra:"); + int paid = r.getDCI().getInt("Paid"); + gdi.addInput("PaidAmount", oe->formatCurrency(paid), 5, 0, L"Betalat:").setHandler(h); + gdi.fillDown(); + gdi.dropLine(); + vector< pair > pm; + oe->getPayModes(pm); + int mypm = r.getDCI().getInt("PayMode"); + //pm.insert(pm.begin(), make_pair(lang.tl(L"Faktureras"), 1000)); + gdi.addSelection("PayMode", 110, 100, SportIdentCB); + gdi.addItem("PayMode", pm); + gdi.selectItemByData("PayMode", mypm); + gdi.autoGrow("PayMode"); + gdi.setInputStatus("PayMode", paid != 0); + + gdi.dropLine(); + gdi.popX(); + + gdi.addString("", 1, "Manuellt gjorda justeringar"); + gdi.fillRight(); + + gdi.addCheckbox("ModFee", "Avgift", 0, r.hasFlag(oAbstractRunner::FlagFeeSpecified)); + gdi.disableInput("ModFee"); + + gdi.addCheckbox("ModCls", "Klass", 0, r.hasFlag(oAbstractRunner::FlagUpdateClass)); + gdi.disableInput("ModCls"); + + gdi.fillDown(); + gdi.addCheckbox("ModCls", "Namn", 0, r.hasFlag(oAbstractRunner::FlagUpdateName)); + gdi.disableInput("ModCls"); + + gdi.fillRight(); + gdi.addButton("Cancel", "Ångra").setHandler(h); + gdi.addButton("Close", "Stäng").setHandler(h); + gdi.addButton("Save", "Spara").setHandler(h); + gdi.refresh(); +} diff --git a/code/TabRunner.h b/code/TabRunner.h index efeb67f..1588a4e 100644 --- a/code/TabRunner.h +++ b/code/TabRunner.h @@ -91,6 +91,21 @@ private: static void autoGrowCourse(gdioutput &gdi); + void loadEconomy(gdioutput &gdi, oRunner &r); + + class EconomyHandler : public GuiHandler { + int runnerId; + oEvent *oe; + oRunner &getRunner() const; + public: + void init(oRunner &r); + void handle(gdioutput &gdi, BaseInfo &info, GuiEventType type); + void save(gdioutput &gdi); + }; + + shared_ptr ecoHandler; + EconomyHandler *getEconomyHandler(oRunner &r); + protected: void clearCompetitionData(); @@ -104,6 +119,8 @@ public: bool loadPage(gdioutput &gdi); bool loadPage(gdioutput &gdi, int runnerId); + + TabRunner(oEvent *oe); ~TabRunner(void); diff --git a/code/TabSI.cpp b/code/TabSI.cpp index c0c8ced..564e3a6 100644 --- a/code/TabSI.cpp +++ b/code/TabSI.cpp @@ -818,7 +818,7 @@ int TabSI::siCB(gdioutput &gdi, int type, void *data) pRunner cardRunner = oe->getRunnerByCardNo(cardNo, 0, true); if (cardNo>0 && cardRunner!=0 && cardRunner!=r) { - gdi.alert(L"Bricknummret är upptaget (X).#" + cardRunner->getName() + L", " + cardRunner->getClass()); + gdi.alert(L"Bricknummret är upptaget (X).#" + cardRunner->getName() + L", " + cardRunner->getClass(true)); return 0; } @@ -857,7 +857,7 @@ int TabSI::siCB(gdioutput &gdi, int type, void *data) } lastClubId=r->getClubId(); - lastClassId=r->getClassId(); + lastClassId=r->getClassId(true); lastFee = gdi.getText("Fee", true); int lastFeeNum = oe->interpretCurrency(lastFee); @@ -890,10 +890,10 @@ int TabSI::siCB(gdioutput &gdi, int type, void *data) wchar_t bf[256]; if (r->getClubId() != 0) { swprintf_s(bf, L"(%d), %s, %s", r->getCardNo(), r->getClub().c_str(), - r->getClass().c_str()); + r->getClass(true).c_str()); } else { - swprintf_s(bf, L"(%d), %s", r->getCardNo(), r->getClass().c_str()); + swprintf_s(bf, L"(%d), %s", r->getCardNo(), r->getClass(true).c_str()); } wstring info(bf); @@ -1810,7 +1810,7 @@ void TabSI::insertSICardAux(gdioutput &gdi, SICard &sic) autoAssignClass(r, sic); if (interactiveReadout) { - if (r && r->getClassId() && !readBefore && !sameCardNewRace) { + if (r && r->getClassId(false) && !readBefore && !sameCardNewRace) { //We can do a silent read-out... processCard(gdi, r, sic, true); return; @@ -1823,7 +1823,7 @@ void TabSI::insertSICardAux(gdioutput &gdi, SICard &sic) } else { if (!readBefore) { - if (r && r->getClassId() && !sameCardNewRace) + if (r && r->getClassId(false) && !sameCardNewRace) processCard(gdi, r, sic, true); else processUnmatched(gdi, sic, true); @@ -1839,7 +1839,7 @@ void TabSI::insertSICardAux(gdioutput &gdi, SICard &sic) // Assign a class if not already done autoAssignClass(r, sic); - if (r && r->getClassId() && !readBefore && !sameCardNewRace) { + if (r && r->getClassId(false) && !readBefore && !sameCardNewRace) { //We can do a silent read-out... processCard(gdi, r, sic, true); return; @@ -1923,7 +1923,7 @@ void TabSI::insertSICardAux(gdioutput &gdi, SICard &sic) // Assign a class if not already done autoAssignClass(r, sic); - if (r && r->getClassId() && !r->getCard()) { + if (r && r->getClassId(false) && !r->getCard()) { SICard copy = sic; activeSIC.clear(0); processCard(gdi, r, copy); //Everyting is OK @@ -2148,11 +2148,11 @@ bool TabSI::processCard(gdioutput &gdi, pRunner runner, const SICard &csic, bool //Update from SQL-source runner->synchronize(); - if (!runner->getClassId()) + if (!runner->getClassId(false)) runner->setClassId(gEvent->addClass(lang.tl(L"Okänd klass"))->getId(), true); // Choose course from pool - pClass cls=gEvent->getClass(runner->getClassId()); + pClass cls = runner->getClassRef(false); if (cls && cls->hasCoursePool()) { unsigned leg=runner->legToRun(); @@ -2171,11 +2171,11 @@ bool TabSI::processCard(gdioutput &gdi, pRunner runner, const SICard &csic, bool } } - pClass pclass=gEvent->getClass(runner->getClassId()); + pClass pclass = runner->getClassRef(true); if (!runner->getCourse(false) && !csic.isManualInput()) { if (pclass && !pclass->hasMultiCourse() && !pclass->hasDirectResult()) { - pCourse pcourse=gEvent->addCourse(runner->getClass()); + pCourse pcourse=gEvent->addCourse(pclass->getName()); pclass->setCourse(pcourse); for(unsigned i=0;igetName() + L" (" + cardno + L"), " + runner->getClub() - + L", " + runner->getClass(); + info = runner->getName() + L" (" + cardno + L"), "; + if (!runner->getClub().empty()) + info += runner->getClub() + +L", "; + info += runner->getClass(true); // Write read card to log logCard(sic); @@ -2260,7 +2262,7 @@ bool TabSI::processCard(gdioutput &gdi, pRunner runner, const SICard &csic, bool } else { //Manual input - info = runner->getName() + L", " + runner->getClub() + L", " + runner->getClass(); + info = runner->getName() + L", " + runner->getClub() + L", " + runner->getClass(true); runner->setCard(0); if (csic.statusOK) { @@ -2297,7 +2299,10 @@ bool TabSI::processCard(gdioutput &gdi, pRunner runner, const SICard &csic, bool gEvent->calculateResults(oEvent::RTClassResult); if (runner->getTeam()) gEvent->calculateTeamResults(runner->getLegNumber(), false); - wstring placeS = runner->getTeam() ? runner->getTeam()->getLegPlaceS(runner->getLegNumber(), false) : runner->getPlaceS(); + bool qfClass = runner->getClassId(false) != runner->getClassId(true); + wstring placeS = (runner->getTeam() && !qfClass) ? + runner->getTeam()->getLegPlaceS(runner->getLegNumber(), false) : + runner->getPlaceS(); if (!silent) { gdi.fillDown(); @@ -2322,7 +2327,7 @@ bool TabSI::processCard(gdioutput &gdi, pRunner runner, const SICard &csic, bool } else { wstring msg = L"#" + runner->getName() + L" (" + cardno + L")\n"+ - runner->getClub() + L". " + runner->getClass() + + runner->getClub() + L". " + runner->getClass(true) + L"\n" + lang.tl("Tid: ") + runner->getRunningTimeS() + lang.tl(L", Plats ") + placeS; gdi.addInfoBox("SIINFO", msg, 10000); @@ -2359,7 +2364,7 @@ bool TabSI::processCard(gdioutput &gdi, pRunner runner, const SICard &csic, bool } else { wstring statusmsg = L"#" + runner->getName() + L" (" + cardno + L")\n"+ - runner->getClub() + L". "+ runner->getClass() + + runner->getClub() + L". "+ runner->getClass(true) + L"\n" + msg; gdi.addInfoBox("SIINFO", statusmsg, 10000); @@ -2594,7 +2599,7 @@ void TabSI::generateEntryLine(gdioutput &gdi, pRunner r) if (gdi.hasField("Club")) { gdi.selectItemByData("Club", r->getClubId()); } - gdi.selectItemByData("Class", r->getClassId()); + gdi.selectItemByData("Class", r->getClassId(true)); oDataConstInterface dci = r->getDCI(); if (gdi.hasField("Fee")) @@ -2729,7 +2734,7 @@ void TabSI::checkMoreCardsInQueue(gdioutput &gdi) { } bool TabSI::autoAssignClass(pRunner r, const SICard &sic) { - if (r && r->getClassId()==0) { + if (r && r->getClassId(false)==0) { vector classes; int dist = oe->findBestClass(sic, classes); @@ -2737,7 +2742,7 @@ bool TabSI::autoAssignClass(pRunner r, const SICard &sic) { r->setClassId(classes[0]->getId(), true); } - return r && r->getClassId() != 0; + return r && r->getClassId(false) != 0; } void TabSI::showManualInput(gdioutput &gdi) { diff --git a/code/TabSpeaker.cpp b/code/TabSpeaker.cpp index 33e39f0..781fa15 100644 --- a/code/TabSpeaker.cpp +++ b/code/TabSpeaker.cpp @@ -717,6 +717,10 @@ void TabSpeaker::generateControlList(gdioutput &gdi, int classId) vector stages; pc->getTrueStages(stages); + if (pc->getQualificationFinal()) { + while (stages.size() > 1) + stages.pop_back(); //Ignore for qualification race + } int leg = selectedControl[pc->getId()].getLeg(); const bool multiDay = oe->hasPrevStage(); @@ -1017,6 +1021,8 @@ bool TabSpeaker::loadPage(gdioutput &gdi) } } + gdi.setRestorePoint("classes"); + if (classId == -1) { string btn = "Events"; if (gdi.hasField(btn)) @@ -1028,8 +1034,6 @@ bool TabSpeaker::loadPage(gdioutput &gdi) gdi.sendCtrlMessage(btn); } - - gdi.setRestorePoint("classes"); gdi.refresh(); return true; } diff --git a/code/TabTeam.cpp b/code/TabTeam.cpp index dfc8aa7..d29a356 100644 --- a/code/TabTeam.cpp +++ b/code/TabTeam.cpp @@ -188,7 +188,7 @@ void TabTeam::selectTeam(gdioutput &gdi, pTeam t) gdi.enableInput("Remove"); oe->fillClasses(gdi, "RClass", oEvent::extraNone, oEvent::filterNone); - gdi.selectItemByData("RClass", t->getClassId()); + gdi.selectItemByData("RClass", t->getClassId(false)); gdi.selectItemByData("Teams", t->getId()); if (gdi.hasField("StatusIn")) { @@ -353,14 +353,14 @@ bool TabTeam::save(gdioutput &gdi, bool dontReloadTeams) { gdi.getSelectedItem("RClass", lbi); int classId = lbi.data; - bool newClass = t->getClassId() != classId; + bool newClass = t->getClassId(false) != classId; set classes; bool globalDep = false; - if (t->getClassRef()) - globalDep = t->getClassRef()->hasClassGlobalDependance(); + if (t->getClassRef(false)) + globalDep = t->getClassRef(false)->hasClassGlobalDependence(); classes.insert(classId); - classes.insert(t->getClassId()); + classes.insert(t->getClassId(false)); bool readStatusIn = true; if (newClass && t->getInputStatus() != StatusNotCompetiting && t->hasInputData()) { @@ -402,7 +402,7 @@ bool TabTeam::save(gdioutput &gdi, bool dontReloadTeams) { pClass pc=oe->getClass(classId); if (pc) { - globalDep |= pc->hasClassGlobalDependance(); + globalDep |= pc->hasClassGlobalDependence(); for (unsigned i=0;igetNumStages(); i++) { char bf[16]; @@ -428,7 +428,7 @@ bool TabTeam::save(gdioutput &gdi, bool dontReloadTeams) { // Same runner set if (oldId == r->getId()) { if (newName) { - r->updateFromDB(name, r->getClubId(), r->getClassId(), + r->updateFromDB(name, r->getClubId(), r->getClassId(false), cardNo, 0); r->setName(name, true); } @@ -447,12 +447,12 @@ bool TabTeam::save(gdioutput &gdi, bool dontReloadTeams) { if (!t->getClub().empty()) r->setClub(t->getClub()); r->resetPersonalData(); - r->updateFromDB(name, r->getClubId(), r->getClassId(), + r->updateFromDB(name, r->getClubId(), r->getClassId(false), cardNo, 0); } } else - r=oe->addRunner(name, t->getClubId(), t->getClassId(), cardNo, 0, false); + r=oe->addRunner(name, t->getClubId(), t->getClassId(false), cardNo, 0, false); r->setName(name, true); r->setCardNo(cardNo, true); @@ -594,7 +594,7 @@ int TabTeam::teamCB(gdioutput &gdi, int type, void *data) } else if (bi.id == "DirOK") { pTeam t = oe->getTeam(teamId); - if (!t || !t->getClassRef()) + if (!t || !t->getClassRef(false)) return 0; int leg = bi.getExtraInt(); @@ -632,7 +632,7 @@ int TabTeam::teamCB(gdioutput &gdi, int type, void *data) bool rent = gdi.isChecked("DirRent"); if (r == 0) { - r = oe->addRunner(name, clb ? clb->getId() : t->getClubId(), t->getClassId(), card, 0, false); + r = oe->addRunner(name, clb ? clb->getId() : t->getClubId(), t->getClassId(false), card, 0, false); } if (rent) r->getDI().setInt("CardFee", oe->getDI().getInt("CardFee")); @@ -671,10 +671,10 @@ int TabTeam::teamCB(gdioutput &gdi, int type, void *data) } else if (bi.id == "ChangeKey") { pTeam t = oe->getTeam(teamId); - if (!t || !t->getClassRef()) + if (!t || !t->getClassRef(false)) return 0; - pClass pc = t->getClassRef(); + pClass pc = t->getClassRef(false); gdi.restore("ChangeKey", false); gdi.fillRight(); gdi.pushX(); @@ -695,10 +695,10 @@ int TabTeam::teamCB(gdioutput &gdi, int type, void *data) } else if (bi.id == "SaveKey") { pTeam t = oe->getTeam(teamId); - if (!t || !t->getClassRef()) + if (!t || !t->getClassRef(false)) return 0; - pClass pc = t->getClassRef(); + pClass pc = t->getClassRef(false); int nf = pc->getNumForks(); ListBoxInfo lbi; gdi.getSelectedItem("ForkKey", lbi); @@ -752,7 +752,7 @@ int TabTeam::teamCB(gdioutput &gdi, int type, void *data) pTeam t = oe->getTeam(teamId); if (t == 0) return 0; - pClass pc = t->getClassRef(); + pClass pc = t->getClassRef(false); if (pc == 0) return 0; @@ -806,7 +806,7 @@ int TabTeam::teamCB(gdioutput &gdi, int type, void *data) continue; if (clubs.count(clsR[i]->getClubId()) == 0) continue; - if (clsR[i]->getClassId() != t->getClassId()) + if (clsR[i]->getClassId(false) != t->getClassId(false)) continue; if (clsR[i]->getName() == anon) continue; @@ -839,7 +839,7 @@ int TabTeam::teamCB(gdioutput &gdi, int type, void *data) if (usedR.count(clsR[i]->getId())) continue; const wstring &club = clsR[i]->getClub(); - wstring id = clsR[i]->getName() + L", " + clsR[i]->getClass(); + wstring id = clsR[i]->getName() + L", " + clsR[i]->getClass(false); if (!club.empty()) id += L" (" + club + L")"; @@ -999,7 +999,7 @@ int TabTeam::teamCB(gdioutput &gdi, int type, void *data) pc->getNumStages() == shownRunners) { // Keep team setup, i.e. do nothing } - else if (t && pc && (t->getClassId()==bi.data + else if (t && pc && (t->getClassId(false)==bi.data || t->getNumRunners()==pc->getNumStages()) ) loadTeamMembers(gdi, 0,0,t); else @@ -1134,7 +1134,7 @@ int TabTeam::teamCB(gdioutput &gdi, int type, void *data) void TabTeam::loadTeamMembers(gdioutput &gdi, int ClassId, int ClubId, pTeam t) { if (ClassId==0) - if (t) ClassId=t->getClassId(); + if (t) ClassId=t->getClassId(false); classId=ClassId; gdi.restore("",false); @@ -1696,7 +1696,7 @@ void TabTeam::doAddTeamMembers(gdioutput &gdi) { for (size_t k = 0; k < t.size(); k++) { pTeam mt = t[k]; - pClass cls = mt->getClassRef(); + pClass cls = mt->getClassRef(false); if (cls == 0) continue; bool ch = false; @@ -1845,7 +1845,7 @@ void TabTeam::switchRunners(pTeam t, int leg, pRunner r, pRunner oldR) { else if (oldR) { t->setRunner(leg, 0, false); t->synchronize(true); - oldR->setClassId(r->getClassId(), true); + oldR->setClassId(r->getClassId(false), true); oldR->evaluateCard(true, mp, 0, true); oldR->synchronize(true); } diff --git a/code/bmp00001.bmp b/code/bmp00001.bmp new file mode 100644 index 0000000..f66e3e3 Binary files /dev/null and b/code/bmp00001.bmp differ diff --git a/code/csvparser.cpp b/code/csvparser.cpp index 4522e6c..f6cb53f 100644 --- a/code/csvparser.cpp +++ b/code/csvparser.cpp @@ -352,7 +352,7 @@ bool csvparser::importOE_CSV(oEvent &event, const wstring &file) { if (pc) { pc->synchronize(); - if (pr->getClassId() == 0 || !pr->hasFlag(oAbstractRunner::FlagUpdateClass)) + if (pr->getClassId(false) == 0 || !pr->hasFlag(oAbstractRunner::FlagUpdateClass)) pr->setClassId(pc->getId(), false); } } @@ -397,8 +397,8 @@ bool csvparser::importOE_CSV(oEvent &event, const wstring &file) { course->synchronize(); } if (course) { - if (pr->getClassId() != 0) - event.getClass(pr->getClassId())->setCourse(course); + if (pr->getClassId(false) != 0) + event.getClass(pr->getClassId(false))->setCourse(course); else pr->setCourseId(course->getId()); } @@ -1253,23 +1253,71 @@ void csvparser::parse(const wstring &file, list< vector > &data) { fin.close(); }*/ + +void csvparser::parseUnicode(const wstring &file, list< vector > &data) { + fin.open(file, ifstream::in | ifstream::binary); + fin.seekg(0, ios_base::end); + size_t len = int(fin.tellg())-2; + if (len == 0) + return; + fin.seekg(2); // BOM + assert(len % 2 == 0); + vector bf(len / 2 + 1); + fin.read((char *)&bf[0], len); + vector rows; + int spp = 0; + for (size_t k = 0; k < len / 2; k++) { + if (bf[spp] == '\r') + spp++; + if (bf[k] == '\n') { + bf[k] = 0; + if (k > 0 && bf[k - 1] == '\r') + bf[k - 1] = 0; + wstring r = &bf[spp]; + spp = k + 1; + rows.push_back(r); + } + } + if (size_t(spp + 1) < len / 2) { + wstring r = &bf[spp]; + rows.push_back(r); + } + vector sp; + + for (size_t k = 0; k < rows.size(); k++) { + split(const_cast(rows[k].c_str()), sp); + + if (!sp.empty()) { + data.push_back(vector()); + data.back().resize(sp.size()); + for (size_t k = 0; k < sp.size(); k++) { + data.back()[k] = sp[k]; + } + } + } +} + void csvparser::parse(const wstring &file, list< vector > &data) { data.clear(); fin.open(file); - // const size_t bf_size = 8192; + + fin.seekg(0, ios_base::end); + auto len = fin.tellg(); + fin.seekg(0); + string rbf; if (!fin.good()) throw meosException("Failed to read file"); bool isUTF8 = false; - bool isUnicode = false; bool firstLine = true; vector sp; - wchar_t wbf[buff_pre_alloc]; + vector wbf_a; + wbf_a.resize(size_t(len)); + wchar_t *wbf = &wbf_a[0]; wstring w; - while(std::getline(fin, rbf)) { const char *bf = rbf.c_str(); if (firstLine) { @@ -1278,27 +1326,23 @@ void csvparser::parse(const wstring &file, list< vector > &data) { bf += 3; } else if (bf[0] == -1 && bf[1] == -2) { - isUnicode = true; - bf += 2; + fin.close(); + vector().swap(wbf_a); + parseUnicode(file, data); + return; } } - - if (isUnicode) { - int len = 0; - if (bf[len] == 0 && bf[len+1] != 0) - len++; - split((wchar_t*)&bf[len], sp); - } - else if (isUTF8) { + + if (isUTF8) { int len = strlen(bf); int wlen = MultiByteToWideChar(CP_UTF8, 0, bf, len, wbf, buff_pre_alloc); wbf[wlen] = 0; - split(wbf, sp); + split(&wbf[0], sp); } else { w = gdi_main->recodeToWide(bf); - wchar_t *wbf = const_cast(w.c_str()); - split(wbf, sp); + wchar_t *wbfL = const_cast(w.c_str()); + split(wbfL, sp); } firstLine = false; diff --git a/code/csvparser.h b/code/csvparser.h index 311a014..64f9b6a 100644 --- a/code/csvparser.h +++ b/code/csvparser.h @@ -99,6 +99,8 @@ protected: map siconfigmap; const wchar_t *getSIC(SIConfigFields sic, const vector &sp) const; + void parseUnicode(const wstring &file, list< vector > &data); + // Check and process a punch line static int selectPunchIndex(const wstring &competitionDate, const vector &sp, int &cardIndex, int &timeIndex, int &dateIndex, diff --git a/code/english.lng b/code/english.lng index ce2e830..62b92da 100644 --- a/code/english.lng +++ b/code/english.lng @@ -2284,3 +2284,17 @@ RunnerEntryTime = Competitor's entry time RunnerPaid = Paid amount RunnerPayMethod = Payment method EntryTime = Entry Time +Ekonomihantering, X = Economy status, X +Manuellt gjorda justeringar = Manually made adjustments +Antal förfrÃ¥gningar: X = Number of requests: X +Genomsnittlig svarstid: X ms = Average response time: X ms +Informationsserver = Information server +Längsta svarstid: X ms = Longest response time: X ms +MeOS Informationsserver REST-API = MeOS Information Server REST-API +Testa servern = Test the server +help:rest = MeOS REST API lets you access competition data via a webb connection. You can show result lists directly in a webb browser, but you can also request competition data and results in a XML format, suitble for further processing in third party programs and apps. +Server startad pÃ¥ X = Server running on port X +Inconsistent qualification rule, X = Inconsistent qualification rule, X +help:LockStartList = MeOS will not update assignement to a locked class even if qualification results are altered. +Kval-Final-Schema = Load qualification scheme +LÃ¥s startlista = Lock start list diff --git a/code/gdioutput.cpp b/code/gdioutput.cpp index 6888459..ac90156 100644 --- a/code/gdioutput.cpp +++ b/code/gdioutput.cpp @@ -4215,7 +4215,7 @@ void gdioutput::calcStringSize(TextInfo &ti, HDC hDC_in) const HDC hDC=hDC_in; if (!hDC) { - assert(hWndTarget!=0); +// assert(hWndTarget!=0); hDC=GetDC(hWndTarget); } RECT rc; diff --git a/code/generalresult.cpp b/code/generalresult.cpp index 2cf22b2..acaa79d 100644 --- a/code/generalresult.cpp +++ b/code/generalresult.cpp @@ -78,6 +78,7 @@ GeneralResult::GeneralResult(void) { GeneralResult::~GeneralResult(void) { } +// Needed to generate template instances void GRINSTANCE() { vector a; vector b; @@ -86,6 +87,7 @@ void GRINSTANCE() { gr.sort(b, SortByFinishTime); } + void GeneralResult::setContext(const oListParam *contextIn) { context = contextIn; } @@ -230,8 +232,10 @@ template void GeneralResult::sort(vector &rt, SortOrder so) const const int maxT = 3600 * 100; for(size_t k = 0; k < rt.size(); k++) { arr[k].first = 0; - if (ps == ClassWise) - arr[k].first = rt[k]->getClassRef() ? rt[k]->getClassRef()->getSortIndex() : 0; + if (ps == ClassWise) { + pClass sclass = rt[k]->getClassRef(true); + arr[k].first = sclass ? sclass->getSortIndex() : 0; + } else if (ps == CourseWise) { oRunner *r = dynamic_cast(rt[k]); arr[k].first = r && r->getCourse(false) ? r->getCourse(false)->getId() : 0; @@ -266,7 +270,10 @@ template void GeneralResult::sort(vector &rt, SortOrder so) const } } -void GeneralResult::calculateIndividualResults(vector &runners, oListInfo::ResultType resType, bool sortRunners, int inputNumber) const { +void GeneralResult::calculateIndividualResults(vector &runners, + oListInfo::ResultType resType, + bool sortRunners, + int inputNumber) const { if (runners.empty()) return; @@ -286,7 +293,7 @@ void GeneralResult::calculateIndividualResults(vector &runners, oList int ln = r->getLegNumber(); const oTeam *pt = r->getTeam(); if (pt) { - const oClass *tcls = pt->getClassRef(); + const oClass *tcls = pt->getClassRef(false); if (tcls && tcls->getClassType() == oClassRelay) { int dummy; tcls->splitLegNumberParallel(r->getLegNumber(), ln, dummy); @@ -294,6 +301,10 @@ void GeneralResult::calculateIndividualResults(vector &runners, oList } runnerScore[k].principalSort = runnerScore[k].principalSort * 50 + ln; } + else if (resType == oListInfo::Coursewise) { + pCourse crs = r->getCourse(false); + runnerScore[k].principalSort = crs ? crs->getId() : 0; + } else runnerScore[k].principalSort = 0; @@ -474,10 +485,10 @@ RunnerStatus TotalResultAtControl::deduceStatus(oRunner &runner) const { if (runner.getTeam() && getListParamTimeFromControl() <= 0) { // Only use input time when start time is used const pTeam t = runner.getTeam(); - if (runner.getLegNumber()>0 && t->getClassRef()) { + if (runner.getLegNumber()>0 && t->getClassRef(false)) { // Find base leg int legIx = runner.getLegNumber(); - const pClass cls = t->getClassRef(); + const pClass cls = t->getClassRef(false); while (legIx > 0 && (cls->isParallel(legIx) || cls->isOptional(legIx))) legIx--; if (legIx > 0) @@ -504,10 +515,10 @@ int TotalResultAtControl::deduceTime(oRunner &runner, int startTime) const { if (runner.getTeam() && getListParamTimeFromControl() <= 0) { // Only use input time when start time is used const pTeam t = runner.getTeam(); - if (runner.getLegNumber()>0 && t->getClassRef()) { + if (runner.getLegNumber()>0 && t->getClassRef(false)) { // Find base leg int legIx = runner.getLegNumber(); - const pClass cls = t->getClassRef(); + const pClass cls = t->getClassRef(false); while (legIx > 0 && (cls->isParallel(legIx) || cls->isOptional(legIx))) legIx--; if (legIx > 0) @@ -1125,11 +1136,10 @@ void DynamicResult::prepareCalculations(oTeam &team) const { parser.addSymbol("RunnerCourse", team.getResultCache(oTeam::RCCCourse)); parser.addSymbol("RunnerSplitTimes", team.getResultCache(oTeam::RCCSplitTime)); - pClass cls = team.getClassRef(); + pClass cls = team.getClassRef(true); if (cls) { int nl = max(1, cls->getNumStages()-1); parser.addSymbol("ShortestClassTime", cls->getTotalLegLeaderTime(nl, false)); - } } @@ -1237,7 +1247,7 @@ void DynamicResult::prepareCalculations(oRunner &runner) const { parser.addSymbol("SplitTimesAccumulated", e); } - pClass cls = runner.getClassRef(); + pClass cls = runner.getClassRef(true); if (cls) { int nl = runner.getLegNumber(); parser.addSymbol("ShortestClassTime", cls->getBestLegTime(nl)); @@ -1303,3 +1313,294 @@ void DynamicResult::debugDumpVariables(gdioutput &gdi, bool includeSymbols) cons gdi.dropLine(); } } + +void GeneralResult::calculateIndividualResults(vector &runners, + const pair & controlId, + bool totalResults, + const string &resTag, + oListInfo::ResultType resType, + int inputNumber, + oEvent &oe, + vector &results) { + + results.reserve(runners.size()); + + if (resTag.empty() && resType == ClassWise) { + GeneralResultInfo ri; + if (controlId.second == oPunch::PunchFinish && + controlId.first == oPunch::PunchStart) { + + if (!totalResults) { + oe.calculateResults(oEvent::RTClassResult, true); + for (pRunner r : runners) { + ri.status = r->getStatus(); + if (ri.status == StatusUnknown) { + if (r->getFinishTime() == 0) + continue; + ri.status = StatusOK; // Preliminary status + } + if (ri.status == StatusOK) + ri.place = r->getPlace(); + else + ri.place = 0; + + ri.score = r->getRogainingPoints(false); + ri.time = r->getRunningTime(); + ri.src = r; + results.push_back(ri); + } + } + else { + oe.calculateResults(oEvent::RTTotalResult, true); + for (pRunner r : runners) { + ri.status = r->getTotalStatus(); + if (ri.status == StatusUnknown && r->getInputStatus() == StatusOK) { + if (r->getFinishTime() == 0) + continue; + ri.status = StatusOK; // Preliminary status + } + if (ri.status == StatusOK) + ri.place = r->getTotalPlace(); + else + ri.place = 0; + + ri.score = r->getRogainingPoints(true); + ri.time = r->getTotalRunningTime(); + ri.src = r; + results.push_back(ri); + } + } + } + else { + oe.calculateSplitResults(controlId.first, controlId.second); + + ri.score = 0; // Undefined + for (pRunner r : runners) { + ri.status = r->getTempStatus(); + if (ri.status == StatusUnknown) { + continue; + } + if (ri.status == StatusMP && r->getStatus() != StatusMP) + continue; // Control that is not needed requested + + if (ri.status == StatusOK) + ri.place = r->getPlace(); + else + ri.place = 0; + + ri.time = r->getTempTime(); + + if (ri.time <= 0 && ri.status == StatusOK) + continue; + ri.src = r; + results.push_back(ri); + } + } + } + else { + wstring srcDMY; + GeneralResult *gResult = 0; + shared_ptr specialInstance; + oListParam param; + param.useControlIdResultTo = controlId.second; + param.useControlIdResultFrom = controlId.first; + + if (!resTag.empty()) + gResult = &oe.getGeneralResult(resTag, srcDMY); + else { + if (controlId.second == oPunch::PunchFinish && + controlId.first == oPunch::PunchStart && !totalResults) + specialInstance = make_shared(); + else if (!totalResults) + specialInstance = make_shared(); + else + specialInstance = make_shared(); + + gResult = specialInstance.get(); + } + gResult->setContext(¶m); + gResult->calculateIndividualResults(runners, resType, true, inputNumber); + gResult->clearContext(); + map mapHasCourse; + GeneralResultInfo ri; + for (pRunner r : runners) { + const auto &tmp = r->getTempResult(0); + ri.status = tmp.getStatus(); + if (ri.status == StatusUnknown) { + if (r->getFinishTime() == 0) + continue; + ri.status = StatusOK; // Preliminary status + } + if (ri.status == StatusOK) + ri.place = tmp.getPlace(); + else + ri.place = 0; + + if ((controlId.first != oPunch::PunchStart || + controlId.second != oPunch::PunchFinish) && ri.status == StatusMP && r->getStatus() != StatusMP) { + continue; + } + + ri.score = tmp.getPoints(); + ri.time = tmp.getRunningTime(); + + + if (controlId.first != oPunch::PunchStart && ri.time <= 0 && ri.status == StatusOK) + continue; // Wrong order, skip + + ri.src = r; + results.push_back(ri); + } + } +} + +shared_ptr + GeneralResult::calculateTeamResults(vector &teams, + int leg, + const pair &controlId, + bool totalResults, + const string &resTag, + oListInfo::ResultType resType, + int inputNumber, + oEvent &oe, + vector &results) { + shared_ptr out = make_shared(); + out->leg = leg; + out->controlId = controlId; + out->totalResults = totalResults; + results.reserve(teams.size()); + + if (resTag.empty()) { + out->useModule = false; + if (controlId.second == oPunch::PunchFinish) { + oe.calculateTeamResults(false); + GeneralResultInfo ri; + for (pTeam r : teams) { + ri.status = r->getStatus(); + if (ri.status == StatusUnknown) { + if (r->getFinishTime() == 0) + continue; + ri.status = StatusOK; // Preliminary status + } + if (ri.status == StatusOK) + ri.place = r->getPlace(); + else + ri.place = 0; + + ri.score = r->getRogainingPoints(false); + ri.status = r->getStatus(); + ri.time = r->getRunningTime(); + ri.src = r; + results.push_back(ri); + } + } + else { + set clsId; + for (pTeam t : teams) + clsId.insert(t->getClassId(false)); + + oe.calculateTeamResultAtControl(clsId, leg, controlId.second, totalResults); + GeneralResultInfo ri; + for (pTeam r : teams) { + const auto &tmp = r->getTempResult(0); + ri.status = tmp.getStatus(); + if (ri.status == StatusUnknown || ri.status == StatusMP) + continue; + ri.place = tmp.getPlace(); + ri.score = tmp.getPoints(); + ri.time = tmp.getRunningTime(); + ri.src = r; + results.push_back(ri); + } + } + } + else { + out->useModule = true; + wstring srcDMY; + auto &gResult = oe.getGeneralResult(resTag, srcDMY); + gResult.calculateTeamResults(teams, resType, true, inputNumber); + GeneralResultInfo ri; + for (pTeam r : teams) { + const auto &tmp = r->getTempResult(0); + ri.status = tmp.getStatus(); + if (ri.status == StatusUnknown) { + if (r->getFinishTime() == 0) + continue; + ri.status = StatusOK; // Preliminary status + } + if (ri.status == StatusOK) + ri.place = tmp.getPlace(); + else + ri.place = 0; + + ri.score = tmp.getPoints(); + ri.time = tmp.getRunningTime(); + ri.src = r; + results.push_back(ri); + } + } + + return out; +} + +int GeneralResult::GeneralResultInfo::getNumSubresult(const BaseResultContext &context) const { + int cid = src->getClassId(false); + if (cid == 0) + return 0; + + if (context.resIntervalCache.count(cid)) { + const auto &cached = context.resIntervalCache[cid]; + return cached.second - cached.first; + } + else { + const pClass cls = src->getClassRef(false); + int leg = context.leg == -1 ? cls->getNumStages() : context.leg; + + int nb = cls->getNextBaseLeg(leg); + if (nb == -1) + nb = cls->getNumStages(); + + int pb = cls->getPreceedingLeg(leg)+1; + + context.resIntervalCache[cid] = make_pair(pb, nb); + return nb - pb; + } +} + +bool GeneralResult::GeneralResultInfo::getSubResult(const BaseResultContext &context, + int ix, GeneralResultInfo &out) const { + + if (getNumSubresult(context) == 0) + return false; // Ensure cache is setup + int base = context.resIntervalCache[src->getClassId(false)].first; + pRunner r = pTeam(src)->getRunner(base + ix); + if (r == 0) + return false; + out.place = 0; // Not defined + out.src = r; + + if (context.useModule) { + out.score = r->tmpResult.getPoints(); + out.status = r->tmpResult.getStatus(); + out.time = r->tmpResult.getRunningTime(); + } + else { + if (context.controlId.second == oPunch::PunchFinish) { + out.score = r->getRogainingPoints(false); + out.status = r->getStatus(); + out.time = r->getRunningTime(); + } + else { + out.score = 0; // Undefined + r->getSplitTime(context.controlId.second, out.status, out.time); + } + } + + if (out.status == StatusUnknown && out.time > 0) + out.status = StatusOK; + + if (out.status == StatusUnknown) + return false; + + return true; +} diff --git a/code/generalresult.h b/code/generalresult.h index a32d01e..8172e06 100644 --- a/code/generalresult.h +++ b/code/generalresult.h @@ -42,8 +42,6 @@ protected: enum PrincipalSort {None, ClassWise, CourseWise}; - virtual PrincipalSort getPrincipalSort() const {return ClassWise;} - virtual int score(oTeam &team, RunnerStatus st, int time, int points) const; virtual RunnerStatus deduceStatus(oTeam &team) const; virtual int deduceTime(oTeam &team) const; @@ -64,6 +62,82 @@ protected: public: + struct BaseResultContext { + private: + int leg; + bool useModule; + pair controlId; // Start - finish + bool totalResults; + mutable map > resIntervalCache; + + friend class GeneralResult; + }; + + struct GeneralResultInfo { + oAbstractRunner *src; + int time; + RunnerStatus status; + int score; + int place; + + int getNumSubresult(const BaseResultContext &context) const; + bool getSubResult(const BaseResultContext &context, int ix, GeneralResultInfo &out) const; + + inline bool compareResult(const GeneralResultInfo &o) const { + if (status != o.status) + return RunnerStatusOrderMap[status] < RunnerStatusOrderMap[o.status]; + + if (place != o.place) + return place < o.place; + + const wstring &name = src->getName(); + const wstring &oname = o.src->getName(); + + return CompareString(LOCALE_USER_DEFAULT, 0, + name.c_str(), name.length(), + oname.c_str(), oname.length()) == CSTR_LESS_THAN; + } + + bool operator<(const GeneralResultInfo &o) const { + pClass cls = src->getClassRef(true); + pClass ocls = o.src->getClassRef(true); + + if (cls != ocls) { + int so = cls ? cls->getSortIndex() : 0; + int oso = ocls ? ocls->getSortIndex() : 0; + if (so != oso) + return so < oso; + + // Use id as fallback + so = cls ? cls->getId() : 0; + oso = ocls ? ocls->getId() : 0; + if (so != oso) + return so < oso; + } + return compareResult(o); + } + }; + + + static void calculateIndividualResults(vector &runners, + const pair &controlId, + bool totalResults, + const string &resTag, + oListInfo::ResultType resType, + int inputNumber, + oEvent &oe, + vector &results); + + static shared_ptr calculateTeamResults(vector &teams, + int leg, + const pair &controlId, + bool totalResults, + const string &resTag, + oListInfo::ResultType resType, + int inputNumber, + oEvent &oe, + vector &results); + void setContext(const oListParam *context); void clearContext(); diff --git a/code/html1.htm b/code/html1.htm new file mode 100644 index 0000000..f820281 --- /dev/null +++ b/code/html1.htm @@ -0,0 +1,300 @@ +

Documentation of MeOS REST API

+ +

Competition

+Syntax: +
/meos?get=competition
+ +Returns: +

MOP Competition XML

+ +Example: +
+*MOPComplete>
+ *competition date="2015-09-06" organizer="Orienteringsklubben Linné" homepage="http://www.oklinne.nu">Stafett-DM, Uppland*/competition>
+*/MOPComplete>
+
+ +

Classes

+Syntax: +
/meos?get=class
+ +Returns: +

MOP Classes XML

+ +Example: +
+*MOPComplete>
+ *cls id="1" ord="10" radio="90,130;90,130;90,130">D21*/cls>
+ *cls id="100" ord="1000" radio="90,130">M7*/cls>
+*/MOPComplete>
+
+ +Remarks: The attribute ord should be used to sort classes. The attribute radio +lists the MeOS default radio control for each class and leg. +Each leg of the class is separated by ';' and each radio control within a leg with ','. +Note that you may query for results at any control; the listed +controls are only for convenience. + +

Controls

+Syntax: +
/meos?get=control
+ +Returns: +

MOP Controls XML

+ +Example: +
+*MOPComplete>
+  *control id="31">[31]*/control>
+  *control id="32">[32]*/control>
+  *control id="50">Radio 1*/control>
+*/MOPComplete>
+
+ +

Competitors

+Syntax: +
/meos?get=competitor
+
/meos?get=competitor#class=*c1>,*c2>,...
+ +Arguments: +
  • class A list of one or more class id:s, separated by comma. The default is all classes. +
+ +Returns: +

MOP Competitors XML

+ +Example: +
+*MOPComplete>
+  *cmp id="3565">
+    *base org="570" cls="5" stat="1" st="360000" rt="20580">Elsa Winter*/base>
+    *input it="20340" tstat="1"/>
+  */cmp>
+  *cmp id="55851">
+    *base org="134" cls="7" stat="1" st="380710" rt="46910">Anna Spring*/base>
+  */cmp>
+*/MOPComplete>
+
+ +Remarks: +Please refer to the MOP documentation for a complete description of all attributes. Below is a quick reference. +
    +
  • stat Status code. 1 is OK, 0 is unknown. Higher codes are used for disqualifications.
  • +
  • st Start time. In 1/10 seconds after 00:00:00.
  • +
  • rt Running time. In 1/10 seconds.
  • +
  • input/it Input running time accumulated from earlier races. In 1/10 seconds.
  • +
  • input/st Input status from earlier races.
  • +
+ +

Teams

+Syntax: +
/meos?get=team
+
/meos?get=team#class=*c1>,*c2>,...
+ +Arguments: +
  • class A list of one or more class id:s, separated by comma. The default is all classes. +
+ +Returns: +

MOP Teams XML

+ +Example: +
+*MOPComplete>
+  *tm id="2928771">
+    *base org="396" cls="5" stat="1" st="360000" rt="65770">IF Thor 1*/base>
+    *r>34346;48866,866,0;32609*/r>
+  */tm>
+  *tm id="2928346">
+    *base org="134" cls="5" stat="1" st="360000" rt="99660">OK Enen 1*/base>
+    *r>42020;55851;35732*/r>
+  */tm>
+*/MOPComplete>
+
+ +Remarks: +Please refer to the MOP documentation for a complete description of all attributes. Below is a quick reference. +
    +
  • stat Status code. 1 is OK, 0 is unknown. Higher codes are used for disqualifications.
  • +
  • st Start time. In 1/10 seconds after 00:00:00.
  • +
  • rt Running time (At finish, last leg). In 1/10 seconds.
  • +
  • r List of runners in the team. A ';' is used to separate the legs, a ',' within a legs + are used to list paralell runners. A '0' indicates a vacant leg, which may or may + not be valid for the team, depending on the rules.
  • +
+ +

Organizations and Clubs

+Syntax: +
/meos?get=organization
+ +Returns: +

MOP Organization XML

+ +Example: +
+*MOPComplete>
+  *org id="1012">BAOC*/org>
+  *org id="598">Dalaportens OL*/org>
+  *org id="133">Enebybergs IF*/org>
+  *org id="140">Falkenbergs OK*/org>
+*/MOPComplete>
+
+ +

Results

+Syntax: +
/meos?get=result
+
/meos?get=result#class=*c1>,*c2>,...
+
/meos?get=result#to=*c1>
+
/meos?get=result#from=*c1>
+
/meos?get=result#leg=*leg>
+
/meos?get=result#module=*module>
+
/meos?get=result#module=*module>#argument=*arg>
+
/meos?get=result#limit=*limit>
+
/meos?get=result#total=*true/false>
+
/meos?get=result#type=*type>
+ +Arguments: +
    +
  • class A list of one or more class id:s, separated by comma. The default is all classes.
  • +
  • from Returns the result measuring time from a specific control. If a specified class + does not visit the control, an empty result set is returned; no error is given. + The default is the start.
  • +
  • to Returns the result at a specific control. If a specified class does not visit the control, + an empty result set is returned; no error is given. The default is the finish.
  • + +
  • leg In a team competition, returns result for a specified leg. If the leg is parallel, + the result is for the completed parallel leg. The leg number refer to the flat view in + MeOS class settings, i.e., for a setup "1, 2a, 2b, 2c, 3", correspons to flat leg numbers, + "1, 2, 3, 4, 5". The default is the last leg.
  • +
  • module The result module tag to use for the result calculation. + The support of the leg and control options depends on result module. The default is no module.
  • +
  • argument A numeric argument, defined and used by the result module.
  • + +
  • limit Limit the number of results per class. If there is a tie, the number of returned results + may be higher than the requested. The default is no limit.
  • +
  • total Set to true if you want to calculate total results, i.e., results including earliers stages.
  • + +
  • type Use one of ClassIndividual (default for individual classes), CourseIndividual, GlobalIndividual, or + LegIndividual to calculate individual results. CourseIndividual calculates result + per course, ignoring class. GlobalIndividual calculates results without + considering classes, LegIndividual calculates results considering classes and legs. + Use one of ClassTeam (default for team classes) or GlobalTeam to calculate team + results. GlobalTeam calculates team results without considering classes.
  • +
+ + +Returns: +

MOP Individual or Team Result XML. The result list is sorted according to the result. Note than disqualified + competitors are also included; for example a competitor with status *DNS> will be returned with this status for + all radio controls. You may want to filter out such entries, depending on your application. +

+ + +Examples: + +

Individual results at the finish.

+
+*MOPComplete>
+  *results location="Finish">
+    *person cls="100" stat="1" st="387240" rt="21880" place="1">
+      *name id="134940">Isac Ulvesson*/name>
+      *org id="2184">BOIC*/org>
+    */person>
+  
+    *person cls="100" stat="1" st="387700" rt="22720" place="2">
+      *name id="134923">Anne Brahe*/name>
+      *org id="2253">IFA*/org>
+    */person>
+  
+    *person cls="100" stat="1" st="382570" rt="23260" place="3">
+      *name id="134914">Dan Andersson*/name>
+      *org id="5938">OK Kompassen*/org>
+    */person>
+  */results>
+*/MOPComplete>
+
+ +

Team results, leg 2 at Radio 1, leg has three parallel runners.

+
+*MOPComplete>
+  *results leg="2" location="Radio 1">
+    *team cls="6" stat="1" st="372000" rt="32030" place="1">
+      *name id="2927509">OK Linné 1*/name>
+      *org id="84">OK Linné*/org>
+      *person cls="6" leg="2" stat="1" st="392720" rt="11310">
+        *name id="52054">John Woods*/name>
+        *org id="84">OK Linné*/club>
+      */person>
+  
+      *person cls="6" leg="3" stat="3" st="392720">
+        *name id="134850">Ralph Audoro*/name>
+        *org id="84">OK Linné*/org>
+      */person>
+  
+      *person cls="6" leg="4" stat="1" st="392720" rt="12850">
+        *name id="134851">Niel Success*/name>
+        *club id="84">OK Linné*/club>
+      */person>
+  */team>
+  */results>
+*/MOPComplete>
+
+ +

Roganing results, Invoke with argument module=rogaining, which is a built in module.

+
+*MOPComplete>
+  *results module="rogaining" location="Finish">
+    *person cls="1" stat="1" score="26" st="324000" rt="72550" place="1">
+      *name id="4">Omar Rue*/name>
+    */person>
+    
+    *person cls="1" stat="1" score="25" st="324000" rt="72730" place="2">
+      *name id="3">Oscar Wilde*/name>
+    */person>
+  
+    *person cls="1" stat="1" score="25" st="324000" rt="72780" place="3">
+      *name id="5">Alice Alison*/name>
+    */person>
+    
+    *person cls="1" stat="5" score="26" st="324000" rt="72590">
+      *name id="6">Reimund Rossinger*/name>
+    */person>
+  */results>
+*/MOPComplete>
+
+ +Remarks: +When and only when the type is CourseIndividual, the attribute course/i> is included. +
+*person cls="1" stat="1" st="324000" rt="72550" place="1" course=28>
+  *name id="4">Jordan Griesmer*/name>
+*/person>
+
+ +

IOF XML Results

+ +Syntax: +
/meos?get=iofresult
+
/meos?get=iofresult#class=*c1>,*c2>,...
+ +Arguments: +
    +
  • class A list of one or more class id:s, separated by comma. The default is all classes.
  • +
+ +Returns: +

IOF XML version 3.0 result list.

+ +

IOF XML Startlist

+ +Syntax: +
/meos?get=iofstart
+
/meos?get=iofstart#class=*c1>,*c2>,...
+ +Arguments: +
    +
  • class A list of one or more class id:s, separated by comma. The default is all classes.
  • +
+ +Returns: +

IOF XML version 3.0 start list.

diff --git a/code/infoserver.cpp b/code/infoserver.cpp index a449dd1..7bf29e3 100644 --- a/code/infoserver.cpp +++ b/code/infoserver.cpp @@ -203,7 +203,7 @@ bool InfoCompetition::synchronize(oEvent &oe, bool onlyCmp, const set &incl vector t; oe.getTeams(0, t, false); for (size_t k = 0; k < t.size(); k++) { - if (!includeCls.count(t[k]->getClassId())) + if (!includeCls.count(t[k]->getClassId(true))) continue; int wid = t[k]->getId(); knownId.insert(wid); @@ -228,7 +228,7 @@ bool InfoCompetition::synchronize(oEvent &oe, bool onlyCmp, const set &incl vector r; oe.getRunners(0, 0, r, false); for (size_t k = 0; k < r.size(); k++) { - if (!includeCls.count(r[k]->getClassId())) + if (!includeCls.count(r[k]->getClassId(true))) continue; int wid = r[k]->getId(); knownId.insert(wid); @@ -397,7 +397,7 @@ bool InfoBaseCompetitor::synchronizeBase(oAbstractRunner &bc) { ch = true; } - int cls = bc.getClassId(); + int cls = bc.getClassId(true); if (cls != classId) { classId = cls; ch = true; @@ -463,8 +463,8 @@ bool InfoCompetitor::synchronize(const InfoCompetition &cmp, oRunner &r) { bool ch = synchronize(useTotalResults, r); vector newRT; - if (r.getClassId() > 0) { - const vector &radios = cmp.getControls(r.getClassId(), r.getLegNumber()); + if (r.getClassId(false) > 0) { + const vector &radios = cmp.getControls(r.getClassId(false), r.getLegNumber()); for (size_t k = 0; k < radios.size(); k++) { RadioTime radioTime; RunnerStatus s_split; @@ -520,7 +520,7 @@ void InfoCompetitor::serialize(xmlbuffer &xml, bool diffOnly) const { bool InfoTeam::synchronize(oTeam &t) { bool ch = synchronizeBase(t); - const pClass cls = t.getClassRef(); + const pClass cls = t.getClassRef(true); if (cls) { vector< vector > r; diff --git a/code/infoserver.h b/code/infoserver.h index 6084e63..244a3b9 100644 --- a/code/infoserver.h +++ b/code/infoserver.h @@ -127,12 +127,13 @@ class InfoClass : public InfoBase { class InfoOrganization : public InfoBase { protected: wstring name; - bool synchronize(oClub &c); - void serialize(xmlbuffer &xml, bool diffOnly) const; public: InfoOrganization(int id); virtual ~InfoOrganization() {} + bool synchronize(oClub &c); + void serialize(xmlbuffer &xml, bool diffOnly) const; + friend class InfoCompetition; }; @@ -183,9 +184,10 @@ class InfoTeam : public InfoBaseCompetitor { protected: // The outer level holds legs, the inner level holds (parallel/patrol) runners on each leg. vector< vector > competitors; + public: bool synchronize(oTeam &t); void serialize(xmlbuffer &xml, bool diffOnly) const; - public: + InfoTeam(int id); virtual ~InfoTeam() {} friend class InfoCompetition; diff --git a/code/iof30interface.cpp b/code/iof30interface.cpp index 6441b28..176b642 100644 --- a/code/iof30interface.cpp +++ b/code/iof30interface.cpp @@ -335,7 +335,7 @@ void IOF30Interface::personCourseAssignment(gdioutput &gdi, xmlList &xAssignment xPAssignment.getObjectString("ClassName", cls); multimap::const_iterator res = name2Runner.find(person); while (res != name2Runner.end() && person == res->first) { - if (cls.empty() || res->second->getClass() == cls) { + if (cls.empty() || res->second->getClass(false) == cls) { r = res->second; break; } @@ -386,7 +386,7 @@ void IOF30Interface::teamCourseAssignment(gdioutput &gdi, xmlList &xAssignment, if (!bib.empty()) bib2Team[bib] = allT[k]; - nameClass2Team[make_pair(allT[k]->getName(), allT[k]->getClass())] = allT[k]; + nameClass2Team[make_pair(allT[k]->getName(), allT[k]->getClass(false))] = allT[k]; } for (size_t k = 0; k < xAssignment.size(); k++) { @@ -426,7 +426,7 @@ void IOF30Interface::teamCourseAssignment(gdioutput &gdi, xmlList &xAssignment, void IOF30Interface::assignTeamCourse(gdioutput &gdi, oTeam &team, xmlList &xAssignment, const map &courses) { - if (!team.getClassRef()) + if (!team.getClassRef(false)) return; for (size_t k = 0; k getLegNumberLinear(leg, legorder); + int legId = team.getClassRef(false)->getLegNumberLinear(leg, legorder); if (legId>=0) { pRunner r = team.getRunner(legId); if (r == 0) { - r = oe.addRunner(lang.tl(L"N.N."), team.getClubId(), team.getClassId(), 0, 0, false); + r = oe.addRunner(lang.tl(L"N.N."), team.getClubId(), team.getClassId(false), 0, 0, false); if (r) { r->setEntrySource(entrySourceId); r->flagEntryTouched(true); @@ -460,7 +460,7 @@ void IOF30Interface::assignTeamCourse(gdioutput &gdi, oTeam &team, xmlList &xAss } } else - gdi.addString("", 0, L"Bantilldelning för 'X' hänvisar till en sträcka som inte finns#" + team.getClass()).setColor(colorRed); + gdi.addString("", 0, L"Bantilldelning för 'X' hänvisar till en sträcka som inte finns#" + team.getClass(false)).setColor(colorRed); } else { wstring name; @@ -543,8 +543,8 @@ void IOF30Interface::classAssignmentObsolete(gdioutput &gdi, xmlList &xAssignmen for (bibIterT it = range.first; it != range.second; ++it) { int ln = it->second->getLegNumber(); int rLegNumber = 0, rLegOrder = 0; - if (it->second->getClassRef()) - it->second->getClassRef()->splitLegNumberParallel(ln, rLegNumber, rLegOrder); + if (it->second->getClassRef(false)) + it->second->getClassRef(false)->splitLegNumberParallel(ln, rLegNumber, rLegOrder); bool match = true; if (leg != 0 && leg != rLegNumber+1) match = false; @@ -569,14 +569,14 @@ void IOF30Interface::classAssignmentObsolete(gdioutput &gdi, xmlList &xAssignmen vector t; oe.getTeams(0, t); for (size_t i = 0; i < t.size(); i++) - clsName2Team[make_pair(t[i]->getClass(), t[i]->getName())] = t[i]; + clsName2Team[make_pair(t[i]->getClass(false), t[i]->getName())] = t[i]; c2TeamInit = true; } teamIterT res = clsName2Team.find(make_pair(className, teamName)); if (res != clsName2Team.end()) { - pClass cls = res->second->getClassRef(); + pClass cls = res->second->getClassRef(false); if (cls) { int ln = cls->getLegNumberLinear(leg, legOrder); pRunner r = res->second->getRunner(ln); @@ -863,7 +863,7 @@ void IOF30Interface::readEntryList(gdioutput &gdi, xmlobject &xo, bool removeNon key[j] = it->second[j].second; sort(key.begin(), key.end()); - classLegEqClasses[t->getClassId()].insert(key); + classLegEqClasses[t->getClassId(false)].insert(key); } } } @@ -1326,7 +1326,7 @@ pTeam IOF30Interface::readTeamEntry(gdioutput &gdi, xmlobject &xTeam, map > localTeamClassConfig; pClass pc = readClass(xTeam.getObject("Class"), localTeamClassConfig); - if (pc && (t->getClassId() == 0 || !t->hasFlag(oAbstractRunner::FlagUpdateClass)) ) { + if (pc && (t->getClassId(false) == 0 || !t->hasFlag(oAbstractRunner::FlagUpdateClass)) ) { t->setClassId(pc->getId(), false); } wstring bib; @@ -1392,7 +1392,7 @@ pTeam IOF30Interface::readTeamStart(gdioutput &gdi, pClass pc, xmlobject &xTeam, return 0; // Class - if (pc && (t->getClassId() == 0 || !t->hasFlag(oAbstractRunner::FlagUpdateClass)) ) + if (pc && (t->getClassId(false) == 0 || !t->hasFlag(oAbstractRunner::FlagUpdateClass)) ) t->setClassId(pc->getId(), false); wstring bib; @@ -1534,7 +1534,7 @@ pRunner IOF30Interface::readPersonEntry(gdioutput &gdi, xmlobject &xo, pTeam tea if (cardNo > 0 && r == 0 && team) { // We got no person, but a card number. Add the runner anonymously. - r = oe.addRunner(lang.tl(L"N.N."), team->getClubId(), team->getClassId(), cardNo, 0, false); + r = oe.addRunner(lang.tl(L"N.N."), team->getClubId(), team->getClassId(false), cardNo, 0, false); r->flagEntryTouched(true); r->setEntrySource(entrySourceId); r->synchronize(); @@ -1555,21 +1555,21 @@ pRunner IOF30Interface::readPersonEntry(gdioutput &gdi, xmlobject &xo, pTeam tea map > localTeamClassConfig; pClass pc = readClass(xo.getObject("Class"), localTeamClassConfig); - if (pc && (r->getClassId() == 0 || !r->hasFlag(oAbstractRunner::FlagUpdateClass)) ) + if (pc && (r->getClassId(false) == 0 || !r->hasFlag(oAbstractRunner::FlagUpdateClass)) ) r->setClassId(pc->getId(), false); if (team) { int leg = xo.getObjectInt("Leg"); int legorder = xo.getObjectInt("LegOrder"); int legindex = max(0, leg - 1); - map >::const_iterator res = teamClassConfig.find(team->getClassId()); + map >::const_iterator res = teamClassConfig.find(team->getClassId(false)); if (res != teamClassConfig.end()) { legindex = getIndexFromLegPos(leg, legorder, res->second); } if (personId2TeamLeg.find(r->getId()) == personId2TeamLeg.end()) { - if (team->getClassRef()) - legindex = team->getClassRef()->getLegRunner(legindex); + if (team->getClassRef(false)) + legindex = team->getClassRef(false)->getLegRunner(legindex); // Ensure unique team->setRunner(legindex, r, false); @@ -1676,7 +1676,7 @@ pRunner IOF30Interface::readPersonStart(gdioutput &gdi, pClass pc, xmlobject &xo int leg = starts[k].getObjectInt("Leg"); int legorder = starts[k].getObjectInt("LegOrder"); int legindex = max(0, leg - 1); - map >::const_iterator res = teamClassConfig.find(team->getClassId()); + map >::const_iterator res = teamClassConfig.find(team->getClassId(false)); if (res != teamClassConfig.end()) { legindex = getIndexFromLegPos(leg, legorder, res->second); } @@ -1698,7 +1698,7 @@ pRunner IOF30Interface::readPersonStart(gdioutput &gdi, pClass pc, xmlobject &xo } } - if (pc && (r->getClassId() == 0 || !r->hasFlag(oAbstractRunner::FlagUpdateClass)) ) + if (pc && (r->getClassId(false) == 0 || !r->hasFlag(oAbstractRunner::FlagUpdateClass)) ) r->setClassId(pc->getId(), true); r->synchronize(); @@ -1961,7 +1961,7 @@ void IOF30Interface::writeAssignedFee(xmlparser &xml, const oAbstractRunner &tr, if (paid >= paidForCard) { paid -= paidForCard; // Included in card service fee } - const pClass pc = tr.getClassRef(); + const oClass *pc = tr.getClassRef(false); if (!splitLateFee || !pc || !tr.hasLateEntryFee()) { xml.startTag("AssignedFee"); string type = tr.hasLateEntryFee() ? "Late" : "Normal"; @@ -2476,7 +2476,6 @@ void IOF30Interface::writeClass(xmlparser &xml, const oClass &c) { else if (stat == oClass::InvalidRefund) xml.write("Status", L"InvalidatedNoFee"); - xml.endTag(); } @@ -2537,7 +2536,7 @@ void IOF30Interface::writePersonResult(xmlparser &xml, const oRunner &r, if (teamMember) { oRunner const *resultHolder = &r; cTeam t = r.getTeam(); - pClass cls = r.getClassRef(); + const oClass *cls = r.getClassRef(false); if (t && cls) { int leg = r.getLegNumber(); @@ -2614,9 +2613,9 @@ void IOF30Interface::writeResult(xmlparser &xml, const oRunner &rPerson, const o xml.write("TimeBehind", after); } - if (r.getClassRef()) { + if (r.getClassRef(false)) { - if (r.statusOK() && r.getClassRef()->getNoTiming() == false) { + if (r.statusOK() && r.getClassRef(false)->getNoTiming() == false) { if (!teamMember && r.getPlace() > 0 && r.getPlace() < 50000) { xml.write("Position", r.getPlace()); } @@ -2634,19 +2633,19 @@ void IOF30Interface::writeResult(xmlparser &xml, const oRunner &rPerson, const o xml.write("Status", formatStatus(r.getStatus())); - if ( (r.getTeam() && r.getClassRef()->getClassType() != oClassPatrol && !teamsAsIndividual) || hasInputTime) { + if ( (r.getTeam() && r.getClassRef(false)->getClassType() != oClassPatrol && !teamsAsIndividual) || hasInputTime) { xml.startTag("OverallResult"); int rt = r.getTotalRunningTime(); if (rt > 0) xml.write("Time", rt); - bool hasTiming = r.getClassRef()->getNoTiming() == false; + bool hasTiming = r.getClassRef(false)->getNoTiming() == false; RunnerStatus stat = r.getTotalStatus(); int tleg = r.getLegNumber() >= 0 ? r.getLegNumber() : 0; if (stat == StatusOK && hasTiming) { - int after = r.getTotalRunningTime() - r.getClassRef()->getTotalLegLeaderTime(tleg, true); + int after = r.getTotalRunningTime() - r.getClassRef(true)->getTotalLegLeaderTime(tleg, true); if (after >= 0) xml.write("TimeBehind", after); } @@ -3100,7 +3099,7 @@ void IOF30Interface::writeStart(xmlparser &xml, const oRunner &r, void IOF30Interface::writeLegOrder(xmlparser &xml, const oRunner &r) const { // Team member race result int legNumber, legOrder; - const pClass pc = r.getClassRef(); + const oClass *pc = r.getClassRef(false); if (pc) { bool par = pc->splitLegNumberParallel(r.getLegNumber(), legNumber, legOrder); xml.write("Leg", legNumber + 1); diff --git a/code/liveresult.cpp b/code/liveresult.cpp index 9753067..14a06b7 100644 --- a/code/liveresult.cpp +++ b/code/liveresult.cpp @@ -112,7 +112,7 @@ void LiveResult::showTimer(gdioutput &gdi, const oListInfo &liIn) { pair key = make_pair(r->getId(), pp[k]->getControlId()); processedPunches[key] = max(processedPunches[key], pp[k]->getAdjustedTime()); - if (!li.getParam().selection.empty() && !li.getParam().selection.count(r->getClassId())) + if (!li.getParam().selection.empty() && !li.getParam().selection.count(r->getClassId(true))) continue; // Filter class if (pp[k]->getTypeCode() == fromPunch) { @@ -182,7 +182,7 @@ void LiveResult::handle(gdioutput &gdi, BaseInfo &bu, GuiEventType type) { if (!r) continue; - if (!li.getParam().selection.empty() && !li.getParam().selection.count(r->getClassId())) + if (!li.getParam().selection.empty() && !li.getParam().selection.count(r->getClassId(true))) continue; // Filter class pair key = make_pair(r->getId(), pp[k]->getControlId()); diff --git a/code/meos.cpp b/code/meos.cpp index 0a57138..21acb02 100644 --- a/code/meos.cpp +++ b/code/meos.cpp @@ -1149,6 +1149,11 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) delete gSI; gSI=0; + for (size_t k = 0; k < gdi_extra.size(); k++) { + if (gdi_extra[k]) + gdi_extra[k]->clearPage(false, false); + } + if (gEvent) { try { gEvent->save(); diff --git a/code/meos.rc b/code/meos.rc index 803ca73..ab687bb 100644 --- a/code/meos.rc +++ b/code/meos.rc @@ -28,10 +28,29 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL // BMP_TEST BITMAP "bitmap1.bmp" + #endif // Neutral resources ///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// +// Neutral (Default) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEUD) +LANGUAGE LANG_NEUTRAL, SUBLANG_DEFAULT +#pragma code_page(1252) + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDB_ECO BITMAP "bmp00001.bmp" + +#endif // Neutral (Default) resources +///////////////////////////////////////////////////////////////////////////// + + ///////////////////////////////////////////////////////////////////////////// // English (United States) resources @@ -48,6 +67,7 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // remains consistent on all systems. IDI_MEOS ICON "meos.ICO" + ///////////////////////////////////////////////////////////////////////////// // // Menu @@ -160,6 +180,14 @@ BEGIN END END + +///////////////////////////////////////////////////////////////////////////// +// +// HTML +// + +IDR_HTML1 HTML "html1.htm" + #endif // Swedish (Sweden) resources ///////////////////////////////////////////////////////////////////////////// diff --git a/code/meos_util.cpp b/code/meos_util.cpp index d14ecff..4ef08e1 100644 --- a/code/meos_util.cpp +++ b/code/meos_util.cpp @@ -1368,7 +1368,7 @@ static void decomposeClassName(const wstring &name, vector &dec) { for (size_t i = 0; i < name.size(); i++) { int bchar = toLowerStripped(name[i]); - if (isspace(bchar) || bchar == '-' || bchar == 160) { + if (myIsSpace(bchar) || bchar == '-' || bchar == 160) { if (!dec.back().empty()) dec.push_back(wstring()); continue; @@ -1823,7 +1823,7 @@ void capitalize(wstring &str) { int getTimeZoneInfo(const wstring &date) { static wchar_t lastDate[16] = {0}; static int lastValue = -1; - // Local cacheing + // Local caching if (lastValue != -1 && lastDate == date) { return lastValue; } @@ -2161,8 +2161,14 @@ void processGeneralTime(const wstring &generalTime, wstring &meosTime, wstring & } void string2Wide(const string &in, wstring &out) { - out.clear(); - out.insert(out.begin(), in.begin(), in.end());// XXX Simple extend + int cp = 1252; + if (in.empty()) { + out = L""; + return; + } + out.reserve(in.size() + 1); + out.resize(in.size(), 0); + MultiByteToWideChar(cp, MB_PRECOMPOSED, in.c_str(), in.size(), &out[0], out.size() * sizeof(wchar_t)); } void wide2String(const wstring &in, string &out) { diff --git a/code/meosdb/MeosSQL.cpp b/code/meosdb/MeosSQL.cpp index dc2c51c..eb8ff41 100644 --- a/code/meosdb/MeosSQL.cpp +++ b/code/meosdb/MeosSQL.cpp @@ -1906,7 +1906,7 @@ OpFailStatus MeosSQL::syncUpdate(oRunner *r, bool forceWriteAll) << " StartTime=" << r->startTime << ", " << " FinishTime=" << r->FinishTime << ", " << " Course=" << r->getCourseId() << ", " - << " Class=" << r->getClassId() << ", " + << " Class=" << r->getClassId(false) << ", " << " Club=" << r->getClubId() << ", " << " Card=" << r->getCardId() << ", " << " Status=" << r->status << ", " @@ -2101,7 +2101,7 @@ OpFailStatus MeosSQL::syncUpdate(oTeam *t, bool forceWriteAll) { << " Runners=" << quote << t->getRunners() << ", " << " StartTime=" << t->startTime << ", " << " FinishTime=" << t->FinishTime << ", " - << " Class=" << t->getClassId() << ", " + << " Class=" << t->getClassId(false) << ", " << " Club=" << t->getClubId() << ", " << " StartNo=" << t->getStartNo() << ", " << " Status=" << t->status diff --git a/code/meosvc15.vcxproj b/code/meosvc15.vcxproj index 0b0fd7f..67e8374 100644 --- a/code/meosvc15.vcxproj +++ b/code/meosvc15.vcxproj @@ -335,6 +335,7 @@ + %(PreprocessorDefinitions) %(PreprocessorDefinitions) @@ -420,6 +421,7 @@ + @@ -482,6 +484,9 @@ + + true + diff --git a/code/meosversion.cpp b/code/meosversion.cpp index a91ac30..898f6dc 100644 --- a/code/meosversion.cpp +++ b/code/meosversion.cpp @@ -27,9 +27,9 @@ //V2: ABCDEFGHIHJKMN //V31: a //V33: abcde -//V35: abc +//V35: abcde int getMeosBuild() { - string revision("$Rev: 621 $"); + string revision("$Rev: 634 $"); return 174 + atoi(revision.substr(5, string::npos).c_str()); } @@ -41,7 +41,7 @@ int getMeosBuild() { //V33: abcdefghij //V34: abcdfg wstring getMeosDate() { - wstring date(L"$Date: 2017-10-21 22:43:38 +0200 (lö, 21 okt 2017) $"); + wstring date(L"$Date: 2017-12-25 16:08:33 +0100 (mÃ¥, 25 dec 2017) $"); return date.substr(7,10); } @@ -109,7 +109,7 @@ void getSupporters(vector &supp) supp.push_back("Oskarström OK"); supp.push_back("Skogslöparna"); supp.push_back("OK Milan"); - supp.push_back("GoIF Tjalve"); + supp.push_back("Tjalve IF"); supp.push_back("OK Skärmen"); supp.push_back("Østkredsen"); supp.push_back("OK Roskilde"); @@ -177,4 +177,7 @@ void getSupporters(vector &supp) supp.push_back("Åke Larsson, OK Hedströmmen"); supp.push_back("Avesta OK"); supp.push_back("Motionsorientering Göteborg"); + supp.push_back("OK Måsen"); + supp.push_back("IF Thor"); + supp.push_back("SOS Jindrichuv Hradec"); } diff --git a/code/oBase.cpp b/code/oBase.cpp index b728240..de84e64 100644 --- a/code/oBase.cpp +++ b/code/oBase.cpp @@ -54,6 +54,7 @@ oBase::oBase(oEvent *poe) counter = 0; Modified.update(); correctionNeeded = true; + localObject = false; } oBase::~oBase() @@ -68,6 +69,8 @@ bool oBase::synchronize(bool writeOnly) } if (oe && oe->HasDBConnection && (changed || !writeOnly)) { correctionNeeded = false; + if (localObject) + return false; return oe->msSynchronize(this); } else { @@ -79,17 +82,6 @@ bool oBase::synchronize(bool writeOnly) return true; } -void oBase::clearCombo(HWND hWnd) -{ - SendMessage(hWnd, CB_RESETCONTENT, 0, 0); -} - -void oBase::addToCombo(HWND hWnd, const string &str, int data) -{ - int index=SendMessage(hWnd, CB_ADDSTRING, 0, LPARAM(str.c_str())); - SendMessage(hWnd, CB_SETITEMDATA, index, data); -} - void oBase::setExtIdentifier(__int64 id) { getDI().setInt64("ExtId", id); diff --git a/code/oBase.h b/code/oBase.h index e86e0c8..3bbf071 100644 --- a/code/oBase.h +++ b/code/oBase.h @@ -75,7 +75,7 @@ private: void resetChangeStatus() {changed &= reChanged;} bool reChanged; bool changed; - + bool localObject; const static unsigned long long BaseGenStringFlag = 1ull << 63; const static unsigned long long Base36StringFlag = 1ull << 62; const static unsigned long long ExtStringMask = ~(BaseGenStringFlag|Base36StringFlag); @@ -106,15 +106,17 @@ protected: /** Change the id of the object */ virtual void changeId(int newId); - //Used for handeling GUI combo boxes; - static void clearCombo(HWND hWnd); - static void addToCombo(HWND hWnd, const string &str, int data); - /** Get internal data buffers for DI */ virtual oDataContainer &getDataBuffers(pvoid &data, pvoid &olddata, pvectorstr &strData) const = 0; virtual int getDISize() const = 0; + void setLocalObject() { localObject = true; } + public: + + // Returns true if the object is local, not stored in DB/On disc + bool isLocalObject() { return localObject; } + /// Returns textual information on the object virtual wstring getInfo() const = 0; diff --git a/code/oClass.cpp b/code/oClass.cpp index 26905da..6edb3ed 100644 --- a/code/oClass.cpp +++ b/code/oClass.cpp @@ -43,6 +43,8 @@ #include "gdistructures.h" #include "meosexception.h" #include "random.h" +#include "qualification_final.h" +#include "generalresult.h" ////////////////////////////////////////////////////////////////////// // Construction/Destruction @@ -64,6 +66,7 @@ oClass::oClass(oEvent *poe): oBase(poe) tCoursesChanged = false; tStatusRevision = 0; tShowMultiDialog = false; + parentClass = 0; } oClass::oClass(oEvent *poe, int id): oBase(poe) @@ -73,7 +76,7 @@ oClass::oClass(oEvent *poe, int id): oBase(poe) if (id == 0) id = oe->getFreeClassId(); Id=id; - oe->qFreeClassId = max(id, oe->qFreeClassId); + oe->qFreeClassId = max(id % MaxClassId, oe->qFreeClassId); tLeaderTime.resize(1); tNoTiming = -1; tIgnoreStartPunch = -1; @@ -85,6 +88,8 @@ oClass::oClass(oEvent *poe, int id): oBase(poe) tCoursesChanged = false; tStatusRevision = 0; tShowMultiDialog = false; + + parentClass = 0; } oClass::~oClass() @@ -359,7 +364,7 @@ int oClass::getNumRunners(bool checkFirstLeg, bool noCountVacant, bool noCountNo oRunnerList::iterator it; for (it=oe->Runners.begin(); it != oe->Runners.end(); ++it) { - if (it->getClassId()==Id) { + if (it->getClassId(true)==Id) { if (it->skip()) continue; if (checkFirstLeg && it->tLeg > 0) @@ -412,7 +417,7 @@ void oClass::setName(const wstring &name) oDataContainer &oClass::getDataBuffers(pvoid &data, pvoid &olddata, pvectorstr &strData) const { data = (pvoid)oData; olddata = (pvoid)oDataOld; - strData = 0; + strData = const_cast< vector >* >(&oDataStr); return *oe->oClassData; } @@ -1067,13 +1072,14 @@ const vector< pair > &oEvent::fillClasses(vector< pairisRemoved()) continue; - if (rit->Class) { - if (rit->Class->getNumStages() > 0 && rit->Class->getStartType(rit->tLeg) != STDrawn) + pClass pc = rit->getClassRef(true); + if (pc) { + if (pc->getNumStages() > 0 && pc->getStartType(rit->tLeg) != STDrawn) needTime = false; } if (rit->tStartTime==0 && needTime) - undrawn.insert(rit->getClassId()); - hasRunner.insert(rit->getClassId()); + undrawn.insert(rit->getClassId(true)); + hasRunner.insert(rit->getClassId(true)); } } else if (extended == extraNumMaps) @@ -1137,9 +1143,9 @@ void oEvent::getNotDrawnClasses(set &classes, bool someMissing) synchronizeList(oLRunnerId); for (rit=Runners.begin(); rit != Runners.end(); ++rit) { if (rit->tStartTime>0) - drawn.insert(rit->getClassId()); + drawn.insert(rit->getClassId(true)); else if (someMissing) - classes.insert(rit->getClassId()); + classes.insert(rit->getClassId(true)); } // Return all classe where some runner has no start time @@ -1231,28 +1237,6 @@ bool oClass::isCourseUsed(int Id) const return false; } -string oClass::getClassResultStatus() const -{ - list::iterator it; - - int nrunner=0; - int nrunner_finish=0; - - for (it=oe->Runners.begin(); it!=oe->Runners.end();++it) { - if (it->getClassId()==Id){ - nrunner++; - - if (it->getStatus()) - nrunner_finish++; - } - } - - char bf[128]; - sprintf_s(bf, "%d/%d", nrunner_finish, nrunner); - - return bf; -} - bool oClass::hasTrueMultiCourse() const { if (MultiCourse.empty()) return false; @@ -1516,7 +1500,7 @@ void oEvent::getNumClassRunners(int id, int leg, int &total, int &finished, int int maxleg = pc->getLastStageIndex(); for (it=Runners.begin(); it != Runners.end(); ++it){ - if (!it->skip() && it->getClassId()==id && it->getStatus() != StatusNotCompetiting) { + if (!it->skip() && it->getClassId(true)==id && it->getStatus() != StatusNotCompetiting) { if (leg==0) { total++; @@ -1543,7 +1527,7 @@ void oEvent::getNumClassRunners(int id, int leg, int &total, int &finished, int oTeamList::const_iterator it; for (it=Teams.begin(); it != Teams.end(); ++it) { - if (it->getClassId()==id) { + if (it->getClassId(true)==id) { total++; if (it->tStatus!=StatusUnknown || @@ -2695,7 +2679,7 @@ void oClass::getStatistics(const set &feeLock, int &entries, int &started) if (it->getStatus() == StatusNotCompetiting) continue; - if (it->getClassId()==Id) { + if (it->getClassId(false)==Id) { if (feeLock.empty() || feeLock.count(it->getDCI().getInt("Fee"))) { entries++; if (it->getStatus()!= StatusUnknown && it->getStatus()!= StatusDNS && it->tStatus != StatusCANCEL) @@ -2789,6 +2773,22 @@ void oClass::reinitialize() tMaxTime = oe->getMaximalTime(); } + wstring wInfo = getDI().getString("Qualification"); + if (!wInfo.empty()) { + if (!qualificatonFinal) + qualificatonFinal = make_shared(MaxClassId, Id); + + qualificatonFinal->init(wInfo); + virtualClasses.resize(getNumQualificationFinalClasses()); + + int nc = qualificatonFinal->getNumClasses(); + for (int i = 1; i <= nc; i++) + getVirtualClass(i); + } + else { + qualificatonFinal.reset(); + } + tNoTiming = -1; tIgnoreStartPunch = -1; } @@ -3787,7 +3787,7 @@ void oClass::extractBibPatterns(oEvent &oe, map > &patte patterns.clear(); wchar_t pattern[32]; for (size_t k = t.size(); k > 0; k--) { - int cls = t[k-1]->getClassId(); + int cls = t[k-1]->getClassId(true); if (cls == 0) continue; const wstring &bib = t[k-1]->getBib(); @@ -3806,7 +3806,7 @@ void oClass::extractBibPatterns(oEvent &oe, map > &patte for (size_t k = r.size(); k > 0; k--) { if (r[k-1]->getTeam() != 0) continue; - int cls = r[k-1]->getClassId(); + int cls = r[k-1]->getClassId(true); if (cls == 0) continue; const wstring &bib = r[k-1]->getBib(); @@ -4061,7 +4061,7 @@ void oClass::drawSeeded(ClassSeedMethod seed, int leg, int firstStart, } } -bool oClass::hasClassGlobalDependance() const { +bool oClass::hasClassGlobalDependence() const { for (size_t k = 0; k < legInfo.size(); k++) { if (legInfo[k].startMethod == STHunting) return true; @@ -4139,6 +4139,8 @@ int oClass::getNextBaseLeg(int leg) const { } int oClass::getPreceedingLeg(int leg) const { + if (size_t(leg) >= legInfo.size()) + leg = legInfo.size() - 1; for (int k = leg; k > 0; k--) { if (!(legInfo[k].isParallel() || legInfo[k].isOptional())) return k-1; @@ -4147,9 +4149,242 @@ int oClass::getPreceedingLeg(int leg) const { } bool oClass::lockedForking() const { - return getDCI().getInt("Locked") != 0; + return (getDCI().getInt("Locked") & 1) == 1; } void oClass::lockedForking(bool locked) { - getDI().setInt("Locked", locked); + int current = getDCI().getInt("Locked"); + getDI().setInt("Locked", locked ? (current | 1) : (current & ~1)); +} + +bool oClass::lockedClassAssignment() const { + return (getDCI().getInt("Locked") & 2) == 2; +} + +void oClass::lockedClassAssignment(bool locked) { + int current = getDCI().getInt("Locked"); + getDI().setInt("Locked", locked ? (current | 2) : (current & ~2)); +} + +oClass *oClass::getVirtualClass(int instance, bool allowCreation) { + if (instance == 0) + return this; + if (parentClass) + return parentClass->getVirtualClass(instance, allowCreation); + + if (size_t(instance) < virtualClasses.size() && virtualClasses[instance]) + return virtualClasses[instance]; + + if (instance >= getNumQualificationFinalClasses()) + return this; // Invalid + virtualClasses.resize(getNumQualificationFinalClasses()); + int virtId = Id + instance * MaxClassId; + virtualClasses[instance] = oe->getClass(virtId); + if (virtualClasses[instance]) { + virtualClasses[instance]->parentClass = pClass(this); + return virtualClasses[instance]; + } + configureInstance(instance, allowCreation); + if (virtualClasses[instance]) + return virtualClasses[instance]; + return this; // Fallback +} + +const oClass *oClass::getVirtualClass(int instance) const { + if (instance == 0) + return this; + if (parentClass) + return parentClass->getVirtualClass(instance); + + if (size_t(instance) < virtualClasses.size() && virtualClasses[instance]) + return virtualClasses[instance]; + + if (instance >= getNumQualificationFinalClasses()) + return this; // Invalid + virtualClasses.resize(getNumQualificationFinalClasses()); + + int virtId = Id + instance * MaxClassId; + virtualClasses[instance] = oe->getClass(virtId); + if (virtualClasses[instance]) { + virtualClasses[instance]->parentClass = pClass(this); + return virtualClasses[instance]; + } + configureInstance(instance, false); + if (virtualClasses[instance]) + return virtualClasses[instance]; + return this; // Fallback +} + +void oClass::configureInstance(int instance, bool allowCreation) const { + int virtId = Id + instance * MaxClassId; + virtualClasses[instance] = oe->getClass(virtId); + if (virtualClasses[instance]) { + virtualClasses[instance]->parentClass = pClass(this); + return; + } + if (!allowCreation) + return; + + oClass copy(*this); + copy.Id = Id + instance * MaxClassId; + copy.setExtIdentifier(copy.Id); + copy.Name += makeDash(L"-") + qualificatonFinal->getInstanceName(instance); + copy.setLocalObject(); + copy.parentClass = pClass(this); + copy.tSortIndex += instance; + copy.getDI().setInt("SortIndex", copy.tSortIndex); + copy.legInfo.clear(); + copy.MultiCourse.clear(); + copy.getDI().setString("Qualification", L""); + virtualClasses[instance] = oe->addClass(copy); +} + +int oClass::getNumQualificationFinalClasses() const { + if (qualificatonFinal) + return qualificatonFinal->getNumClasses()+1; + return 0; +} + +void oClass::loadQualificationFinalScheme(const wstring &fileName) { + auto qf = make_shared(MaxClassId, Id); + qf->import(fileName); + wstring enc; + qf->encode(enc); + int ns = qf->getNumStages(); + setNumStages(ns); + for (int i = 1; i < ns; i++) { + setStartType(i, StartTypes::STDrawn, true); + setLegType(i, LegTypes::LTNormal); + setLegRunner(i, 0); + } + qualificatonFinal = qf; + getDI().setString("Qualification", enc); + for (int i = 0; i < qualificatonFinal->getNumClasses(); i++) { + pClass inst = getVirtualClass(i+1, true); + inst->synchronize(); + } + synchronize(); +} + +void oClass::updateFinalClasses(oRunner *causingResult, bool updateStartNumbers) { + if (!qualificatonFinal) + return; + assert(!causingResult || causingResult->Class == this); + int instance = causingResult ? causingResult->classInstance() : 0; + pClass currentInst = getVirtualClass(instance, false); + if (instance == virtualClasses.size()) + return; // Final class + int maxDepth = getNumStages(); + bool needIter = true; + int limit = virtualClasses.size() - 1; + + while (needIter && --maxDepth > 0) { + needIter = false; + if (size_t(instance) >= virtualClasses.size()) + break; // Final class + + vector< vector > classSplit(virtualClasses.size()); + + for (oRunner &r : oe->Runners) { + if (r.isRemoved() || !r.Class) + continue; + + if (r.Class != this && (r.Class->getId() % MaxClassId) != getId()) + continue; + + int inst = r.Class == this ? r.classInstance() : (r.Class->getId() - getId()) & MaxClassId; + + if (inst == 0 && r.tLeg > 0) + continue; // Only allow base class for leg 0. + + if (inst < instance || inst >= limit) + continue; + + classSplit[inst].push_back(&r); + } + + GeneralResult gr; + + for (int i = instance; i < limit; i++) { + if (classSplit[i].empty()) + continue; + + if (i == 0 && qualificatonFinal->noQualification(i)) { + set allowed; + qualificatonFinal->getBaseClassInstances(allowed); + // Place all in this group + for (pRunner r : classSplit[i]) { + auto di = r->getDI(); + int oldHeat = di.getInt("Heat"); + if (allowed.count(oldHeat) || classSplit.size() < 2) + continue; + // Take the smallest gruop. User can set heat explicitly of other distribution is wanted. + int heat = 1; + for (int i : allowed) { + if (size_t(i) < classSplit.size() && + classSplit[heat].size() > classSplit[i].size()) + heat = i; + } + if (heat != oldHeat) { + bool lockedStartList = getVirtualClass(heat)->lockedClassAssignment() || + getVirtualClass(oldHeat)->lockedClassAssignment(); + + if (!lockedStartList) { + pClass oldClass = r->getClassRef(true); + oldClass->markSQLChanged(-1, 0); + di.setInt("Heat", heat); + r->classInstanceRev.first = -1; + r->synchronize(); + } + } + } + } + + gr.calculateIndividualResults(classSplit[i], oListInfo::Classwise, true, 0); + int lastPlace = 0, orderPlace = 1; + int numEqual = 0; + for (size_t k = 0; k < classSplit[i].size(); k++) { + auto &res = classSplit[i][k]->getTempResult(); + int heat = 0; + if (res.getStatus() == StatusOK) { + int place = res.getPlace(); + if (lastPlace == place) + numEqual++; + else + numEqual = 0; + + auto nextFinal = qualificatonFinal->getNextFinal(i, orderPlace, numEqual); + heat = nextFinal.first; + lastPlace = place; + } + orderPlace++; + oRunner &thisRunner = *classSplit[i][k]; + pRunner runnerToChange = thisRunner.getMultiRunner(thisRunner.getRaceNo() + 1); + + if (runnerToChange) { + auto di = runnerToChange->getDI(); + int oldHeat = di.getInt("Heat"); + + if (heat != oldHeat) { + bool lockedStartList = getVirtualClass(heat)->lockedClassAssignment() || + getVirtualClass(oldHeat)->lockedClassAssignment(); + + if (!lockedStartList) { + pClass oldClass = runnerToChange->getClassRef(true); + oldClass->markSQLChanged(-1, 0); + di.setInt("Heat", heat); + runnerToChange->classInstanceRev.first = -1; + runnerToChange->synchronize(); + if (runnerToChange->getFinishTime() > 0) + needIter = true; + } + } + } + } + if (needIter) { + instance = i+1; // Need not process last class again + break; + } + } + } } diff --git a/code/oClass.h b/code/oClass.h index 4c176e9..8a27fc1 100644 --- a/code/oClass.h +++ b/code/oClass.h @@ -40,6 +40,8 @@ class oClass; typedef oClass* pClass; class oDataInterface; +const int MaxClassId = 1000000; + enum PersonSex; enum StartTypes { @@ -132,6 +134,7 @@ struct ClassResultInfo { int lastStartTime; }; +class QualificationFinal; enum ClassType {oClassIndividual=1, oClassPatrol=2, oClassRelay=3, oClassIndividRelay=4}; @@ -206,7 +209,7 @@ protected: BYTE oData[dataSize]; BYTE oDataOld[dataSize]; - + vector< vector > oDataStr; //Multicourse data string codeMultiCourse() const; //Fill courseId with id:s of used courses. @@ -258,14 +261,34 @@ protected: static long long setupForkKey(const vector indices, const vector< vector< vector > > &courseKeys, vector &ws); + mutable vector virtualClasses; + pClass parentClass; + + shared_ptr qualificatonFinal; + + void configureInstance(int instance, bool allowCreation) const; public: + /** The master class in a qualification/final scheme. */ + const pClass getParentClass() const { return parentClass; } + + const QualificationFinal *getQualificationFinal() const { return qualificatonFinal.get(); } + + /** Returns the number of possible final classes.*/ + int getNumQualificationFinalClasses() const; + void loadQualificationFinalScheme(const wstring &fileName); + + void updateFinalClasses(oRunner *causingResult, bool updateStartNumbers); + static void initClassId(oEvent &oe); // Return true if forking in the class is locked bool lockedForking() const; void lockedForking(bool locked); + bool lockedClassAssignment() const; + void lockedClassAssignment(bool locked); + // Draw data int getDrawFirstStart() const; void setDrawFirstStart(int st); @@ -286,7 +309,7 @@ public: void drawSeeded(ClassSeedMethod seed, int leg, int firstStart, int interval, const vector &groups, bool noClubNb, bool reverse, int pairSize); /** Returns true if the class is setup so that changeing one runner can effect all others. (Pursuit)*/ - bool hasClassGlobalDependance() const; + bool hasClassGlobalDependence() const; // Autoassign new bibs static void extractBibPatterns(oEvent &oe, map > &patterns); pair getNextBib(map > &patterns); // Version that calculates next free bib from cached data (fast, no gap usage) @@ -315,6 +338,9 @@ public: return false; } + oClass *getVirtualClass(int instance, bool allowCreation); + const oClass *getVirtualClass(int instance) const; + ClassStatus getClassStatus() const; ClassMetaType interpretClassType() const; @@ -441,9 +467,6 @@ public: void setDirectResult(bool directResult); bool hasDirectResult() const; - - string getClassResultStatus() const; - bool isCourseUsed(int Id) const; wstring getLength(int leg) const; @@ -472,7 +495,7 @@ public: void setNumStages(int no); - bool operator<(const oClass &b){return tSortIndex &c, bool sort) { } } - void oEvent::viewClubMembers(gdioutput &gdi, int clubId) { sortRunners(ClassStartTime); @@ -440,10 +439,12 @@ void oEvent::viewClubMembers(gdioutput &gdi, int clubId) int nt = 0; // Update teams for (oTeamList::iterator it = Teams.begin(); it!=Teams.end(); ++it) { + if (it->skip()) + continue; if (it->getClubId() == clubId) { if (nt==0) gdi.addString("", 1, "Lag(flera)"); - gdi.addStringUT(0, it->getName() + L", " + it->getClass() ); + gdi.addStringUT(0, it->getName() + L", " + it->getClass(false) ); nt++; } } @@ -451,10 +452,12 @@ void oEvent::viewClubMembers(gdioutput &gdi, int clubId) gdi.dropLine(); // Update runners for (oRunnerList::iterator it = Runners.begin(); it!=Runners.end(); ++it) { + if (it->skip()) + continue; if (it->getClubId() == clubId) { if (nr==0) gdi.addString("", 1, "Löpare:"); - gdi.addStringUT(0, it->getName() + L", " + it->getClass() ); + gdi.addStringUT(0, it->getName() + L", " + it->getClass(true) ); nr++; } } @@ -496,7 +499,7 @@ void oClub::addRunnerInvoiceLine(const pRunner r, bool inTeam, wstring ts; if (!inTeam) - line.addString(xs+data.clsPos, r->getClass()); + line.addString(xs+data.clsPos, r->getClass(true)); if (r->getStatus() == StatusUnknown) ts = L"-"; @@ -504,8 +507,8 @@ void oClub::addRunnerInvoiceLine(const pRunner r, bool inTeam, if (r->getStatus()==StatusOK) { ClassType type = oClassIndividual; cTeam t = r->getTeam(); - if (t && r->getClassRef()) - type = r->getClassRef()->getClassType(); + if (t && r->getClassRef(false)) + type = r->getClassRef(false)->getClassType(); if (type == oClassIndividRelay || type == oClassRelay) { int leg = r->getLegNumber(); @@ -541,7 +544,7 @@ void oClub::addRunnerInvoiceLine(const pRunner r, bool inTeam, if (res != definedPayModes.end()) payMode = ", " + res->second; */ - if (r->getClassRef() && r->getClassRef()->getClassStatus() == oClass::InvalidRefund) { + if (r->getClassRef(false) && r->getClassRef(false)->getClassStatus() == oClass::InvalidRefund) { fee = 0; card = 0; } @@ -574,7 +577,7 @@ void oClub::addTeamInvoiceLine(const pTeam t, const map &definedPa return; line.addString(xs, t->getName()); - line.addString(xs+data.clsPos, t->getClass()); + line.addString(xs+data.clsPos, t->getClass(false)); wstring ts; if (t->getStatus() == StatusUnknown) @@ -588,7 +591,7 @@ void oClub::addTeamInvoiceLine(const pTeam t, const map &definedPa } - if (t->getClassRef() && t->getClassRef()->getClassStatus() == oClass::InvalidRefund) { + if (t->getClassRef(false) && t->getClassRef(false)->getClassStatus() == oClass::InvalidRefund) { fee = 0; } diff --git a/code/oControl.cpp b/code/oControl.cpp index 0562e82..0911c5d 100644 --- a/code/oControl.cpp +++ b/code/oControl.cpp @@ -998,3 +998,21 @@ void oControl::getClasses(vector &cls) const { cls.push_back(pClass(&*it)); } } + +int oControl::getControlIdByName(const oEvent &oe, const string &name) { + if (_stricmp(name.c_str(), "finish") == 0) + return oPunch::PunchFinish; + if (_stricmp(name.c_str(), "start") == 0) + return oPunch::PunchStart; + + vector ac; + oe.getControls(ac, true); + wstring wname = oe.gdiBase().recodeToWide(name); + for (pControl c : ac) { + if (_wcsicmp(c->getName().c_str(), wname.c_str()) == 0) + return c->getId(); + } + + return 0; +} + diff --git a/code/oControl.h b/code/oControl.h index ed81199..3ff9410 100644 --- a/code/oControl.h +++ b/code/oControl.h @@ -117,6 +117,7 @@ protected: void changedObject(); public: + static int getControlIdByName(const oEvent &oe, const string &name); // Returns true if controls is considered a radio control. bool isValidRadio() const; diff --git a/code/oDataContainer.cpp b/code/oDataContainer.cpp index ef0526b..42c0f16 100644 --- a/code/oDataContainer.cpp +++ b/code/oDataContainer.cpp @@ -289,8 +289,7 @@ __int64 oDataContainer::getInt64(const void *data, const char *Name) const } } -bool oDataContainer::setString(oBase *ob, const char *name, const wstring &v) -{ +bool oDataContainer::setString(oBase *ob, const char *name, const wstring &v) { oDataInfo *odi=findVariable(name); if (!odi) diff --git a/code/oEvent.cpp b/code/oEvent.cpp index b698897..6e545e8 100644 --- a/code/oEvent.cpp +++ b/code/oEvent.cpp @@ -441,6 +441,7 @@ oEvent::oEvent(gdioutput &gdi):oBase(0), gdibase(gdi) oClassData->addVariableInt("Unordered", oDataContainer::oIS8U, "Oordnade parallella"); oClassData->addVariableInt("Heat", oDataContainer::oIS8U, "Heat"); oClassData->addVariableInt("Locked", oDataContainer::oIS8U, "Låst gaffling"); + oClassData->addVariableString("Qualification", "Kvalschema"); oTeamData = new oDataContainer(oTeam::dataSize); oTeamData->addVariableCurrency("Fee", "Anm. avgift"); @@ -1210,6 +1211,7 @@ bool oEvent::open(const xmlparser &xml) { } toc("class"); + reinitializeClasses(); //Get clubs xo=xml.getObject("ClubList"); @@ -1839,7 +1841,7 @@ void oEvent::updateFreeId(oBase *obj) qFreeRunnerId=max(obj->Id, qFreeRunnerId); } else if (typeid(*obj)==typeid(oClass)){ - qFreeClassId=max(obj->Id, qFreeClassId); + qFreeClassId=max(obj->Id % MaxClassId, qFreeClassId); } else if (typeid(*obj)==typeid(oCourse)){ qFreeCourseId=max(obj->Id, qFreeCourseId); @@ -1881,7 +1883,7 @@ void oEvent::updateFreeId() oClassList::iterator it; qFreeClassId=0; for (it=Classes.begin(); it != Classes.end(); ++it) - qFreeClassId=max(qFreeClassId, it->Id); + qFreeClassId=max(qFreeClassId, it->Id % MaxClassId); } { oCourseList::iterator it; @@ -2553,17 +2555,27 @@ void oEvent::removeCourse(int Id) void oEvent::removeClass(int Id) { oClassList::iterator it; - - for (it=Classes.begin(); it != Classes.end(); ++it){ + vector subRemove; + for (it = Classes.begin(); it != Classes.end(); ++it){ if (it->Id==Id){ + if (it->getQualificationFinal()) { + for (int n = 0; n < it->getNumQualificationFinalClasses(); n++) { + const oClass *pc = it->getVirtualClass(n); + if (pc && pc != &*it) + subRemove.push_back(pc->getId()); + } + } if (HasDBConnection) msRemove(&*it); Classes.erase(it); dataRevision++; updateTabs(); - return; + break; } } + for (int id : subRemove) { + removeClass(id); + } } void oEvent::removeControl(int Id) @@ -2642,17 +2654,33 @@ bool oEvent::isCourseUsed(int Id) const bool oEvent::isClassUsed(int Id) const { + pClass cl = getClass(Id); + if (cl && cl->parentClass) { + if (isClassUsed(cl->parentClass->Id)) + return true; + } + + set idToCheck; + idToCheck.insert(Id); + if (cl) { + for (int i = 0; i < cl->getNumQualificationFinalClasses(); i++) + idToCheck.insert(cl->getVirtualClass(i)->getId()); + } //Search runners oRunnerList::const_iterator it; for (it=Runners.begin(); it != Runners.end(); ++it){ - if (it->getClassId()==Id) + if (it->isRemoved()) + continue; + if (idToCheck.count(it->getClassId(false))) return true; } //Search teams oTeamList::const_iterator tit; for (tit=Teams.begin(); tit != Teams.end(); ++tit){ - if (tit->getClassId()==Id) + if (it->isRemoved()) + continue; + if (idToCheck.count(tit->getClassId(false))) return true; } return false; @@ -2713,7 +2741,7 @@ bool oEvent::classHasResults(int Id) const for (it=Runners.begin(); it != Runners.end(); ++it) { if (it->isRemoved()) continue; - if ( (Id == 0 || it->getClassId() == Id) && (it->getCard() || it->FinishTime)) + if ( (Id == 0 || it->getClassId(true) == Id) && (it->getCard() || it->FinishTime)) return true; } @@ -2722,10 +2750,16 @@ bool oEvent::classHasResults(int Id) const bool oEvent::classHasTeams(int Id) const { - oTeamList::const_iterator it; + pClass pc = oe->getClass(Id); + if (pc == 0) + return false; + if (pc->getQualificationFinal() != 0) + return false; + + oTeamList::const_iterator it; for (it=Teams.begin(); it != Teams.end(); ++it) - if (it->getClassId()==Id) + if (!it->isRemoved() && it->getClassId(false)==Id) return true; return false; @@ -2768,8 +2802,8 @@ void oEvent::generateVacancyList(gdioutput &gdi, GUICALLBACK cb) if (it->skip() || !it->isVacant()) continue; - if (it->getClassId() != Id) { - Id=it->getClassId(); + if (it->getClassId(true) != Id) { + Id=it->getClassId(true); y+=lh/2; if (nRunner>=RunnersPerCol) { @@ -2779,7 +2813,7 @@ void oEvent::generateVacancyList(gdioutput &gdi, GUICALLBACK cb) } - gdi.addStringUT(y, x+dx[0], 1, it->getClass()); + gdi.addStringUT(y, x+dx[0], 1, it->getClass(true)); y+=lh+lh/3; } @@ -2865,7 +2899,7 @@ void oEvent::generateInForestList(gdioutput &gdi, GUICALLBACK cb, GUICALLBACK cb } if (unknown) { - if (id != it->getClassId()) { + if (id != it->getClassId(false)) { if (nr>0) { gdi.addString("", y, x, 0, "Antal: X#"+itos(nr)); y+=lh; @@ -2876,11 +2910,11 @@ void oEvent::generateInForestList(gdioutput &gdi, GUICALLBACK cb, GUICALLBACK cb y += lh; } y += lh; - id = it->getClassId(); - gdi.addStringUT(y, x, 1, it->getClass()); + id = it->getClassId(false); + gdi.addStringUT(y, x, 1, it->getClass(false)); y += lh; } - gdi.addStringUT(y, x, 0, it->getClass()); + gdi.addStringUT(y, x, 0, it->getClass(false)); nr++; gdi.addStringUT(y, x+100, 0, it->getName(), 0, cb).setExtra(it->getId()).id = "T"; y+=lh; @@ -2920,15 +2954,15 @@ void oEvent::generateInForestList(gdioutput &gdi, GUICALLBACK cb, GUICALLBACK cb if (it->tStatus == StatusUnknown) { - if (id != it->getClassId()) { + if (id != it->getClassId(true)) { if (nr>0) { gdi.addString("", y, x, 0, "Antal: X#"+itos(nr)); y+=lh; nr=0; } y += lh; - id = it->getClassId(); - gdi.addStringUT(y, x, 1, it->getClass()); + id = it->getClassId(true); + gdi.addStringUT(y, x, 1, it->getClass(true)); y += lh; } @@ -2982,7 +3016,7 @@ void oEvent::generateInForestList(gdioutput &gdi, GUICALLBACK cb, GUICALLBACK cb gdi.addToolTip("", L"#" + punches + L". " + otherRunners, 0, &rc); } } - gdi.addStringUT(y, x+dx[3], 0, it->getClass()); + gdi.addStringUT(y, x+dx[3], 0, it->getClass(true)); y+=lh; } } @@ -3126,7 +3160,7 @@ void oEvent::generateMinuteStartlist(gdioutput &gdi) { } gdi.addStringUT(src_y, x+dx[2], fontMedium, r[0]->getClub(), dx[3]-dx[2]-4); - gdi.addStringUT(src_y, x+dx[3], fontMedium, r[0]->getClass()); + gdi.addStringUT(src_y, x+dx[3], fontMedium, r[0]->getClass(true)); y+=lh; } } @@ -3689,7 +3723,7 @@ void oEvent::reEvaluateCourse(int CourseId, bool DoSync) set classes; for(it=Runners.begin(); it != Runners.end(); ++it){ if (it->getCourse(false) && it->getCourse(false)->getId()==CourseId){ - classes.insert(it->getClassId()); + classes.insert(it->getClassId(true)); } } @@ -3710,7 +3744,7 @@ void oEvent::reEvaluateAll(const set &cls, bool doSync) } for(oTeamList::iterator tit=Teams.begin();tit!=Teams.end();++tit) { - if (!cls.empty() && cls.count(tit->getClassId()) == 0) + if (!cls.empty() && cls.count(tit->getClassId(false)) == 0) continue; tit->resetTmpStore(); @@ -3727,7 +3761,7 @@ void oEvent::reEvaluateAll(const set &cls, bool doSync) oRunnerList::iterator it; for (it=Runners.begin(); it != Runners.end(); ++it) { - if (!cls.empty() && cls.count(it->getClassId()) == 0) + if (!cls.empty() && cls.count(it->getClassId(true)) == 0) continue; if (!it->tInTeam) { @@ -3742,7 +3776,7 @@ void oEvent::reEvaluateAll(const set &cls, bool doSync) while (needupdate) { needupdate = false; for (it=Runners.begin(); it != Runners.end(); ++it) { - if (!cls.empty() && cls.count(it->getClassId()) == 0) + if (!cls.empty() && cls.count(it->getClassId(true)) == 0) continue; if (!it->isRemoved()) { @@ -3760,7 +3794,7 @@ void oEvent::reEvaluateAll(const set &cls, bool doSync) // Update team start times etc. for(oTeamList::iterator tit=Teams.begin();tit!=Teams.end();++tit) { if (!tit->isRemoved()) { - if (!cls.empty() && cls.count(tit->getClassId()) == 0) + if (!cls.empty() && cls.count(tit->getClassId(true)) == 0) continue; tit->apply(false, 0, true); @@ -3771,7 +3805,7 @@ void oEvent::reEvaluateAll(const set &cls, bool doSync) } for (it=Runners.begin(); it != Runners.end(); ++it) { if (!it->isRemoved()) { - if (!cls.empty() && cls.count(it->getClassId()) == 0) + if (!cls.empty() && cls.count(it->getClassId(true)) == 0) continue; if (!it->tInTeam) @@ -3805,7 +3839,7 @@ void oEvent::reEvaluateChanged() it->clearSplitAnalysis(); it->resetLeaderTime(); it->reinitialize(); - resetClasses[it->getId()] = it->hasClassGlobalDependance(); + resetClasses[it->getId()] = it->hasClassGlobalDependence(); } } @@ -3833,11 +3867,11 @@ void oEvent::reEvaluateChanged() for (it=Runners.begin(); it != Runners.end(); ++it) { if (it->isRemoved()) continue; - - if (resetClasses.count(it->getClassId())) + int clz = it->getClassId(true); + if (resetClasses.count(clz)) it->storeTimes(); - if (!it->wasSQLChanged() && !resetClasses[it->getClassId()]) + if (!it->wasSQLChanged() && !resetClasses[clz]) continue; pTeam t = it->tInTeam; @@ -3926,7 +3960,7 @@ void oEvent::reCalculateLeaderTimes(int classId) while (needupdate) { needupdate = false; for (oRunnerList::iterator it=Runners.begin(); it != Runners.end(); ++it) { - if (!it->isRemoved() && (classId==0 || classId==it->getClassId())) { + if (!it->isRemoved() && (classId==0 || classId==it->getClassId(true))) { if (it->tLeg == leg) it->storeTimes(); else if (it->tLeg>leg) @@ -4097,7 +4131,7 @@ int oEvent::getFirstStart(int classId) const { int minTime=3600*24; while(it!=Runners.end()){ - if (!it->isRemoved() && classId==0 || it->getClassId()==classId) + if (!it->isRemoved() && classId==0 || it->getClassId(true)==classId) if (it->tStartTime < minTime && it->tStatus!=StatusNotCompetiting && it->tStartTime>0) minTime = it->tStartTime; ++it; @@ -4168,17 +4202,26 @@ void oEvent::addBib(int ClassId, int leg, const wstring &firstNumber) { sortRunners(ClassStartTimeClub); oRunnerList::iterator it; + pClass cls = getClass(ClassId); + if (cls == 0) + throw meosException("Class not found"); + + if (cls->getParentClass()) + cls->getParentClass()->setBibMode(BibFree); + if (!firstNumber.empty()) { + cls->setBibMode(BibFree); wchar_t pattern[32]; int num = oClass::extractBibPattern(firstNumber, pattern); for (it=Runners.begin(); it != Runners.end(); ++it) { if (it->isRemoved()) continue; - if ( (ClassId==0 || it->getClassId()==ClassId) && it->legToRun()==leg) { + if ( (ClassId==0 || it->getClassId(true)==ClassId) && (it->legToRun()==leg || leg == -1)) { wchar_t bib[32]; swprintf_s(bib, pattern, num); - it->setBib(bib, num, true, false); + pClass pc = it->getClassRef(true); + it->setBib(bib, num, pc ? !pc->lockedForking() : true, false); num++; it->synchronize(); } @@ -4188,7 +4231,7 @@ void oEvent::addBib(int ClassId, int leg, const wstring &firstNumber) { for(it=Runners.begin(); it != Runners.end(); ++it){ if (it->isRemoved()) continue; - if (ClassId==0 || it->getClassId()==ClassId) { + if (ClassId==0 || it->getClassId(true)==ClassId) { it->getDI().setString("Bib", L"");//Update only bib it->synchronize(); } @@ -4206,8 +4249,8 @@ void oEvent::addBib(int ClassId, int leg, const wstring &firstNumber) { for (it=Teams.begin(); it != Teams.end(); ++it) { if (it->isRemoved()) continue; - if (ClassId==0 || it->getClassId()==ClassId) { - if (it->getClassRef() && it->getClassRef()->getBibMode() != BibFree) { + if (ClassId==0 || it->getClassId(false)==ClassId) { + if (it->getClassRef(false) && it->getClassRef(false)->getBibMode() != BibFree) { for (size_t i = 0; i < it->Runners.size(); i++) { if (it->Runners[i]) { //runnerStartNo[it->Runners[i]->getId()] = it->Runners[i]->getStartNo(); @@ -4232,7 +4275,7 @@ void oEvent::addBib(int ClassId, int leg, const wstring &firstNumber) { if (it->isRemoved()) continue; - if (ClassId == 0 || it->getClassId() == ClassId) { + if (ClassId == 0 || it->getClassId(false) == ClassId) { wchar_t bib[32]; swprintf_s(bib, pattern, num); bool lockedStartNo = it->Class && it->Class->lockedForking(); @@ -4250,7 +4293,7 @@ void oEvent::addBib(int ClassId, int leg, const wstring &firstNumber) { } else { for(it=Teams.begin(); it != Teams.end(); ++it){ - if (ClassId==0 || it->getClassId()==ClassId) { + if (ClassId==0 || it->getClassId(false)==ClassId) { it->getDI().setString("Bib", L""); //Update only bib it->apply(true, 0, false); } @@ -4265,7 +4308,7 @@ void oEvent::addAutoBib() { int clsId = -1; int bibGap = oe->getBibClassGap(); int interval = 1; - + set isTeamCls; wchar_t pattern[32] = {0}; wchar_t storedPattern[32]; wcscpy_s(storedPattern, L"%d"); @@ -4283,24 +4326,26 @@ void oEvent::addAutoBib() { for (oTeamList::iterator tit = Teams.begin(); tit != Teams.end(); ++tit) { if (tit->skip()) continue; - pClass cls = tit->getClassRef(); + pClass cls = tit->getClassRef(false); if (cls == 0) continue; teamStartNo[tit->getId()] = tit->getStartNo(); wstring bibInfo = cls->getDCI().getString("Bib"); - bool teamAssign = !bibInfo.empty(); + bool teamAssign = !bibInfo.empty() && cls->getNumStages() > 1; + bool freeMode = cls->getBibMode()==BibFree; if (!teamAssign && freeMode) continue; // Manul or none - + isTeamCls.insert(cls->getId()); + bool addBib = bibInfo != L"-"; if (addBib && teamAssign) tit->setStartNo(0, false); - if (tit->getClassRef() && tit->getClassRef()->getBibMode() != BibFree) { + if (tit->getClassRef(false) && tit->getClassRef(false)->getBibMode() != BibFree) { for (size_t i = 0; i < tit->Runners.size(); i++) { if (tit->Runners[i]) { if (addBib && teamAssign) @@ -4318,15 +4363,15 @@ void oEvent::addAutoBib() { for (oTeamList::iterator tit = Teams.begin(); tit != Teams.end(); ++tit) { if (tit->skip()) continue; - int clsId = tit->getClassId(); + int clsId = tit->getClassId(false); cls2TeamList[clsId].push_back(&*tit); } map > cls2RunnerList; for (it=Runners.begin(); it != Runners.end(); ++it) { - if (it->skip() || !it->getClassId()) + if (it->isRemoved() || !it->getClassId(false)) continue; - int clsId = it->getClassId(); + int clsId = it->getClassId(true); cls2RunnerList[clsId].push_back(&*it); } @@ -4362,7 +4407,7 @@ void oEvent::addAutoBib() { number = oClass::extractBibPattern(bibInfo, pattern); } - if (cls->getNumStages() > 1) { + if (isTeamCls.count(clsId)) { vector &tl = cls2TeamList[clsId]; if (cls->getBibMode() == BibAdd) { @@ -4405,18 +4450,23 @@ void oEvent::addAutoBib() { tl[k]->apply(true, 0, false); } } - continue; } else { interval = 1; vector &rl = cls2RunnerList[clsId]; + bool locked = cls->lockedForking(); + if (pattern[0] && cls->getParentClass()) { + // Switch to free mode if bib set for subclass + cls->getParentClass()->setBibMode(BibFree); + cls->setBibMode(BibFree); + } for (size_t k = 0; k < rl.size(); k++) { if (pattern[0]) { wchar_t buff[32]; swprintf_s(buff, pattern, number); - rl[k]->setBib(buff, number, true, false); + rl[k]->setBib(buff, number, !locked, false); number += interval; } else { @@ -4436,7 +4486,7 @@ void oEvent::checkOrderIdMultipleCourses(int ClassId) //Find first free order for(it=Runners.begin(); it != Runners.end(); ++it){ - if (ClassId==0 || it->getClassId()==ClassId){ + if (ClassId==0 || it->getClassId(false)==ClassId){ it->synchronize();//Ensure we are up-to-date order=max(order, it->StartNo); } @@ -4444,7 +4494,7 @@ void oEvent::checkOrderIdMultipleCourses(int ClassId) //Assign orders for(it=Runners.begin(); it != Runners.end(); ++it){ - if (ClassId==0 || it->getClassId()==ClassId) + if (ClassId==0 || it->getClassId(false)==ClassId) if (it->StartNo==0){ it->StartNo=++order; it->updateChanged(); //Mark as changed. @@ -4610,7 +4660,7 @@ void oEvent::loadProperties(const wchar_t *file) { bool compareClubClassTeamName(const oRunner &a, const oRunner &b) { if (a.Club==b.Club) { - if (a.Class==b.Class) { + if (a.getClassId(true) == b.getClassId(true)) { if (a.tInTeam==b.tInTeam) return a.tRealNameClass) - r+=it->getClass()+L", "; + r+=it->getClass(false)+L", "; if (it->tInTeam) { r+=itow(it->tInTeam->getStartNo()); + L" " + it->tInTeam->getName(); @@ -5345,7 +5394,7 @@ void oEvent::applyEventFees(bool updateClassFromEvent, if (it->skip()) continue; - if (allClass || classFilter.count(it->getClassId())) { + if (allClass || classFilter.count(it->getClassId(true))) { it->addClassDefaultFee(true); it->synchronize(true); } @@ -5451,7 +5500,7 @@ void oEvent::removeVacanies(int classId) { if (it->skip() || !it->isVacant()) continue; - if (classId!=0 && it->getClassId()!=classId) + if (classId!=0 && it->getClassId(false)!=classId) continue; if (!isRunnerUsed(it->Id)) @@ -5472,7 +5521,7 @@ void oEvent::sanityCheck(gdioutput &gdi, bool expectResult, int onlyThisClass) { for (oRunnerList::iterator it = Runners.begin(); it!=Runners.end(); ++it) { if (it->isRemoved()) continue; - if (onlyThisClass > 0 && it->getClassId() != onlyThisClass) + if (onlyThisClass > 0 && it->getClassId(false) != onlyThisClass) continue; if (it->sName.empty()) { if (!warnNoName) { @@ -5502,7 +5551,7 @@ void oEvent::sanityCheck(gdioutput &gdi, bool expectResult, int onlyThisClass) { if (!warnNoTeam) { gdi.alert(L"Deltagaren 'X' deltar i stafettklassen 'Y' men saknar lag. Klassens start- " L"och resultatlistor kan därmed bli felaktiga.#" + it->getName() + - L"#" + it->getClass()); + L"#" + it->getClass(false)); warnNoTeam = true; } } @@ -5510,7 +5559,7 @@ void oEvent::sanityCheck(gdioutput &gdi, bool expectResult, int onlyThisClass) { if (!warnNoPatrol) { gdi.alert(L"Deltagaren 'X' deltar i patrullklassen 'Y' men saknar patrull. Klassens start- " L"och resultatlistor kan därmed bli felaktiga.#" + it->getName() + - + L"#" + it->getClass()); + + L"#" + it->getClass(false)); warnNoPatrol = true; } } @@ -5524,7 +5573,7 @@ void oEvent::sanityCheck(gdioutput &gdi, bool expectResult, int onlyThisClass) { if (it->isRemoved()) continue; - if (onlyThisClass > 0 && it->getClassId() != onlyThisClass) + if (onlyThisClass > 0 && it->getClassId(false) != onlyThisClass) continue; if (it->sName.empty()) { @@ -5549,7 +5598,7 @@ void oEvent::sanityCheck(gdioutput &gdi, bool expectResult, int onlyThisClass) { if (type == oClassIndividual) { if (!warnIndividualTeam) { gdi.alert(L"Laget 'X' deltar i individuella klassen 'Y'. Klassens start- och resultatlistor " - L"kan därmed bli felaktiga.#" + it->getName() + L"#" + it->getClass()); + L"kan därmed bli felaktiga.#" + it->getName() + L"#" + it->getClass(true)); warnIndividualTeam = true; } } @@ -5571,6 +5620,9 @@ void oEvent::sanityCheck(gdioutput &gdi, bool expectResult, int onlyThisClass) { if (onlyThisClass > 0 && it->getId() != onlyThisClass) continue; + if (it->getQualificationFinal()) + continue; + if (it->hasMultiCourse()) { for (unsigned k=0;kgetNumStages(); k++) { StartTypes st = it->getStartType(k); @@ -5786,7 +5838,7 @@ wstring oEvent::cloneCompetition(bool cloneRunners, bool cloneTimes, r.StartNo = it->StartNo; r.CardNo = it->CardNo; r.Club = ce.getClub(it->getClubId()); - r.Class = ce.getClass(it->getClassId()); + r.Class = ce.getClass(it->getClassId(false)); if (cloneCourses) r.Course = ce.getCourse(it->getCourseId()); @@ -5819,7 +5871,7 @@ wstring oEvent::cloneCompetition(bool cloneRunners, bool cloneTimes, t.sName = it->sName; t.StartNo = it->StartNo; t.Club = ce.getClub(it->getClubId()); - t.Class = ce.getClass(it->getClassId()); + t.Class = ce.getClass(it->getClassId(false)); if (cloneTimes) t.startTime = it->startTime; @@ -5879,7 +5931,7 @@ bool checkTargetClass(pRunner target, pRunner source, if (changeClassMethod == oEvent::TransferAnyway) return true; - if (!compareClassName(target->getClass(), source->getClass())) { + if (!compareClassName(target->getClass(false), source->getClass(false))) { // Store all vacant positions in the right class int targetClass = -1; @@ -5894,10 +5946,10 @@ bool checkTargetClass(pRunner target, pRunner source, for (oClassList::const_iterator cit = Classes.begin(); cit != Classes.end(); ++cit) { if (cit->isRemoved()) continue; - if (compareClassName(cit->getName(), source->getClass())) { + if (compareClassName(cit->getName(), source->getClass(false))) { targetClass = cit->getId(); - if (targetClass == source->getClassId() || cit->getName() == source->getClass()) + if (targetClass == source->getClassId(false) || cit->getName() == source->getClass(false)) break; // Assume exact match } } @@ -5907,7 +5959,7 @@ bool checkTargetClass(pRunner target, pRunner source, for (size_t j = 0; j < targetVacant.size(); j++) { if (!targetVacant[j]) continue; - if (targetVacant[j]->getClassId() == targetClass) + if (targetVacant[j]->getClassId(false) == targetClass) vacantIx.insert(j); } int posToUse = -1; @@ -5935,12 +5987,12 @@ bool checkTargetClass(pRunner target, pRunner source, int oldStart = target->getStartTime(); wstring oldBib = target->getBib(); int oldSN = target->getStartNo(); - int oldClass = target->getClassId(); + int oldClass = target->getClassId(false); pRunner tgt = targetVacant[posToUse]; target->cloneStartTime(tgt); target->setBib(tgt->getBib(), 0, false, false); target->setStartNo(tgt->getStartNo(), false); - target->setClassId(tgt->getClassId(), false); + target->setClassId(tgt->getClassId(false), false); tgt->setStartTime(oldStart, true, false); tgt->setBib(oldBib, 0, false, false); @@ -6122,7 +6174,7 @@ void oEvent::transferResult(oEvent &ce, for (size_t j = 0; j < cnd.size(); j++) { pRunner src = remainingRunners[cnd[j]]; int p = 0; - if (src->getClass() == it->getClass()) + if (src->getClass(false) == it->getClass(false)) p += 1; if (src->getBirthYear() == it->getBirthYear()) p += 2; @@ -6165,7 +6217,7 @@ void oEvent::transferResult(oEvent &ce, } pRunner targetVacant = ce.getRunner(src->getId(), 0); - if (targetVacant && targetVacant->isVacant() && compareClassName(targetVacant->getClass(), src->getClass()) ) { + if (targetVacant && targetVacant->isVacant() && compareClassName(targetVacant->getClass(false), src->getClass(false)) ) { targetVacant->setName(src->getName(), false); targetVacant->setClub(src->getClub()); targetVacant->setCardNo(src->getCardNo(), false); @@ -6173,13 +6225,13 @@ void oEvent::transferResult(oEvent &ce, assignedVacant.push_back(targetVacant); } else { - pClass dstClass = ce.getClass(src->getClassId()); - if (dstClass && compareClassName(dstClass->getName(), src->getClass())) { - if ( (!src->hasFlag(oAbstractRunner::FlagTransferSpecified) && allowNewEntries.count(src->getClassId())) + pClass dstClass = ce.getClass(src->getClassId(false)); + if (dstClass && compareClassName(dstClass->getName(), src->getClass(false))) { + if ( (!src->hasFlag(oAbstractRunner::FlagTransferSpecified) && allowNewEntries.count(src->getClassId(false))) || src->hasFlag(oAbstractRunner::FlagTransferNew)) { if (src->getClubId() > 0) ce.getClubCreate(src->getClubId(), src->getClub()); - pRunner dst = ce.addRunner(src->getName(), src->getClubId(), src->getClassId(), + pRunner dst = ce.addRunner(src->getName(), src->getClubId(), src->getClassId(false), src->getCardNo(), src->getBirthYear(), true); dst->cloneData(src); dst->setInputData(*src); @@ -6188,7 +6240,7 @@ void oEvent::transferResult(oEvent &ce, else if (transferAllNoCompete) { if (src->getClubId() > 0) ce.getClubCreate(src->getClubId(), src->getClub()); - pRunner dst = ce.addRunner(src->getName(), src->getClubId(), src->getClassId(), + pRunner dst = ce.addRunner(src->getName(), src->getClubId(), src->getClassId(false), 0, src->getBirthYear(), true); dst->cloneData(src); dst->setInputData(*src); @@ -6313,7 +6365,7 @@ void oEvent::transferResult(oEvent &ce, for (size_t j = 0; j < cnd.size(); j++) { pTeam src = remainingTeams[cnd[j]]; int p = 0; - if (src->getClass() == it->getClass()) + if (src->getClass(false) == it->getClass(false)) p += 1; if (p > point) { winnerIx = cnd[j]; diff --git a/code/oEvent.h b/code/oEvent.h index 98a0ac2..d3be48a 100644 --- a/code/oEvent.h +++ b/code/oEvent.h @@ -447,7 +447,7 @@ protected: void exportIOFResults(xmlparser &xml, bool selfContained, const set &classes, int leg, bool oldStylePatrol); void exportTeamSplits(xmlparser &xml, const set &classes, bool oldStylePatrol); - /** Set up transient data in cassss */ + /** Set up transient data in classes */ void reinitializeClasses(); /** Analyze the result status of each class*/ @@ -779,7 +779,7 @@ public: short partialCount; short legNumber; - inline int classId() const {return r->getClassId();} + inline int classId() const {return r->getClassId(true);} inline int leg() const {return legNumber;} }; @@ -917,8 +917,7 @@ public: bool empty() const; void generateMinuteStartlist(gdioutput &gdi); - void generateMinuteStartlist(const string &file); - + bool classHasTeams(int Id) const; bool classHasResults(int Id) const; bool isCourseUsed(int Id) const; @@ -982,12 +981,14 @@ public: bool saveRunnerDatabase(const wchar_t *file, bool onlyLocal); enum ResultType {RTClassResult, RTTotalResult, RTCourseResult, RTClassCourseResult}; - void calculateResults(ResultType result); + void calculateResults(ResultType result, bool includePreliminary = false); void calculateRogainingResults(); void calculateResults(list &rl); void calculateTeamResults(bool totalMultiday); bool calculateTeamResults(int leg, bool totalMultiday); + // Set results for specified classes to tempResult + void calculateTeamResultAtControl(const set &classId, int leg, int controlId, bool totalResults); bool sortRunners(SortOrder so); /** If linear leg is true, leg is interpreted as actual leg numer, otherwise w.r.t to parallel legs. */ diff --git a/code/oEventDraw.cpp b/code/oEventDraw.cpp index 7fa6433..c0fa45d 100644 --- a/code/oEventDraw.cpp +++ b/code/oEventDraw.cpp @@ -565,13 +565,13 @@ void oEvent::optimizeStartOrder(vector< vector > > &StartField, D int relPos = relSt / di.baseInterval; if (st>0 && relSt>=0 && relPos<3000 && (relSt%di.baseInterval) == 0) { - if (otherClasses.count(it->getClassId())==0) + if (otherClasses.count(it->getClassId(false))==0) continue; if (!di.startName.empty() && it->Class && it->Class->getStart()!=di.startName) continue; - ClassInfo &ci = otherClasses[it->getClassId()]; + ClassInfo &ci = otherClasses[it->getClassId(false)]; int k = 0; while(true) { if (k==StartField.size()) { @@ -688,14 +688,18 @@ void oEvent::drawList(const vector &spec, if (spec[k].vacances>0 && pc->getClassType()==oClassRelay) throw std::exception("Vakanser stöds ej i stafett."); - if (spec[k].vacances>0 && spec[k].leg>0) + if (spec[k].vacances>0 && (spec[k].leg>0 || pc->getParentClass())) throw std::exception("Det går endast att sätta in vakanser på sträcka 1."); if (size_t(spec[k].leg) < pc->legInfo.size()) { pc->legInfo[spec[k].leg].startMethod = STDrawn; //Automatically change start method } + else if (spec[k].leg == -1) { + for (size_t j = 0; j < pc->legInfo.size(); j++) + pc->legInfo[j].startMethod = STDrawn; //Automatically change start method + } clsId2Ix[spec[k].classID] = k; - if (!multiDay && spec[k].leg == 0) + if (!multiDay && spec[k].leg == 0 && pc->getParentClass() == 0) clsIdClearVac.insert(spec[k].classID); } @@ -709,7 +713,7 @@ void oEvent::drawList(const vector &spec, vector toRemove; //Remove old vacances for (it=Runners.begin(); it != Runners.end(); ++it) { - if (clsIdClearVac.count(it->getClassId())) { + if (clsIdClearVac.count(it->getClassId(true))) { if (it->isRemoved()) continue; if (it->tInTeam) @@ -734,11 +738,12 @@ void oEvent::drawList(const vector &spec, } for (it=Runners.begin(); it != Runners.end(); ++it) { - if (!it->isRemoved() && clsId2Ix.count(it->getClassId())) { + int cid = it->getClassId(true); + if (!it->isRemoved() && clsId2Ix.count(cid)) { if (it->getStatus() == StatusNotCompetiting) continue; - int ix = clsId2Ix[it->getClassId()]; - if (it->legToRun() == spec[ix].leg ) { + int ix = clsId2Ix[cid]; + if (it->legToRun() == spec[ix].leg || spec[ix].leg == -1) { runners.push_back(&*it); spec[ix].ntimes++; } @@ -753,12 +758,12 @@ void oEvent::drawList(const vector &spec, int baseInterval = 10*60; for (it=Runners.begin(); it != Runners.end(); ++it) { - if (!it->isRemoved() && clsId2Ix.count(it->getClassId())) { + if (!it->isRemoved() && clsId2Ix.count(it->getClassId(true))) { if (it->getStatus() == StatusNotCompetiting) continue; int st = it->getStartTime(); - int ix = clsId2Ix[it->getClassId()]; + int ix = clsId2Ix[it->getClassId(false)]; if (st>0) { first[ix] = min(first[ix], st); @@ -839,7 +844,6 @@ void oEvent::drawList(const vector &spec, nextFreeStartNo = max(nextFreeStartNo, minStartNo + stimes.size()); } - void getLargestClub(map > &clubRunner, vector &largest) { size_t maxClub=0; @@ -1176,7 +1180,7 @@ void oEvent::automaticDrawAll(gdioutput &gdi, const wstring &firstStart, continue; if (it->tLeg != leg) continue; - if (it->isVacant() && notDrawn.count(it->getClassId())==1) + if (it->isVacant() && notDrawn.count(it->getClassId(false))==1) continue; pClass pc = it->Class; diff --git a/code/oEventResult.cpp b/code/oEventResult.cpp index 2b2dfcc..e15d313 100644 --- a/code/oEventResult.cpp +++ b/code/oEventResult.cpp @@ -47,7 +47,7 @@ void oEvent::calculateSplitResults(int controlIdFrom, int controlIdTo) for (it=Runners.begin(); it!=Runners.end(); ++it) { int st = 0; - if (controlIdFrom > 0) { + if (controlIdFrom > 0 && controlIdFrom != oPunch::PunchStart) { RunnerStatus stat; it->getSplitTime(controlIdFrom, stat, st); if (stat != StatusOK) { @@ -56,7 +56,7 @@ void oEvent::calculateSplitResults(int controlIdFrom, int controlIdTo) continue; } } - if (controlIdTo == 0) { + if (controlIdTo == 0 || controlIdTo == oPunch::PunchFinish) { it->tempRT = max(0, it->FinishTime - (st + it->tStartTime) ); if (it->tempRT > 0) it->tempRT += it->getTimeAdjustment(); @@ -79,8 +79,8 @@ void oEvent::calculateSplitResults(int controlIdFrom, int controlIdTo) int cTime=0; for (it=Runners.begin(); it != Runners.end(); ++it){ - if (it->getClassId()!=cClassId){ - cClassId=it->getClassId(); + if (it->getClassId(true)!=cClassId){ + cClassId=it->getClassId(true); cPlace=0; vPlace=0; cTime=0; @@ -105,7 +105,7 @@ void oEvent::calculateSplitResults(int controlIdFrom, int controlIdTo) } } -void oEvent::calculateResults(ResultType resultType) { +void oEvent::calculateResults(ResultType resultType, bool includePreliminary) { const bool totalResults = resultType == RTTotalResult; const bool courseResults = resultType == RTCourseResult; const bool classCourseResults = resultType == RTClassCourseResult; @@ -135,7 +135,7 @@ void oEvent::calculateResults(ResultType resultType) { // Start new "class" if (classCourseResults) { const pCourse crs = it->getCourse(false); - int crsId = it->getClassId() * 997 + (crs ? crs->getId() : 0); + int crsId = it->getClassId(true) * 997 + (crs ? crs->getId() : 0); if (crsId != cClassId) { cClassId = crsId; cPlace=0; @@ -156,8 +156,8 @@ void oEvent::calculateResults(ResultType resultType) { cTime=0; } } - else if (it->getClassId() != cClassId || it->tDuplicateLeg!=cDuplicateLeg || it->tLegEquClass != cLegEquClass) { - cClassId=it->getClassId(); + else if (it->getClassId(true) != cClassId || it->tDuplicateLeg!=cDuplicateLeg || it->tLegEquClass != cLegEquClass) { + cClassId=it->getClassId(true); useResults = it->Class ? !it->Class->getNoTiming() : false; cPlace=0; vPlace=0; @@ -176,7 +176,7 @@ void oEvent::calculateResults(ResultType resultType) { else if (!totalResults) { int tPlace = 0; - if (it->tStatus==StatusOK){ + if (it->tStatus==StatusOK || (includePreliminary && it->tStatus == StatusUnknown && it->FinishTime > 0)){ cPlace++; int rt = it->getRunningTime() + it->getNumShortening() * 3600 * 24* 8; @@ -200,7 +200,8 @@ void oEvent::calculateResults(ResultType resultType) { else { int tt = it->getTotalRunningTime(it->FinishTime, true); - if (it->getTotalStatus() == StatusOK && tt>0) { + RunnerStatus totStat = it->getTotalStatus(); + if (totStat == StatusOK || (includePreliminary && totStat == StatusUnknown) && tt>0) { cPlace++; if (tt > cTime) @@ -236,8 +237,8 @@ void oEvent::calculateRogainingResults() { if (it->isRemoved()) continue; - if (it->getClassId()!=cClassId || it->tDuplicateLeg!=cDuplicateLeg) { - cClassId = it->getClassId(); + if (it->getClassId(true)!=cClassId || it->tDuplicateLeg!=cDuplicateLeg) { + cClassId = it->getClassId(true); useResults = it->Class ? !it->Class->getNoTiming() : false; cPlace = 0; vPlace = 0; @@ -515,3 +516,81 @@ void oEvent::getGeneralResults(bool onlyEditable, vector< pairgetClassRef(false); + pClass ocls = o.team->getClassRef(false); + + if (cls != ocls) { + int so = cls ? cls->getSortIndex() : 0; + int oso = ocls ? ocls->getSortIndex() : 0; + if (so != oso) + return so < oso; + } + + if (status != o.status) + return status < o.status; + + if (runningTime != o.runningTime) + return runningTime < o.runningTime; + + return false; + } +}; + +void oEvent::calculateTeamResultAtControl(const set &classId, int leg, int courseControlId, bool totalResults) { + vector objs; + objs.reserve(Teams.size()); + oSpeakerObject temp; + for (auto &t : Teams) { + if (t.isRemoved()) + continue; + + if (!classId.empty() && !classId.count(t.getClassId(false))) + continue; + temp.reset(); + t.fillSpeakerObject(leg, courseControlId, -1, totalResults, temp); + if (!temp.owner) + continue; + TeamResultContainer trc; + trc.runningTime = temp.runningTime.time; + trc.status = temp.status; + trc.team = &t; + objs.push_back(trc); + } + + sort(objs.begin(), objs.end()); + + int cClass = -1; + int cPlace = -1; + int placeCounter = -1; + int cTime = 0; + for (size_t i = 0; i < objs.size(); i++) { + pTeam team = objs[i].team; + int c = team->getClassId(false); + if (c != cClass) { + cClass = c; + placeCounter = 1; + cTime = -1; + } + else { + placeCounter++; + } + + if (cTime != objs[i].runningTime) { + cPlace = placeCounter; + } + + team->tmpResult.startTime = team->getStartTime(); + team->tmpResult.status = objs[i].status; + team->tmpResult.runningTime = objs[i].runningTime; + team->tmpResult.place = objs[i].status == StatusOK ? cPlace : 0; + team->tmpResult.points = 0; // Not supported + } +} diff --git a/code/oEventSQL.cpp b/code/oEventSQL.cpp index 3a09ace..39edd2e 100644 --- a/code/oEventSQL.cpp +++ b/code/oEventSQL.cpp @@ -251,6 +251,7 @@ bool oEvent::synchronizeList(oListId id, bool preSyncEvent, bool postSyncEvent) advanceInformationPunches.clear(); if (postSyncEvent) { + reinitializeClasses(); reEvaluateChanged(); resetChangeStatus(); return true; diff --git a/code/oEventSpeaker.cpp b/code/oEventSpeaker.cpp index a530560..9e27d5a 100644 --- a/code/oEventSpeaker.cpp +++ b/code/oEventSpeaker.cpp @@ -561,7 +561,7 @@ void oEvent::speakerList(gdioutput &gdi, int ClassId, int leg, int ControlId, if (classHasTeams(ClassId)) { oTeamList::iterator it=Teams.begin(); while(it!=Teams.end()){ - if (it->getClassId()==ClassId && !it->skip()) { + if (it->getClassId(true)==ClassId && !it->skip()) { oSpeakerObject so; it->fillSpeakerObject(leg, ControlId, PreviousControlId, totalResults, so); if (so.owner) @@ -571,15 +571,23 @@ void oEvent::speakerList(gdioutput &gdi, int ClassId, int leg, int ControlId, } } else { - oRunnerList::iterator it=Runners.begin(); - while(it!=Runners.end()){ - if (it->getClassId()==ClassId && !it->skip()) { + pClass pc = getClass(ClassId); + bool qfBaseClass = pc && pc->getQualificationFinal(); + bool qfFinalClass = pc && pc->getParentClass(); + + if (qfBaseClass || qfFinalClass) + leg = 0; + + for(auto &it : Runners){ + if (it.getClassId(true) == ClassId && !it.isRemoved()) { + if (qfBaseClass && it.getLegNumber() > 0) + continue; + oSpeakerObject so; - it->fillSpeakerObject(leg, ControlId, PreviousControlId, totalResults, so); + it.fillSpeakerObject(leg, ControlId, PreviousControlId, totalResults, so); if (so.owner) speakerList.push_back(so); } - ++it; } } if (speakerList.empty()) { @@ -1089,9 +1097,9 @@ int oEvent::setupTimeLineEvents(int classId, int currentTime) if (false && startTimes.size() == 1) { oRunner &r = *started[firstNonEmpty][0]; - oTimeLine tl(r.tStartTime, oTimeLine::TLTStart, oTimeLine::PHigh, r.getClassId(), 0, 0); + oTimeLine tl(r.tStartTime, oTimeLine::TLTStart, oTimeLine::PHigh, r.getClassId(true), 0, 0); TimeLineIterator it = timeLineEvents.insert(pair(r.tStartTime, tl)); - it->second.setMessage(L"X har startat.#" + r.getClass()); + it->second.setMessage(L"X har startat.#" + r.getClass(true)); } else { for (size_t j = 0; j(r.tStartTime + 1, tl)); it->second.setMessage(L"har startat."); } else if (!startedClass) { // The entire class started - oTimeLine tl(r.tStartTime, oTimeLine::TLTStart, oTimeLine::PHigh, r.getClassId(), 0, 0); + oTimeLine tl(r.tStartTime, oTimeLine::TLTStart, oTimeLine::PHigh, r.getClassId(true), 0, 0); TimeLineIterator it = timeLineEvents.insert(pair(r.tStartTime, tl)); - it->second.setMessage(L"X har startat.#" + r.getClass()); + it->second.setMessage(L"X har startat.#" + r.getClass(true)); startedClass = true; } } @@ -1203,7 +1211,7 @@ void oEvent::timeLinePrognose(TempResultMap &results, TimeRunner &tr, int prelT, else if (p>6 + prio * 5) mp = oTimeLine::PLow; - oTimeLine tl(tr.time, oTimeLine::TLTExpected, mp, tr.runner->getClassId(), radioId, tr.runner); + oTimeLine tl(tr.time, oTimeLine::TLTExpected, mp, tr.runner->getClassId(true), radioId, tr.runner); TimeLineIterator tlit = timeLineEvents.insert(pair(tl.getTime(), tl)); tlit->second.setMessage(msg); } @@ -1411,7 +1419,7 @@ int oEvent::setupTimeLineEvents(vector &started, const vector< pair(tl.getTime(), tl)); tlit->second.setMessage(msg).setDetail(detail); } @@ -1509,7 +1517,7 @@ int oEvent::setupTimeLineEvents(vector &started, const vector< pair(tl.getTime(), tl)); tlit->second.setMessage(msg).setDetail(detail); } @@ -1535,7 +1543,7 @@ int oEvent::setupTimeLineEvents(vector &started, const vector< pair(t, tl)); wstring msg; if (r.getStatus() != StatusDQ) @@ -1558,7 +1566,7 @@ void oEvent::renderTimeLineEvents(gdioutput &gdi) const wstring msg = t>0 ? getAbsTime(t) : makeDash(L"--:--:--"); oAbstractRunner *r = it->second.getSource(*this); if (r) { - msg += L" (" + r->getClass() + L") "; + msg += L" (" + r->getClass(true) + L") "; msg += r->getName() + L", " + r->getClub(); } msg += L", " + lang.tl(it->second.getMessage()); @@ -1676,7 +1684,7 @@ void oEvent::getResultEvents(const set &classFilter, const set &punchF teamLegStatusOK.reserve(Teams.size() * 5); map teamStatusPos; for (oTeamList::const_iterator it = Teams.begin(); it != Teams.end(); ++it) { - if (!classFilter.count(it->getClassId())) + if (!classFilter.count(it->getClassId(false))) continue; int base = teamLegStatusOK.size(); @@ -1699,7 +1707,7 @@ void oEvent::getResultEvents(const set &classFilter, const set &punchF for (oRunnerList::const_iterator it = Runners.begin(); it != Runners.end(); ++it) { const oRunner &r = *it; - if (r.isRemoved() || !classFilter.count(r.getClassId())) + if (r.isRemoved() || !classFilter.count(r.getClassId(true))) continue; if (r.prelStatusOK() || r.getStatus() != StatusUnknown) { RunnerStatus stat = r.prelStatusOK() ? StatusOK : r.getStatus(); @@ -1738,7 +1746,7 @@ void oEvent::getResultEvents(const set &classFilter, const set &punchF continue; pRunner r = getRunner(fp.tRunnerId, 0); - if (r == 0 || !classFilter.count(r->getClassId()) || r->getCard()) + if (r == 0 || !classFilter.count(r->getClassId(true)) || r->getCard()) continue; int courseControlId = oFreePunch::getControlIdFromHash(fp.iHashType, true); @@ -1766,7 +1774,7 @@ void oEvent::getResultEvents(const set &classFilter, const set &punchF if (fp.isRemoved() || fp.tRunnerId == 0 || fp.Type == oPunch::PunchCheck || fp.Type == oPunch::PunchStart) continue; pRunner r = getRunner(fp.tRunnerId, 0); - if (r == 0 || !classFilter.count(r->getClassId())) + if (r == 0 || !classFilter.count(r->getClassId(true))) continue; int courseControlId = oFreePunch::getControlIdFromHash(fp.iHashType, true); int ctrl = oControl::getIdIndexFromCourseControlId(courseControlId).first; diff --git a/code/oFreeImport.cpp b/code/oFreeImport.cpp index 30d3c2f..6fcc8e9 100644 --- a/code/oFreeImport.cpp +++ b/code/oFreeImport.cpp @@ -1037,7 +1037,7 @@ void oFreeImport::test(const oRunnerList &li) givenDB.insert(it->getGivenName().c_str()); familyDB.insert(it->getFamilyName().c_str()); clubDB.insert(it->getClub().c_str()); - classDB.insert(it->getClass().c_str()); + classDB.insert(it->getClass(true).c_str()); } diff --git a/code/oFreePunch.cpp b/code/oFreePunch.cpp index b4e7cf0..69efb9f 100644 --- a/code/oFreePunch.cpp +++ b/code/oFreePunch.cpp @@ -475,14 +475,14 @@ pFreePunch oEvent::addFreePunch(int time, int type, int card, bool updateStartFi if (tr && tr->getStatus() == StatusUnknown && time > 0) { tr->synchronize(); if (type == oPunch::PunchStart) { - if (tr->getClassRef() && !tr->getClassRef()->ignoreStartPunch()) + if (tr->getClassRef(false) && !tr->getClassRef(true)->ignoreStartPunch()) tr->setStartTime(time, true, false); } else tr->setFinishTime(time); // Direct result - if (type == oPunch::PunchFinish && tr->getClassRef() && tr->getClassRef()->hasDirectResult()) { + if (type == oPunch::PunchFinish && tr->getClassRef(false) && tr->getClassRef(true)->hasDirectResult()) { if (tr->getCourse(false) == 0 && tr->getCard() == 0) { tr->setStatus(StatusOK, true, false, true); } diff --git a/code/oImportExport.cpp b/code/oImportExport.cpp index a72cc88..4b7be7e 100644 --- a/code/oImportExport.cpp +++ b/code/oImportExport.cpp @@ -218,9 +218,9 @@ bool oEvent::exportOECSV(const wchar_t *file, int languageTypeIndex, bool includ row[OEclubcity] = gdibase.recodeToNarrow(it->getClub()); } row[OEnat] = gdibase.recodeToNarrow(di.getString("Nationality")); - row[OEclassno] = conv_is(it->getClassId()); - row[OEclassshortname] = gdibase.recodeToNarrow(it->getClass()); - row[OEclassname] = gdibase.recodeToNarrow(it->getClass()); + row[OEclassno] = conv_is(it->getClassId(true)); + row[OEclassshortname] = gdibase.recodeToNarrow(it->getClass(true)); + row[OEclassname] = gdibase.recodeToNarrow(it->getClass(true)); row[OErent] = conv_is(di.getInt("CardFee")); row[OEfee] = conv_is(di.getInt("Fee")); @@ -300,7 +300,7 @@ void oEvent::importXML_EntryData(gdioutput &gdi, const wstring &file, vector< pair > runnersInTeam; for (oRunnerList::iterator it = Runners.begin(); it != Runners.end(); ++it) { if (!it->isRemoved() && it->tInTeam) { - runnersInTeam.push_back(make_pair(it->getId(), it->getClassId()) ); + runnersInTeam.push_back(make_pair(it->getId(), it->getClassId(false)) ); } } @@ -597,7 +597,7 @@ void oEvent::importXML_EntryData(gdioutput &gdi, const wstring &file, int id = runnersInTeam[k].first; int classId = runnersInTeam[k].second; pRunner r = getRunner(id, 0); - if (r && !r->tInTeam && r->getClassId() == classId) { + if (r && !r->tInTeam && r->getClassId(false) == classId) { toRemove.push_back(r->getId()); } } @@ -2048,7 +2048,7 @@ void oClass::exportIOFStart(xmlparser &xml) { if (getClassType() == oClassIndividual || getClassType() == oClassIndividRelay) { for (oRunnerList::iterator it = oe->Runners.begin(); it!=oe->Runners.end(); ++it) { - if (it->getClassId() != getId() || it->isRemoved()) + if (it->getClassId(true) != getId() || it->isRemoved()) continue; xml.startTag("PersonStart"); @@ -2096,7 +2096,7 @@ void oClass::exportIOFStart(xmlparser &xml) { bool writeTeamName = !useEventor || getClassType() != oClassPatrol; for (oTeamList::iterator it = oe->Teams.begin(); it!=oe->Teams.end(); ++it) { - if (it->getClassId() != getId() || it->isRemoved()) + if (it->getClassId(true) != getId() || it->isRemoved()) continue; xml.startTag("TeamStart"); @@ -2218,7 +2218,7 @@ void oEvent::exportIOFResults(xmlparser &xml, bool selfContained, const set if (it->isRemoved()) continue; if (it->Runners.size()>=2 && it->Runners[0] && it->Runners[1]) { - if (it->getClassId()!=Id) { + if (it->getClassId(true)!=Id) { if (ClassStarted) xml.endTag(); if (!it->Class || it->Class->getClassType()!=oClassPatrol) { @@ -2227,7 +2227,7 @@ void oEvent::exportIOFResults(xmlparser &xml, bool selfContained, const set continue; } - if ((!classes.empty() && classes.count(it->getClassId()) == 0) || leg != -1) { + if ((!classes.empty() && classes.count(it->getClassId(true)) == 0) || leg != -1) { skipClass=true; ClassStarted=false; continue; @@ -2236,9 +2236,9 @@ void oEvent::exportIOFResults(xmlparser &xml, bool selfContained, const set skipClass=false; xml.startTag("ClassResult"); ClassStarted=true; - Id=it->getClassId(); + Id=it->getClassId(true); - xml.write("ClassShortName", it->getClass()); + xml.write("ClassShortName", it->getClass(true)); } if (skipClass) @@ -2315,7 +2315,7 @@ void oEvent::exportIOFResults(xmlparser &xml, bool selfContained, const set if (it->isRemoved() || (leg != -1 && it->tLeg != leg) || it->isVacant()) continue; - if (it->getClassId()!=Id) { + if (it->getClassId(true)!=Id) { if (ClassStarted) xml.endTag(); if (!it->Class) { @@ -2332,7 +2332,7 @@ void oEvent::exportIOFResults(xmlparser &xml, bool selfContained, const set continue; } - if ( (!classes.empty() && classes.count(it->getClassId()) == 0) ) { + if ( (!classes.empty() && classes.count(it->getClassId(true)) == 0) ) { skipClass=true; ClassStarted=false; continue; @@ -2341,9 +2341,9 @@ void oEvent::exportIOFResults(xmlparser &xml, bool selfContained, const set xml.startTag("ClassResult"); ClassStarted=true; skipClass=false; - Id=it->getClassId(); + Id=it->getClassId(true); - xml.write("ClassShortName", it->getClass()); + xml.write("ClassShortName", it->getClass(true)); } if (skipClass) @@ -2419,7 +2419,7 @@ void oEvent::exportTeamSplits(xmlparser &xml, const set &classes, bool oldS for(oTeamList::iterator it=Teams.begin(); it != Teams.end(); ++it) { if (it->isRemoved()) continue; - if (it->getClassId()!=Id) { + if (it->getClassId(true)!=Id) { if (ClassStarted) { xml.endTag(); ClassStarted = false; @@ -2442,7 +2442,7 @@ void oEvent::exportTeamSplits(xmlparser &xml, const set &classes, bool oldS continue; } - if (!classes.empty() && classes.count(it->getClassId()) == 0) { + if (!classes.empty() && classes.count(it->getClassId(true)) == 0) { skipClass=true; continue; } @@ -2450,9 +2450,9 @@ void oEvent::exportTeamSplits(xmlparser &xml, const set &classes, bool oldS skipClass=false; xml.startTag("ClassResult"); ClassStarted=true; - Id=it->getClassId(); + Id=it->getClassId(true); - xml.write("ClassShortName", it->getClass()); + xml.write("ClassShortName", it->getClass(true)); } if (skipClass) diff --git a/code/oListInfo.cpp b/code/oListInfo.cpp index 2b437b3..1e51fd1 100644 --- a/code/oListInfo.cpp +++ b/code/oListInfo.cpp @@ -396,9 +396,9 @@ int oListInfo::getMaxCharWidth(const oEvent *oe, continue; // Case when runner/team has different class - bool teamOK = it->getTeam() && clsSel.count(it->getTeam()->getClassId()); + bool teamOK = it->getTeam() && clsSel.count(it->getTeam()->getClassId(false)); - if (!clsSel.empty() && (!teamOK && clsSel.count(it->getClassId()) == 0)) + if (!clsSel.empty() && (!teamOK && clsSel.count(it->getClassId(true)) == 0)) continue; totWord.clear(); @@ -417,7 +417,7 @@ int oListInfo::getMaxCharWidth(const oEvent *oe, } rout.clear(); while (numIter-- > 0) { - const wstring &out = oe->formatListString(pp, par, it->tInTeam, pRunner(&*it), it->Club, it->Class, c); + const wstring &out = oe->formatListString(pp, par, it->tInTeam, pRunner(&*it), it->Club, pClass(it->getClassRef(true)), c); //row[k] = max(row[k], int(out.length())); if (out.length() > rout.length()) rout = out; @@ -474,7 +474,7 @@ const wstring & oEvent::formatListString(EPostType type, const pRunner r) const oListParam par; par.setLegNumberCoded(r->tLeg); pp.type = type; - return formatListString(pp, par, r->tInTeam, r, r->Club, r->Class, ctr); + return formatListString(pp, par, r->tInTeam, r, r->Club, r->getClassRef(true), ctr); } const wstring & oEvent::formatListString(EPostType type, const pRunner r, @@ -485,7 +485,7 @@ const wstring & oEvent::formatListString(EPostType type, const pRunner r, par.setLegNumberCoded(r->tLeg); pp.type = type; pp.text = format; - return formatListString(pp, par, r->tInTeam, r, r->Club, r->Class, ctr); + return formatListString(pp, par, r->tInTeam, r, r->Club, r->getClassRef(true), ctr); } @@ -572,16 +572,16 @@ const wstring &oEvent::formatSpecialStringAux(const oPrintPost &pp, const oListP break; case lRunnerLegNumberAlpha: - if (t && t->getClassRef() && legIndex >= 0) { - wstring legStr = t->getClassRef()->getLegNumber(legIndex); + if (t && t->getClassRef(false) && legIndex >= 0) { + wstring legStr = t->getClassRef(false)->getLegNumber(legIndex); wcscpy_s(bfw, legStr.c_str()); } break; case lRunnerLegNumber: - if (t && t->getClassRef() && legIndex >= 0) { + if (t && t->getClassRef(false) && legIndex >= 0) { int legNumber, legOrder; - t->getClassRef()->splitLegNumberParallel(legIndex, legNumber, legOrder); + t->getClassRef(false)->splitLegNumberParallel(legIndex, legNumber, legOrder); wsptr = &itow(legNumber+1); } break; @@ -727,7 +727,7 @@ const wstring &oEvent::formatSpecialStringAux(const oPrintPost &pp, const oListP try { const wstring &res = formatListStringAux(pp, par, t, t ? t->getRunner(legIndex) : 0, t ? t->getClubRef() : 0, - t ? t->getClassRef() : 0, counter); + t ? t->getClassRef(false) : 0, counter); reentrantLock = false; return res; } @@ -928,7 +928,7 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara if (t) { pRunner r1 = t->getRunner(0); pRunner r2 = t->getRunner(1); - if (r1 && r2) { + if (r1 && r2 && r2->tParentRunner != r1) { swprintf_s(wbf, L"%s / %s", r1->getName().c_str(),r2->getName().c_str()); } else if (r1) { @@ -1375,7 +1375,7 @@ const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListPara break; case lRunnerCard: - if (r && r->CardNo > 0) + if (r && r->getCardNo() > 0) swprintf_s(wbf, L"%d", r->getCardNo()); break; case lRunnerRank: @@ -2212,7 +2212,7 @@ void oEvent::generateListInternal(gdioutput &gdi, const oListInfo &li, bool form if (it->isRemoved() || it->tStatus == StatusNotCompetiting) continue; - if (!li.lp.selection.empty() && li.lp.selection.count(it->getClassId())==0) + if (!li.lp.selection.empty() && li.lp.selection.count(it->getClassId(false))==0) continue; it->apply(false, 0, true); } @@ -2263,11 +2263,10 @@ void oEvent::generateListInternal(gdioutput &gdi, const oListInfo &li, bool form if (it->isRemoved() || it->tStatus == StatusNotCompetiting) continue; - if (!li.lp.selection.empty() && li.lp.selection.count(it->getClassId())==0) + if (!li.lp.selection.empty() && li.lp.selection.count(it->getClassId(true))==0) continue; - //if (it->legToRun() != li.lp.legNumber && li.lp.legNumber!=-1) - if (!li.lp.matchLegNumber(it->getClassRef(), it->legToRun())) + if (!li.lp.matchLegNumber(it->getClassRef(false), it->legToRun())) continue; if (li.filter(EFilterExcludeDNS)) @@ -2348,29 +2347,29 @@ void oEvent::generateListInternal(gdioutput &gdi, const oListInfo &li, bool form wstring newKey; printPostInfo.par.relayLegIndex = -1; - calculatePrintPostKey(li.subHead, gdi, li.lp, it->tInTeam, &*it, it->Club, it->Class, printPostInfo.counter, newKey); + calculatePrintPostKey(li.subHead, gdi, li.lp, it->tInTeam, &*it, it->Club, it->getClassRef(true), printPostInfo.counter, newKey); if (newKey != oldKey) { if (li.lp.pageBreak) { if (!oldKey.empty()) gdi.addStringUT(gdi.getCY()-1, 0, pageNewPage, ""); } - gdi.addStringUT(pagePageInfo, it->getClass()); + gdi.addStringUT(pagePageInfo, it->getClass(true)); oldKey.swap(newKey); printPostInfo.counter.level2=0; printPostInfo.counter.level3=0; printPostInfo.reset(); printPostInfo.par.relayLegIndex = -1; - formatPrintPost(li.subHead, printPostInfo, it->tInTeam, &*it, it->Club, it->Class, 0, 0, -1); + formatPrintPost(li.subHead, printPostInfo, it->tInTeam, &*it, it->Club, it->getClassRef(true), 0, 0, -1); } if (li.lp.filterMaxPer==0 || printPostInfo.counter.level2tLeg; - formatPrintPost(li.listPost, printPostInfo, it->tInTeam, &*it, it->Club, it->Class, 0, 0, -1); + formatPrintPost(li.listPost, printPostInfo, it->tInTeam, &*it, it->Club, it->getClassRef(true), 0, 0, -1); if (li.listSubType==li.EBaseTypePunches) { - listGeneratePunches(li.subListPost, gdi, li.lp, it->tInTeam, &*it, it->Club, it->Class); + listGeneratePunches(li.subListPost, gdi, li.lp, it->tInTeam, &*it, it->Club, it->getClassRef(true)); } } ++printPostInfo.counter; @@ -2397,7 +2396,7 @@ void oEvent::generateListInternal(gdioutput &gdi, const oListInfo &li, bool form if (it->isRemoved() || it->tStatus == StatusNotCompetiting) continue; - if (!li.lp.selection.empty() && li.lp.selection.count(it->getClassId())==0) + if (!li.lp.selection.empty() && li.lp.selection.count(it->getClassId(true))==0) continue; tlist.push_back(&*it); } @@ -2416,7 +2415,7 @@ void oEvent::generateListInternal(gdioutput &gdi, const oListInfo &li, bool form for (size_t k = 0; k < tlist.size(); k++) { pTeam it = tlist[k]; - int linearLegSpec = li.lp.getLegNumber(it->getClassRef()); + int linearLegSpec = li.lp.getLegNumber(it->getClassRef(false)); if (gResult && it->getTempResult(0).getStatus() == StatusNotCompetiting) continue; @@ -2475,12 +2474,12 @@ void oEvent::generateListInternal(gdioutput &gdi, const oListInfo &li, bool form gdi.addStringUT(gdi.getCY()-1, 0, pageNewPage, ""); } wstring legInfo; - if (linearLegSpec >= 0 && it->getClassRef()) { + if (linearLegSpec >= 0 && it->getClassRef(false)) { // Specified leg legInfo = lang.tl(L", Str. X#" + li.lp.getLegName()); } - gdi.addStringUT(pagePageInfo, it->getClass() + legInfo); // Teamlist + gdi.addStringUT(pagePageInfo, it->getClass(true) + legInfo); // Teamlist oldKey.swap(newKey); printPostInfo.counter.level2=0; @@ -2561,7 +2560,7 @@ void oEvent::generateListInternal(gdioutput &gdi, const oListInfo &li, bool form printPostInfo.keepToghether = suitableBreak; printPostInfo.par.relayLegIndex = tr[k] ? tr[k]->tLeg : -1; formatPrintPost(li.subListPost, printPostInfo, &*it, tr[k], - it->Club, tr[k]->Class, 0, 0, -1); + it->Club, tr[k]->getClassRef(true), 0, 0, -1); printPostInfo.counter.level3++; } } @@ -2579,7 +2578,7 @@ void oEvent::generateListInternal(gdioutput &gdi, const oListInfo &li, bool form } else { formatPrintPost(li.subListPost, printPostInfo, &*it, tr[usedIx[k]], - it->Club, tr[usedIx[k]]->Class, 0, 0, -1); + it->Club, tr[usedIx[k]]->getClassRef(true), 0, 0, -1); } printPostInfo.counter.level3++; } @@ -2632,16 +2631,15 @@ void oEvent::generateListInternal(gdioutput &gdi, const oListInfo &li, bool form } bool startClub = false; - pRunner pLeader = 0; for (rit = Runners.begin(); rit != Runners.end(); ++rit) { if (rit->isRemoved() || rit->tStatus == StatusNotCompetiting) continue; - if (!li.lp.selection.empty() && li.lp.selection.count(rit->getClassId())==0) + if (!li.lp.selection.empty() && li.lp.selection.count(rit->getClassId(true))==0) continue; - if (!li.lp.matchLegNumber(rit->getClassRef(), rit->legToRun())) + if (!li.lp.matchLegNumber(rit->getClassRef(false), rit->legToRun())) continue; if (li.filter(EFilterExcludeDNS)) @@ -2657,8 +2655,6 @@ void oEvent::generateListInternal(gdioutput &gdi, const oListInfo &li, bool form continue; } - if (!pLeader || pLeader->Class != rit->Class) - pLeader = &*rit; if (rit->Club == &*it) { if (!startClub) { if (li.lp.pageBreak) { @@ -2680,7 +2676,7 @@ void oEvent::generateListInternal(gdioutput &gdi, const oListInfo &li, bool form printPostInfo.counter.level3=0; printPostInfo.reset(); printPostInfo.par.relayLegIndex = rit->tLeg; - formatPrintPost(li.listPost, printPostInfo, 0, &*rit, &*it, rit->Class, 0, 0, -1); + formatPrintPost(li.listPost, printPostInfo, 0, &*rit, &*it, rit->getClassRef(true), 0, 0, -1); if (li.subListPost.empty()) continue; } diff --git a/code/oListInfo.h b/code/oListInfo.h index 514928b..8ee52b6 100644 --- a/code/oListInfo.h +++ b/code/oListInfo.h @@ -294,6 +294,24 @@ class MetaListContainer; struct oListParam { oListParam(); + + bool operator==(const oListParam& a) const { + return a.listCode == listCode && + a.selection == selection && + a.useControlIdResultFrom == useControlIdResultFrom && + a.useControlIdResultTo == useControlIdResultTo && + a.filterMaxPer == filterMaxPer && + a.pageBreak == pageBreak && + a.showInterTimes == showInterTimes && + a.showSplitTimes == showSplitTimes && + a.inputNumber == inputNumber && + a.nextList == nextList && + a.previousList == previousList && + a.bgColor == bgColor && + a.bgColor2 == bgColor2 && + a.bgImage == bgImage && + a.legNumber == legNumber; + } EStdListType listCode; GUICALLBACK cb; set selection; @@ -389,6 +407,7 @@ public: Global, Classwise, Legwise, + Coursewise }; static bool addRunners(EBaseType t) {return t == EBaseTypeRunner || t == EBaseTypeClub;} diff --git a/code/oReport.cpp b/code/oReport.cpp index 761badb..29f2310 100644 --- a/code/oReport.cpp +++ b/code/oReport.cpp @@ -405,7 +405,7 @@ void oEvent::generatePreReport(gdioutput &gdi) { no_club.push_back(&*r_it); } - if (r_it->getClassId()==0) + if (r_it->getClassId(false)==0) no_class.push_back(&*r_it); else if (needCourse && r_it->getCourse(false)==0) no_course.push_back(&*r_it); @@ -481,7 +481,7 @@ void oEvent::generatePreReport(gdioutput &gdi) { while(!no_course.empty() && ++i<20){ pRunner r=no_course.front(); no_course.pop_front(); - wstring name = r->getClass() + L": " + r->getName(); + wstring name = r->getClass(true) + L": " + r->getName(); if (!r->getClub().empty()) name += L" ("+ r->getClub()+ L")"; gdi.addStringUT(0, name); @@ -497,7 +497,7 @@ void oEvent::generatePreReport(gdioutput &gdi) { while(!no_club.empty() && ++i<20){ pRunner r=no_club.front(); no_club.pop_front(); - gdi.addStringUT(0, r->getClass() + L": " + r->getName()); + gdi.addStringUT(0, r->getClass(true) + L": " + r->getName()); } if (!no_club.empty()) gdi.addStringUT(1, Ellipsis); } @@ -510,7 +510,7 @@ void oEvent::generatePreReport(gdioutput &gdi) { while(!no_start.empty() && ++i<20){ pRunner r=no_start.front(); no_start.pop_front(); - wstring name = r->getClass() + L": " + r->getName(); + wstring name = r->getClass(true) + L": " + r->getName(); if (!r->getClub().empty()) name += L" (" + r->getClub() + L")"; @@ -526,7 +526,7 @@ void oEvent::generatePreReport(gdioutput &gdi) { while(!no_card.empty() && ++i<20){ pRunner r=no_card.front(); no_card.pop_front(); - wstring name = r->getClass() + L": " + r->getName(); + wstring name = r->getClass(true) + L": " + r->getName(); if (!r->getClub().empty()) name += L" (" + r->getClub() + L")"; @@ -543,7 +543,7 @@ void oEvent::generatePreReport(gdioutput &gdi) { while(!si_duplicate.empty() && ++i<50){ pRunner r=si_duplicate.front(); si_duplicate.pop_front(); - wstring name = r->getClass() + L" / " + r->getName(); + wstring name = r->getClass(true) + L" / " + r->getName(); if (!r->getClub().empty()) name += L" (" + r->getClub() + L")"; name += L": " + itow(r->getCardNo()); @@ -566,7 +566,7 @@ void oEvent::generatePreReport(gdioutput &gdi) { header = true; } - wstring name = r->getClass() + L" / " + r->getName(); + wstring name = r->getClass(true) + L" / " + r->getName(); if (!r->getClub().empty()) name += L" (" + r->getClub() + L")"; name += L": " + itow(r->getCardNo()); @@ -589,7 +589,7 @@ void oEvent::generatePreReport(gdioutput &gdi) { for (t_it=Teams.begin(); t_it != Teams.end(); ++t_it) { if (t_it->isRemoved()) continue; - pClass pc=getClass(t_it->getClassId()); + pClass pc=getClass(t_it->getClassId(true)); if (pc){ for(unsigned i=0;igetNumStages();i++){ @@ -604,7 +604,7 @@ void oEvent::generatePreReport(gdioutput &gdi) { bool any = false; for (r_it=Runners.begin(); r_it != Runners.end(); ++r_it){ if (r_it->_objectmarker>1) { - wstring name = r_it->getClass() + L": " + r_it->getName(); + wstring name = r_it->getClass(true) + L": " + r_it->getName(); if (!r_it->getClub().empty()) name += L" (" + r_it->getClub() + L")"; @@ -626,7 +626,7 @@ void oEvent::generatePreReport(gdioutput &gdi) { if (r_it->isRemoved()) continue; if (r_it->_objectmarker==0){ //Only consider runners not in a team. - gdi.addStringUT(y, x+tab[0], 0, r_it->getClass(), tab[1]-tab[0]); + gdi.addStringUT(y, x+tab[0], 0, r_it->getClass(true), tab[1]-tab[0]); wstring name = r_it->getName(); if (!r_it->getClub().empty()) name += L" (" + r_it->getClub() + L")"; @@ -650,9 +650,9 @@ void oEvent::generatePreReport(gdioutput &gdi) { gdi.addString("", 1, "Lag(flera)"); for (t_it=Teams.begin(); t_it != Teams.end(); ++t_it){ - pClass pc=getClass(t_it->getClassId()); + pClass pc=getClass(t_it->getClassId(false)); - gdi.addStringUT(0, t_it->getClass() + L": " + t_it->getName() + L" " + t_it->getStartTimeS()); + gdi.addStringUT(0, t_it->getClass(false) + L": " + t_it->getName() + L" " + t_it->getStartTimeS()); if (pc){ for(unsigned i=0;igetNumStages();i++){ diff --git a/code/oRunner.cpp b/code/oRunner.cpp index e740300..9330db3 100644 --- a/code/oRunner.cpp +++ b/code/oRunner.cpp @@ -44,6 +44,7 @@ #include "socket.h" #include "MeOSFeatures.h" #include "oListInfo.h" +#include "qualification_final.h" oRunner::RaceIdFormatter oRunner::raceIdFormatter; @@ -420,8 +421,27 @@ void oAbstractRunner::setClassId(int id, bool isManualUpdate) { // Update all classes (for multirunner) void oRunner::setClassId(int id, bool isManualUpdate) { - if (tParentRunner) - tParentRunner->setClassId(id, isManualUpdate); + if (Class && Class->getQualificationFinal() && isManualUpdate) { + int heat = Class->getQualificationFinal()->getHeatFromClass(id, Class->getId()); + if (heat >= 0) { + int oldHeat = getDI().getInt("Heat"); + + if (heat != oldHeat) { + pClass oldHeatClass = getClassRef(true); + getDI().setInt("Heat", heat); + pClass newHeatClass = getClassRef(true); + oldHeatClass->clearCache(true); + newHeatClass->clearCache(true); + tSplitRevision = 0; + } + } + return; + } + + if (tParentRunner) { + assert(!isManualUpdate); // Do not support! This may be destroyed if calling tParentRunner->setClass + return; + } else { pClass pc = Class; pClass nPc = id>0 ? oe->getClass(id):0; @@ -451,7 +471,7 @@ void oRunner::setClassId(int id, bool isManualUpdate) if (Class != 0 && Class != pc && tInTeam==0 && Class->isSingleRunnerMultiStage()) { if (!isTemporaryObject) { - pTeam t = oe->addTeam(getName(), getClubId(), getClassId()); + pTeam t = oe->addTeam(getName(), getClubId(), getClassId(false)); t->setStartNo(StartNo, false); t->setRunner(0, this, true); } @@ -765,91 +785,89 @@ pCourse oRunner::getCourse(bool useAdaptedCourse) const { if (Course) tCrs = Course; else if (Class) { - if (Class->hasMultiCourse()) { + const oClass *cls = getClassRef(true); + + if (cls->hasMultiCourse()) { if (tInTeam) { if (size_t(tLeg) >= tInTeam->Runners.size() || tInTeam->Runners[tLeg] != this) { tInTeam->quickApply(); } } + + if (Class == cls) { + if (tInTeam && Class->hasUnorderedLegs()) { + vector< pair > group; + Class->getParallelCourseGroup(tLeg, StartNo, group); - if (tInTeam && Class->hasUnorderedLegs()) { - vector< pair > group; - Class->getParallelCourseGroup(tLeg, StartNo, group); - - if (group.size() == 1) { - tCrs = group[0].second; + if (group.size() == 1) { + tCrs = group[0].second; + } + else { + // Remove used courses + int myStart = 0; + + for (size_t k = 0; k < group.size(); k++) { + if (group[k].first == tLeg) + myStart = k; + + pRunner tr = tInTeam->getRunner(group[k].first); + if (tr && tr->Course) { + // The course is assigned. Remove from group + for (size_t j = 0; j < group.size(); j++) { + if (group[j].second == tr->Course) { + group[j].second = 0; + break; + } + } + } + } + + // Clear out already preliminary assigned courses + for (int k = 0; k < myStart; k++) { + pRunner r = tInTeam->getRunner(group[k].first); + if (r && !r->Course) { + size_t j = k; + while (j < group.size()) { + if (group[j].second) { + group[j].second = 0; + break; + } + else j++; + } + } + } + + for (size_t j = 0; j < group.size(); j++) { + int ix = (j + myStart) % group.size(); + pCourse gcrs = group[ix].second; + if (gcrs) { + tCrs = gcrs; + break; + } + } + } + } + else if (tInTeam) { + unsigned leg = legToRun(); + tCrs = Class->getCourse(leg, StartNo); } else { - // Remove used courses - int myStart = 0; - - for (size_t k = 0; k < group.size(); k++) { - if (group[k].first == tLeg) - myStart = k; - - pRunner tr = tInTeam->getRunner(group[k].first); - if (tr && tr->Course) { - // The course is assigned. Remove from group - for (size_t j = 0; j < group.size(); j++) { - if (group[j].second == tr->Course) { - group[j].second = 0; - break; - } - } - } - } - - // Clear out already preliminary assigned courses - for (int k = 0; k < myStart; k++) { - pRunner r = tInTeam->getRunner(group[k].first); - if (r && !r->Course) { - size_t j = k; - while (j < group.size()) { - if (group[j].second) { - group[j].second = 0; - break; - } - else j++; - } - } - } - - for (size_t j = 0; j < group.size(); j++) { - int ix = (j + myStart) % group.size(); - pCourse gcrs = group[ix].second; - if (gcrs) { - tCrs = gcrs; - break; + if (unsigned(tDuplicateLeg) < Class->MultiCourse.size()) { + vector &courses = Class->MultiCourse[tDuplicateLeg]; + if (courses.size() > 0) { + int index = StartNo % courses.size(); + tCrs = courses[index]; } } } } - else if (tInTeam) { - unsigned leg=legToRun(); - tCrs = Class->getCourse(leg, StartNo); - /*if (legMultiCourse.size()) { - vector &courses=Class->MultiCourse[leg]; - if (courses.size()>0) { - int index=StartNo; - if (index>0) - index = (index-1) % courses.size(); - - tCrs = courses[index]; - } - }*/ - } else { - if (unsigned(tDuplicateLeg)MultiCourse.size()) { - vector &courses=Class->MultiCourse[tDuplicateLeg]; - if (courses.size()>0) { - int index=StartNo % courses.size(); - tCrs = courses[index]; - } - } + // Final / qualification classes + tCrs = cls->getCourse(0, StartNo); } } else - tCrs = Class->Course; + tCrs = cls->Course; } if (tCrs && useAdaptedCourse) { @@ -1329,6 +1347,19 @@ bool oRunner::evaluateCard(bool doApply, vector & MissingPunches, doAdjustTimes(course); tRogainingPointsGross = tRogainingPoints; + + if (oldStatus!=*refStatus || oldFT!=FinishTime) { + clearSplitAnalysis = true; + } + + if ((inTeam || !tUseStartPunch) && doApply) + apply(sync, 0, false); //Post apply. Update start times. + + if (tCachedRunningTime != FinishTime - *refStartTime) { + tCachedRunningTime = FinishTime - *refStartTime; + clearSplitAnalysis = true; + } + if (time_limit > 0) { int rt = getRunningTime(); if (rt > 0) { @@ -1342,17 +1373,7 @@ bool oRunner::evaluateCard(bool doApply, vector & MissingPunches, } } - if (oldStatus!=*refStatus || oldFT!=FinishTime) { - clearSplitAnalysis = true; - } - if ((inTeam || !tUseStartPunch) && doApply) - apply(sync, 0, false); //Post apply. Update start times. - - if (tCachedRunningTime != FinishTime - *refStartTime) { - tCachedRunningTime = FinishTime - *refStartTime; - clearSplitAnalysis = true; - } // Clear split analysis data if necessary bool clear = splitTimes.size() != oldTimes.size() || clearSplitAnalysis; @@ -1650,8 +1671,8 @@ int oRunner::getRaceRunningTime(int leg) const bool oRunner::sortSplit(const oRunner &a, const oRunner &b) { - int acid=a.getClassId(); - int bcid=b.getClassId(); + int acid=a.getClassId(true); + int bcid=b.getClassId(true); if (acid!=bcid) return acidgetClassStatus() != oClass::Normal) +bool oRunner::operator<(const oRunner &c) const { + const oClass * myClass = getClassRef(true); + const oClass * cClass = c.getClassRef(true); + if (!myClass || !cClass) + return size_t(myClass)getClassStatus() != oClass::Normal) return CompareString(LOCALE_USER_DEFAULT, 0, tRealName.c_str(), tRealName.length(), c.tRealName.c_str(), c.tRealName.length()) == CSTR_LESS_THAN; if (oe->CurrentSortOrder==ClassStartTime) { - if (Class->Id != c.Class->Id) - return Class->tSortIndex < c.Class->tSortIndex; + if (myClass->Id != cClass->Id) { + if (myClass->tSortIndex != cClass->tSortIndex) + return myClass->tSortIndex < cClass->tSortIndex; + else + return myClass->Id < cClass->Id; + } else if (tStartTime != c.tStartTime) { if (tStartTime <= 0 && c.tStartTime > 0) return false; @@ -1697,8 +1726,8 @@ bool oRunner::operator <(const oRunner &c) { RunnerStatus stat = tStatus == StatusUnknown ? StatusOK : tStatus; RunnerStatus cstat = c.tStatus == StatusUnknown ? StatusOK : c.tStatus; - if (Class != c.Class) - return Class->tSortIndex < c.Class->tSortIndex; + if (myClass != cClass) + return myClass->tSortIndex < cClass->tSortIndex || (myClass->tSortIndex == cClass->tSortIndex && myClass->Id < cClass->Id); else if (tLegEquClass != c.tLegEquClass) return tLegEquClass < c.tLegEquClass; else if (tDuplicateLeg != c.tDuplicateLeg) @@ -1730,8 +1759,8 @@ bool oRunner::operator <(const oRunner &c) { } } else if (oe->CurrentSortOrder == ClassCourseResult) { - if (Class != c.Class) - return Class->tSortIndex < c.Class->tSortIndex; + if (myClass != cClass) + return myClass->tSortIndex < cClass->tSortIndex; const pCourse crs1 = getCourse(false); const pCourse crs2 = c.getCourse(false); @@ -1805,8 +1834,8 @@ bool oRunner::operator <(const oRunner &c) { return ft > cft; } else if (oe->CurrentSortOrder == ClassFinishTime){ - if (Class != c.Class) - return Class->tSortIndex < c.Class->tSortIndex; + if (myClass != cClass) + return myClass->tSortIndex < cClass->tSortIndex || (myClass->tSortIndex == cClass->tSortIndex && myClass->Id < cClass->Id); if (tStatus != c.tStatus) return RunnerStatusOrderMap[tStatus] < RunnerStatusOrderMap[c.tStatus]; else{ @@ -1834,8 +1863,8 @@ bool oRunner::operator <(const oRunner &c) { return et > cet; } else if (oe->CurrentSortOrder == ClassPoints) { - if (Class != c.Class) - return Class->tSortIndex < c.Class->tSortIndex; + if (myClass != cClass) + return myClass->tSortIndex < cClass->tSortIndex || (myClass->tSortIndex == cClass->tSortIndex && myClass->Id < cClass->Id); else if (tDuplicateLeg != c.tDuplicateLeg) return tDuplicateLeg < c.tDuplicateLeg; else if (tStatus != c.tStatus) @@ -1852,8 +1881,8 @@ bool oRunner::operator <(const oRunner &c) { } } else if (oe->CurrentSortOrder==ClassTotalResult) { - if (Class != c.Class) - return Class->tSortIndex < c.Class->tSortIndex; + if (myClass != cClass) + return myClass->tSortIndex < cClass->tSortIndex || (myClass->tSortIndex == cClass->tSortIndex && myClass->Id < cClass->Id); else if (tDuplicateLeg != c.tDuplicateLeg) return tDuplicateLeg < c.tDuplicateLeg; else { @@ -1902,8 +1931,8 @@ bool oRunner::operator <(const oRunner &c) { } } else if (oe->CurrentSortOrder==ClassStartTimeClub) { - if (Class != c.Class) - return Class->tSortIndex < c.Class->tSortIndex; + if (myClass != cClass) + return myClass->tSortIndex < cClass->tSortIndex || (myClass->tSortIndex == cClass->tSortIndex && myClass->Id < cClass->Id); else if (tStartTime != c.tStartTime) { if (tStartTime <= 0 && c.tStartTime > 0) return false; @@ -1916,8 +1945,8 @@ bool oRunner::operator <(const oRunner &c) { } } else if (oe->CurrentSortOrder==ClassTeamLeg) { - if (Class->Id != c.Class->Id) - return Class->tSortIndex < c.Class->tSortIndex; + if (myClass->Id != cClass->Id) + return myClass->tSortIndex < cClass->tSortIndex || (myClass->tSortIndex == cClass->tSortIndex && myClass->Id < cClass->Id); else if (tInTeam != c.tInTeam) { if (tInTeam == 0) return true; @@ -1959,7 +1988,7 @@ void oAbstractRunner::setClub(const wstring &clubName) updateChanged(); if (Class) { // Vacant clubs have special logic - Class->tResultInfo.clear(); + getClassRef(true)->tResultInfo.clear(); } if (Club && Club->isVacant()) { // Clear entry date/time for vacant getDI().setInt("EntryDate", 0); @@ -2326,7 +2355,7 @@ void oEvent::getRunners(int classId, int courseId, vector &r, bool sort continue; } - if (classId <= 0 || it->getClassId() == classId) + if (classId <= 0 || it->getClassId(true) == classId) r.push_back(&*it); } } @@ -2364,7 +2393,7 @@ pRunner oRunner::nextNeedReadout() const { // For a runner in a team, first the team for the card for (size_t k = 0; k < tInTeam->Runners.size(); k++) { pRunner tr = tInTeam->Runners[k]; - if (tr && tr->CardNo == CardNo && !tr->Card && !tr->statusOK()) + if (tr && tr->getCardNo() == CardNo && !tr->Card && !tr->statusOK()) return tr; } } @@ -2432,9 +2461,11 @@ pRunner oEvent::getRunnerByCardNo(int cardNo, int time, bool onlyWithNoCard, boo } else { if (time <= 0) { //No time specified. Card readout search + + pRunner secondTry = 0; //First try runners with no card read or a different card read. for (it=Runners.begin(); it != Runners.end(); ++it) { - if (it->skip()) + if (it->isRemoved()) continue; if (ignoreRunnersWithNoStart && (it->getStatus() == StatusDNS || it->getStatus() == StatusCANCEL)) continue; @@ -2442,9 +2473,15 @@ pRunner oEvent::getRunnerByCardNo(int cardNo, int time, bool onlyWithNoCard, boo continue; pRunner ret; - if (it->CardNo==cardNo && (ret = it->nextNeedReadout()) != 0) - return ret; + if (it->CardNo == cardNo && (ret = it->nextNeedReadout()) != 0) { + if (!it->skip()) + return ret; + else if (secondTry == 0 || secondTry->tLeg > ret->tLeg) + secondTry = ret; + } } + if (secondTry) + return secondTry; } else { for (it=Runners.begin(); it != Runners.end(); ++it) { @@ -2745,11 +2782,11 @@ const vector< pair > &oEvent::fillRunners(vector< pairgetNameAndRace(true).c_str(), it->getClub().c_str(), - it->getClass().c_str()); + it->getClass(true).c_str()); } else { swprintf_s(bf, L"%s\t%s\t%s", it->getNameAndRace(true).c_str(), - it->getClass().c_str(), + it->getClass(true).c_str(), it->getClub().c_str()); } out.push_back(make_pair(bf, it->Id)); @@ -2769,7 +2806,7 @@ const vector< pair > &oEvent::fillRunners(vector< pairgetClubId() != lVacId || lVacId == 0) out.push_back(make_pair(it->getUIName(), it->Id)); else { - swprintf_s(bf, L"%s (%s)", it->getUIName().c_str(), it->getClass().c_str()); + swprintf_s(bf, L"%s (%s)", it->getUIName().c_str(), it->getClass(true).c_str()); out.push_back(make_pair(bf, it->Id)); } } @@ -2879,7 +2916,7 @@ void oRunner::createMultiRunner(bool createMaster, bool sync) if (!multiRunner[k-1]) { update = true; multiRunner[k-1]=oe->addRunner(sName, getClubId(), - getClassId(), 0, 0, false); + getClassId(false), 0, 0, false); multiRunner[k-1]->tDuplicateLeg=k; multiRunner[k-1]->tParentRunner=this; @@ -2909,6 +2946,12 @@ pRunner oRunner::getPredecessor() const bool oRunner::apply(bool sync, pRunner src, bool setTmpOnly) { createMultiRunner(false, sync); + if (sync) { + for (size_t k = 0; k < multiRunner.size(); k++) { + if (multiRunner[k]) + multiRunner[k]->synchronize(true); + } + } tLeg = -1; tLegEquClass = 0; tUseStartPunch=true; @@ -3045,7 +3088,7 @@ void oEvent::generateRunnerTableData(Table &table, oRunner *addRunner) oRunnerList::iterator it; table.reserve(Runners.size()); for (it=Runners.begin(); it != Runners.end(); ++it){ - if (!it->skip()){ + if (!it->isRemoved()){ it->addTableRow(table); } } @@ -3088,11 +3131,13 @@ void oRunner::addTableRow(Table &table) const table.set(row++, it, TID_ID, itow(getId()), false); table.set(row++, it, TID_MODIFIED, getTimeStamp(), false); - table.set(row++, it, TID_RUNNER, getUIName(), true); - - table.set(row++, it, TID_CLASSNAME, getClass(), true, cellSelection); + if (tParentRunner == 0) + table.set(row++, it, TID_RUNNER, getUIName(), true); + else + table.set(row++, it, TID_RUNNER, getUIName() + L" (" + itow(tDuplicateLeg+1) + L")", false); + table.set(row++, it, TID_CLASSNAME, getClass(true), true, cellSelection); table.set(row++, it, TID_COURSE, getCourseName(), true, cellSelection); - table.set(row++, it, TID_CLUB, getClub(), true, cellCombo); + table.set(row++, it, TID_CLUB, getClub(), tParentRunner == 0, cellCombo); table.set(row++, it, TID_TEAM, tInTeam ? tInTeam->getName() : L"", false); table.set(row++, it, TID_LEG, tInTeam ? itow(tLeg+1) : L"" , false); @@ -3138,7 +3183,7 @@ bool oRunner::inputData(int id, const wstring &input, throw std::exception("Tomt namn inte tillåtet."); if (sName != input && tRealName != input) { - updateFromDB(input, getClubId(), getClassId(), getCardNo(), getBirthYear()); + updateFromDB(input, getClubId(), getClassId(false), getCardNo(), getBirthYear()); setName(input, true); synchronizeAll(); } @@ -3184,7 +3229,7 @@ bool oRunner::inputData(int id, const wstring &input, else pc = oe->getClubCreate(0, input); - updateFromDB(getName(), pc ? pc->getId():0, getClassId(), getCardNo(), getBirthYear()); + updateFromDB(getName(), pc ? pc->getId():0, getClassId(false), getCardNo(), getBirthYear()); setClub(pc ? pc->getName() : L""); synchronize(true); @@ -3195,7 +3240,7 @@ bool oRunner::inputData(int id, const wstring &input, case TID_CLASSNAME: setClassId(inputId, true); synchronize(true); - output = getClass(); + output = getClass(true); break; case TID_STATUS: { @@ -3258,7 +3303,7 @@ void oRunner::fillInput(int id, vector< pair > &out, size_t &se else if (id==TID_CLASSNAME) { oe->fillClasses(out, oEvent::extraNone, oEvent::filterNone); out.push_back(make_pair(lang.tl(L"Ingen klass"), 0)); - selected = getClassId(); + selected = getClassId(true); } else if (id==TID_CLUB) { oe->fillClubs(out); @@ -3378,7 +3423,7 @@ void oRunner::getSplitTime(int courseControlId, RunnerStatus &stat, int &rt) con { rt = 0; stat = StatusUnknown; - int cardno = tParentRunner ? tParentRunner->CardNo : CardNo; + int cardno = getCardNo(); if (courseControlId==oPunch::PunchFinish && FinishTime>0 && tStatus!=StatusUnknown) { @@ -3643,7 +3688,9 @@ const wstring &oAbstractRunner::getBib() const } void oRunner::setBib(const wstring &bib, int bibNumerical, bool updateStartNo, bool tmpOnly) { - if (tParentRunner) + const bool freeBib = !Class || Class->getBibMode() == BibMode::BibFree; + + if (tParentRunner && !freeBib) tParentRunner->setBib(bib, bibNumerical, updateStartNo, tmpOnly); else { if (updateStartNo) @@ -3657,10 +3704,11 @@ void oRunner::setBib(const wstring &bib, int bibNumerical, bool updateStartNo, b if (oe) oe->bibStartNoToRunnerTeam.clear(); } - - for (size_t k=0;kgetDI().setString("Bib", bib); + if (!freeBib) { + for (size_t k = 0; k < multiRunner.size(); k++) { + if (multiRunner[k]) { + multiRunner[k]->getDI().setString("Bib", bib); + } } } } @@ -3848,7 +3896,7 @@ void oRunner::printSplits(gdioutput &gdi) const { gdi.dropLine(0.5); pCourse pc = getCourse(true); - gdi.addStringUT(bnormal, getName() + L", " + getClass()); + gdi.addStringUT(bnormal, getName() + L", " + getClass(true)); gdi.addStringUT(normal, getClub()); gdi.dropLine(0.5); gdi.addStringUT(normal, lang.tl("Start: ") + getStartTimeS() + lang.tl(", Mål: ") + getFinishTimeS()); @@ -4175,7 +4223,7 @@ void oRunner::printStartInfo(gdioutput &gdi) const { if (!bib.empty()) bib = bib + L": "; - gdi.addStringUT(boldSmall, bib + getName() + L", " + getClass()); + gdi.addStringUT(boldSmall, bib + getName() + L", " + getClass(true)); gdi.addStringUT(fontSmall, getClub()); gdi.dropLine(0.5); @@ -4274,7 +4322,7 @@ void oEvent::updateRunnersFromDB() for (it=Runners.begin(); it != Runners.end(); ++it) { if (!it->isVacant() && !it->isRemoved()) - it->updateFromDB(it->sName, it->getClubId(), it->getClassId(), it->getCardNo(), it->getBirthYear()); + it->updateFromDB(it->sName, it->getClubId(), it->getClassId(false), it->getCardNo(), it->getBirthYear()); } } @@ -5258,6 +5306,10 @@ void oRunner::markClassChanged(int controlId) { assert(controlId < 4096); if (Class) { Class->markSQLChanged(tLeg, controlId); + pClass cls2 = getClassRef(true); + if (cls2 != Class) + cls2->markSQLChanged(-1, controlId); + if (tInTeam && tInTeam->Class != Class && tInTeam->Class) { tInTeam->Class->markSQLChanged(tLeg, controlId); } @@ -5535,10 +5587,13 @@ int oRunner::getRanking() const { } void oAbstractRunner::hasManuallyUpdatedTimeStatus() { - if (Class && Class->hasClassGlobalDependance()) { + if (Class && Class->hasClassGlobalDependence()) { set cls; oe->reEvaluateAll(cls, false); } + if (Class) { + Class->updateFinalClasses(dynamic_cast(this), false); + } } bool oRunner::canShareCard(const pRunner other, int newCardNo) const { @@ -5555,7 +5610,7 @@ bool oRunner::canShareCard(const pRunner other, int newCardNo) const { if (!getTeam() || other->getTeam() != getTeam()) return false; - pClass tCls = getTeam()->getClassRef(); + const oClass * tCls = getTeam()->getClassRef(false); if (!tCls || tCls != Class) return false; @@ -5598,3 +5653,24 @@ bool oAbstractRunner::hasLateEntryFee() const { return late; } + +int oRunner::classInstance() const { + if (classInstanceRev.first == oe->dataRevision) + return classInstanceRev.second; + classInstanceRev.second = getDCI().getInt("Heat"); + if (Class) + classInstanceRev.second = min(classInstanceRev.second, Class->getNumQualificationFinalClasses()); + classInstanceRev.first = oe->dataRevision; + return classInstanceRev.second; +} + +const wstring &oAbstractRunner::getClass(bool virtualClass) const { + if (Class) { + if (virtualClass) + return Class->getVirtualClass(classInstance())->Name; + else + return Class->Name; + } + + else return _EmptyWString; +} diff --git a/code/oRunner.h b/code/oRunner.h index 83df501..ffbe369 100644 --- a/code/oRunner.h +++ b/code/oRunner.h @@ -255,17 +255,28 @@ public: const pClub getClubRef() const {return Club;} pClub getClubRef() {return Club;} + virtual int classInstance() const = 0; - const pClass getClassRef() const {return Class;} - pClass getClassRef() {return Class;} + const oClass *getClassRef(bool virtualClass) const { + return (virtualClass && Class) ? Class->getVirtualClass(classInstance()) : Class; + } + + pClass getClassRef(bool virtualClass) { + return pClass((virtualClass && Class) ? Class->getVirtualClass(classInstance()) : Class); + } virtual const wstring &getClub() const {if (Club) return Club->name; else return _EmptyWString;} virtual int getClubId() const {if (Club) return Club->Id; else return 0;} virtual void setClub(const wstring &clubName); virtual pClub setClubId(int clubId); - virtual const wstring &getClass() const {if (Class) return Class->Name; else return _EmptyWString;} - virtual int getClassId() const {if (Class) return Class->Id; else return 0;} + const wstring &getClass(bool virtualClass) const; + int getClassId(bool virtualClass) const { + if (Class) + return virtualClass ? Class->getVirtualClass(classInstance())->Id : Class->Id; + return 0; + } + virtual void setClassId(int id, bool isManualUpdate); virtual int getStartNo() const {return StartNo;} virtual void setStartNo(int no, bool storeTmpOnly); @@ -457,6 +468,8 @@ protected: // Running time as calculated by evalute. Used to detect changes. int tCachedRunningTime; + mutable pair classInstanceRev; + void clearOnChangedRunningTime(); // Cached runner statistics @@ -499,6 +512,8 @@ public: /** Get a runner reference (drawing) */ pRunner getReference() const; + int classInstance() const override; + /**Set a runner reference*/ void setReference(int runnerId); @@ -546,6 +561,9 @@ public: int getLegNumber() const {return tLeg;} int getSpeakerPriority() const; + RunnerStatus getTempStatus() const { return tempStatus; } + int getTempTime() const { return tempRT; } + void remove(); bool canRemove() const; @@ -703,7 +721,7 @@ public: pCard getCard() const {return Card;} int getCardId(){if (Card) return Card->Id; else return 0;} - bool operator<(const oRunner &c); + bool operator<(const oRunner &c) const; bool static CompareSINumber(const oRunner &a, const oRunner &b){return a.CardNo &missingPunches, int addpunch=0, bool synchronize=false); @@ -716,7 +734,7 @@ public: int getCourseId() const {if (Course) return Course->Id; else return 0;} void setCourseId(int id); - int getCardNo() const {return tParentRunner ? tParentRunner->CardNo : CardNo;} + int getCardNo() const {return tParentRunner && CardNo == 0 ? tParentRunner->CardNo : CardNo;} void setCardNo(int card, bool matchCard, bool updateFromDatabase = false); /** Sets the card to a given card. An existing card is marked as unpaired. CardNo is updated. Returns id of old card (or 0). diff --git a/code/oTeam.cpp b/code/oTeam.cpp index 33920b6..67956fe 100644 --- a/code/oTeam.cpp +++ b/code/oTeam.cpp @@ -315,7 +315,7 @@ void oTeam::setRunnerInternal(int k, pRunner r) Runners[k]->tInTeam = this; Runners[k]->tLeg = k; if (Class && Class->getLegType(k) != LTGroup) - Runners[k]->setClassId(getClassId(), false); + Runners[k]->setClassId(getClassId(false), false); } updateChanged(); } @@ -698,7 +698,7 @@ bool oTeam::compareResult(const oTeam &a, const oTeam &b) { if (a.Class != b.Class) { if (a.Class) { - if (b.Class) return a.Class->tSortIndex < b.Class->tSortIndex; + if (b.Class) return a.Class->tSortIndex < b.Class->tSortIndex || (a.Class->tSortIndex == b.Class->tSortIndex && a.Class->Id < b.Class->Id); else return true; } else return false; @@ -728,7 +728,8 @@ bool oTeam::compareStartTime(const oTeam &a, const oTeam &b) { if (a.Class != b.Class) { if (a.Class) { - if (b.Class) return a.Class->tSortIndextSortIndex; + if (b.Class) + return a.Class->tSortIndextSortIndex || (a.Class->tSortIndex == b.Class->tSortIndex && a.Class->Id < b.Class->Id); else return true; } } @@ -760,7 +761,8 @@ bool oTeam::compareSNO(const oTeam &a, const oTeam &b) { } else if (a.Class != b.Class) { if (a.Class) { - if (b.Class) return a.Class->tSortIndextSortIndex; + if (b.Class) + return a.Class->tSortIndex < b.Class->tSortIndex || (a.Class->tSortIndex == b.Class->tSortIndex && a.Class->Id < b.Class->Id); else return true; } } @@ -928,6 +930,7 @@ bool oTeam::apply(bool sync, pRunner source, bool setTmpOnly) { } if (Runners[i]) { + pClass actualClass = Runners[i]->getClassRef(true); if (Runners[i]->tInTeam && Runners[i]->tInTeam!=this) { Runners[i]->tInTeam->correctRemove(Runners[i]); } @@ -942,7 +945,8 @@ bool oTeam::apply(bool sync, pRunner source, bool setTmpOnly) { Runners[i]->tLegEquClass = i; } - Runners[i]->setStartNo(StartNo, setTmpOnly); + if (actualClass == Class) + Runners[i]->setStartNo(StartNo, setTmpOnly); if (!bib.empty() && Runners[i]->isChanged()) { if (bibMode == -1 && Class) bibMode = Class->getBibMode(); @@ -980,7 +984,7 @@ bool oTeam::apply(bool sync, pRunner source, bool setTmpOnly) { else lastStatus = Runners[i]->getStatus(); - StartTypes st = pc->getStartType(i); + StartTypes st = actualClass == pc ? pc->getStartType(i) : actualClass->getStartType(0); LegTypes lt = legType; if ((lt==LTParallel || lt==LTParallelOptional) && i==0) { @@ -1016,9 +1020,12 @@ bool oTeam::apply(bool sync, pRunner source, bool setTmpOnly) { } } if (!prs) { - if (lt==LTNormal || lt==LTSum || lt == LTGroup) - lastStartTime=pc->getStartData(i); - + if (lt == LTNormal || lt == LTSum || lt == LTGroup) { + if (actualClass == pc) + lastStartTime = pc->getStartData(i); + else + lastStartTime = actualClass->getStartData(0); // Qualification/final classes + } Runners[i]->setStartTime(lastStartTime, false, setTmpOnly); Runners[i]->tUseStartPunch=false; } @@ -1740,7 +1747,7 @@ void oEvent::getTeams(int classId, vector &t, bool sort) { if (it->isRemoved()) continue; - if (classId == 0 || it->getClassId() == classId) + if (classId == 0 || it->getClassId(false) == classId) t.push_back(&*it); } } @@ -1855,7 +1862,7 @@ void oTeam::addTableRow(Table &table) const { table.set(row++, it, TID_MODIFIED, getTimeStamp(), false); table.set(row++, it, TID_NAME, getName(), true); - table.set(row++, it, TID_CLASSNAME, getClass(), true, cellSelection); + table.set(row++, it, TID_CLASSNAME, getClass(true), true, cellSelection); table.set(row++, it, TID_CLUB, getClub(), true, cellCombo); table.set(row++, it, TID_START, getStartTimeS(), true); @@ -1920,7 +1927,7 @@ bool oTeam::inputData(int id, const wstring &input, } else { if (isName && !input.empty() && Class) { - pRunner r = oe->addRunner(input, getClubId(), getClassId(), 0, 0, false); + pRunner r = oe->addRunner(input, getClubId(), getClassId(false), 0, 0, false); setRunner(ix, r, true); output = r->getName(); } @@ -1968,7 +1975,7 @@ bool oTeam::inputData(int id, const wstring &input, case TID_CLASSNAME: setClassId(inputId, true); synchronize(true); - output = getClass(); + output = getClass(true); break; case TID_STATUS: { @@ -2033,7 +2040,7 @@ void oTeam::fillInput(int id, vector< pair > &out, size_t &sele else if (id==TID_CLASSNAME) { oe->fillClasses(out, oEvent::extraNone, oEvent::filterOnlyMulti); out.push_back(make_pair(lang.tl(L"Ingen klass"), 0)); - selected = getClassId(); + selected = getClassId(true); } else if (id==TID_CLUB) { oe->fillClubs(out); diff --git a/code/oTeam.h b/code/oTeam.h index 408e9a0..a52d966 100644 --- a/code/oTeam.h +++ b/code/oTeam.h @@ -105,6 +105,10 @@ public: int getRanking() const; + int classInstance() const override { + return 0; // Not supported + } + void resetResultCalcCache() const; vector< vector > &getResultCache(ResultCalcCacheSymbol symb) const; void setResultCache(ResultCalcCacheSymbol symb, int leg, vector &data) const; diff --git a/code/oTeamEvent.cpp b/code/oTeamEvent.cpp index 779b47b..f4c156d 100644 --- a/code/oTeamEvent.cpp +++ b/code/oTeamEvent.cpp @@ -102,7 +102,7 @@ const vector< pair > &oEvent::fillTeams(vector< pairgetName(); } if (it->Class) - out.push_back(make_pair(tn + L" (" + it->getClass() + L")", it->Id)); + out.push_back(make_pair(tn + L" (" + it->getClass(true) + L")", it->Id)); else out.push_back(make_pair(tn, it->Id)); } @@ -612,14 +612,14 @@ void oEvent::adjustTeamMultiRunners(pClass cls) if (cls) { bool multi = cls->getNumStages() > 1; for (oRunnerList::iterator it = Runners.begin(); it != Runners.end(); ++it) { - if (it->skip() || it->getClassId() != cls->getId()) + if (it->skip() || it->getClassId(false) != cls->getId()) continue; if (multi && it->tInTeam == 0) { oe->autoAddTeam(&*it); } if (!multi && it->tInTeam) { - assert( it->tInTeam->getClassId() == cls->getId()); + assert( it->tInTeam->getClassId(false) == cls->getId()); removeTeam(it->tInTeam->getId()); } @@ -628,7 +628,7 @@ void oEvent::adjustTeamMultiRunners(pClass cls) vector tr; for (oTeamList::iterator it=Teams.begin(); it != Teams.end(); ++it) { - if (!multi && !it->isRemoved() && it->getClassId() == cls->getId()) { + if (!multi && !it->isRemoved() && it->getClassId(false) == cls->getId()) { tr.push_back(it->getId()); } } @@ -728,9 +728,9 @@ void oTeam::checkClassesWithReferences(oEvent &oe, std::set &clsWithRef) { map > pairedUnpairedPerClass; for (size_t k = 0; k < r.size(); k++) { if (r[k]->getReference()) - ++pairedUnpairedPerClass[r[k]->getClassId()].first; + ++pairedUnpairedPerClass[r[k]->getClassId(false)].first; else - ++pairedUnpairedPerClass[r[k]->getClassId()].second; + ++pairedUnpairedPerClass[r[k]->getClassId(false)].second; } for (auto &it : pairedUnpairedPerClass) { @@ -744,8 +744,8 @@ void oTeam::convertClassWithReferenceToPatrol(oEvent &oe, const std::set &c oe.getRunners(-1, -1, r, true); for(auto it : r) { - if (clsWithRef.count(it->getClassId())) { - pClass cls = it->getClassRef(); + if (clsWithRef.count(it->getClassId(false))) { + pClass cls = it->getClassRef(false); if (cls->getNumStages() == 0) { pCourse crs = cls->getCourse(); diff --git a/code/ospeaker.h b/code/ospeaker.h index ee66d10..37b17b2 100644 --- a/code/ospeaker.h +++ b/code/ospeaker.h @@ -45,11 +45,27 @@ class oSpeakerObject { public: struct RunningTime { + void reset() { time = 0; preliminary = 0; } int time; int preliminary; RunningTime() : time(0), preliminary(0) {} }; + void reset() { + owner = 0; + bib.clear(); + names.clear(); + outgoingnames.clear(); + resultRemark.clear(); + club.clear(); + startTimeS.clear(); + status = StatusUnknown; + finishStatus = StatusUnknown; + useSinceLast = 0; + runningTime.reset(); + runningTimeLeg.reset(); + runningTimeSinceLast.reset(); + } oRunner *owner; wstring bib; vector names; diff --git a/code/qualification_final.cpp b/code/qualification_final.cpp new file mode 100644 index 0000000..b2d92c8 --- /dev/null +++ b/code/qualification_final.cpp @@ -0,0 +1,298 @@ +#include "StdAfx.h" + +#include "qualification_final.h" + +#include "meos_util.h" +#include "meosexception.h" +#include +#include +#include "xmlparser.h" + +pair QualificationFinal::getNextFinal(int instance, int orderPlace, int numSharedPlaceNext) const { + pair key(instance, orderPlace); + int iter = 0; + while (numSharedPlaceNext >= 0) { + auto res = sourcePlaceToFinalOrder.find(key); + if (res != sourcePlaceToFinalOrder.end()) { + if (iter >= 2) { // For three in a shared last place + pair key2 = key; + int extraSub = ((iter + 1) % 2); + key2.second -= extraSub; + auto res2 = sourcePlaceToFinalOrder.find(key2); + if (res2 != sourcePlaceToFinalOrder.end()) { + auto ans = res2->second; + ans.second += iter + extraSub; + return ans; + } + } + + + auto ans = res->second; + ans.second += iter; + return ans; + } + --key.second; + --numSharedPlaceNext; + ++iter; + } + + return make_pair(0, -1); +} + +bool QualificationFinal::noQualification(int instance) const { + if (size_t(instance) >= classDefinition.size()) + return false; + + return classDefinition[instance].qualificationMap.empty() && + classDefinition[instance].timeQualifications.empty(); +} + + +void QualificationFinal::getBaseClassInstances(set &base) const { + for (size_t k = 0; k < classDefinition.size(); k++) { + if (noQualification(k)) + base.insert(k+1); + else break; + } + +} + +void QualificationFinal::import(const wstring &file) { + xmlparser xml; + xml.read(file); + + auto qr = xml.getObject("QualificationRules"); + xmlList levels; + qr.getObjects("Level", levels); + map idToIndex; + map > qualificationRelations; + int numBaseLevels = 0; + int iLevel = 0; + for (size_t iLevel = 0; iLevel < levels.size(); iLevel++) { + auto &level = levels[iLevel]; + xmlList classes; + level.getObjects("Class", classes); + for (auto &cls : classes) { + wstring name; + cls.getObjectString("Name", name); + if (name.empty()) + throw meosException("Klassen måste ha ett namn."); + int classId = cls.getObjectInt("id"); + if (!(classId>0)) + throw meosException("Id must be a positive integer."); + if (idToIndex.count(classId)) + throw meosException("Duplicate class with id " + itos(classId)); + + xmlList rules; + cls.getObjects("Qualification", rules); + if (rules.empty()) + numBaseLevels = 1; // Instance zero is not used as qualification, + + idToIndex[classId] = classDefinition.size() + numBaseLevels; + classDefinition.push_back(Class()); + classDefinition.back().name = name; + + for (auto &qf : rules) { + int place = qf.getObjectInt("place"); + if (place > 0) { + int id = qf.getObjectInt("id"); + if (id == 0 && iLevel == 0) + classDefinition.back().qualificationMap.push_back(make_pair(0, place)); + else if (idToIndex.count(id)) { + classDefinition.back().qualificationMap.push_back(make_pair(idToIndex[id], place)); + qualificationRelations[classId].insert(id); + } + else + throw meosException("Unknown class with id " + itos(id)); + } + else { + string time; + qf.getObjectString("place", time); + if (time == "time") { + string ids; + qf.getObjectString("id", ids); + vector vid; + split(ids, ",;", vid); + vector ivid; + for (auto &s : vid) { + int i = atoi(s.c_str()); + if (!idToIndex.count(i)) + throw meosException("Unknown class with id " + itos(i)); + ivid.push_back(idToIndex[i]); + } + + if (ivid.empty()) + throw meosException(L"Empty time qualification for " + name); + classDefinition.back().timeQualifications.push_back(ivid); + } + else throw meosException("Unknown classification rule " + time); + } + } + } + } + /*classDefinition.resize(3); + classDefinition[0].name = L"Semi A"; + classDefinition[1].name = L"Semi B"; + classDefinition[2].name = L"Final"; + + for (int i = 0; i < 4; i++) { + classDefinition[0].qualificationMap.push_back(make_pair(0, i * 2 + 1)); + classDefinition[1].qualificationMap.push_back(make_pair(0, i * 2 + 2)); + classDefinition[2].qualificationMap.push_back(make_pair(i%2+1, i/2 + 1)); + } + */ + initgmap(true); +} + +void QualificationFinal::init(const wstring &def) { + vector races, rtdef, rdef; + split(def, L"|", races); + classDefinition.resize(races.size()); + bool valid = true; + bool first = true; + for (size_t k = 0; k < races.size(); k++) { + split(races[k], L"T", rtdef); + classDefinition[k].qualificationMap.clear(); + classDefinition[k].timeQualifications.clear(); + + if (first && rtdef.empty()) + continue; + + valid = rtdef.size() > 0; + if (!valid) + break; + first = false; + + split(rtdef[0], L";", rdef); + valid = rdef.size() > 0 && rdef.size()%2 == 0; + if (!valid) + break; + + for (size_t j = 0; j < rdef.size(); j+=2) { + size_t src = _wtoi(rdef[j].c_str()); + if (src > k) { + valid = false; + break; + } + const wstring &rd = rdef[j + 1]; + size_t d1 = _wtoi(rd.c_str()); + size_t d2 = d1; + size_t range = rd.find_first_of('-', 0); + if (range < rd.size()) + d2 = _wtoi(rd.c_str()+range+1); + + if (d1 > d2) { + valid = false; + break; + } + + while (d1 <= d2) { + classDefinition[k].qualificationMap.push_back(make_pair(int(src), int(d1))); + d1++; + } + } + + for (size_t i = 1; valid && i < rtdef.size(); i++) { + split(rtdef[i], L";", rdef); + classDefinition[k].timeQualifications.push_back(vector()); + for (size_t j = 0; valid && j < rdef.size(); j += 2) { + size_t src = _wtoi(rdef[j].c_str()); + if (src > k) { + valid = false; + break; + } + classDefinition[k].timeQualifications.back().push_back(src); + } + } + } + + if (!valid) + classDefinition.clear(); + + initgmap(false); +} + +void QualificationFinal::encode(wstring &output) const { + output.clear(); + + for (size_t k = 0; k < classDefinition.size(); k++) { + if (k > 0) + output.append(L"|"); + + auto &qm = classDefinition[k].qualificationMap; + + for (size_t j = 0; j < qm.size(); j++) { + if (j > 0) + output.append(L";"); + + size_t i = j; + while ((i + 1) < qm.size() && qm[i + 1].first == qm[i].first + && qm[i + 1].second == qm[i].second+1) { + i++; + } + output.append(itow(qm[j].first) + L";"); + if (i <= j + 1) { + output.append(itow(qm[j].second)); + } + else { + output.append(itow(qm[j].second) + L"-" + itow(qm[i].second)); + j = i; + } + } + + auto &tqm = classDefinition[k].timeQualifications; + for (auto &source : tqm) { + output.append(L"T"); + for (size_t i = 0; i < source.size(); i++) { + if (i > 0) + output.append(L";"); + output.append(itow(source[i])); + } + } + } +} + +int QualificationFinal::getNumStages(int stage) const { + if (stage == 0 || classDefinition[stage-1].qualificationMap.empty()) + return 1; + + set races; + for (auto &qm : classDefinition[stage - 1].qualificationMap) { + if (qm.first == stage) + throw meosException("Invalid qualification scheme"); + races.insert(qm.first); + } + + int def = 0; + for (int r : races) + def = max(def, 1 + getNumStages(r)); + + return def; +} + +void QualificationFinal::initgmap(bool check) { + sourcePlaceToFinalOrder.clear(); + + for (int ix = 0; ix < (int)classDefinition.size(); ix++) { + auto &c = classDefinition[ix]; + + for (int k = 0; k < (int)c.qualificationMap.size(); k++) { + const pair &sd = c.qualificationMap[k]; + if (check && sourcePlaceToFinalOrder.count(sd)) + throw meosException(L"Inconsistent qualification rule, X#" + c.name + + L"/" + itow(sd.first)); + + sourcePlaceToFinalOrder[sd] = make_pair(ix+1, k); + } + } +} + +int QualificationFinal::getHeatFromClass(int finalClassId, int baseClassId) const { + if (baseClassId == baseId) { + int fci = (finalClassId - baseId) / maxClassId; + + if (fci * maxClassId + baseClassId == finalClassId) + return fci; + } + + return 0; +} diff --git a/code/qualification_final.h b/code/qualification_final.h new file mode 100644 index 0000000..36caa8d --- /dev/null +++ b/code/qualification_final.h @@ -0,0 +1,84 @@ +#pragma once + +/************************************************************************ +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 + + +class QualificationFinal { +private: + + int maxClassId; + int baseId; + + struct Class { + wstring name; + vector< pair > qualificationMap; + vector< vector > timeQualifications; + }; + + vector classDefinition; + + map, pair> sourcePlaceToFinalOrder; + + void initgmap(bool check); + + // Recursive implementation + int getNumStages(int stage) const; +public: + + QualificationFinal(int maxClassId, int baseId) : maxClassId(maxClassId), baseId(baseId) {} + + int getNumClasses() const { + return classDefinition.size(); + } + + const wstring getInstanceName(int inst) { + return classDefinition.at(inst-1).name; + } + + // Count number of stages + int getNumStages() const { + return getNumStages(classDefinition.size()); + } + + int getHeatFromClass(int finalClassId, int baseClassId) const; + + void import(const wstring &file); + + void init(const wstring &def); + void encode(wstring &output) const; + + /** Retuns the final class and the order within that class. */ + pair getNextFinal(int instance, int orderPlace, int numSharedPlaceNext) const; + + /** Returns true if all competitors are automatically qualified*/ + bool noQualification(int instance) const; + + /** Fills in the set of no-qualification classes*/ + void getBaseClassInstances(set &base) const; +}; diff --git a/code/resource.h b/code/resource.h index b901a63..90939e1 100644 --- a/code/resource.h +++ b/code/resource.h @@ -10,14 +10,16 @@ #define IDI_SMALL 108 #define IDC_MEOS 109 #define IDR_MAINFRAME 128 +#define IDB_ECO 131 +#define IDR_HTML1 132 #define IDC_STATIC -1 // Next default values for new objects -// +// #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NO_MFC 1 -#define _APS_NEXT_RESOURCE_VALUE 131 +#define _APS_NEXT_RESOURCE_VALUE 133 #define _APS_NEXT_COMMAND_VALUE 32771 #define _APS_NEXT_CONTROL_VALUE 1001 #define _APS_NEXT_SYMED_VALUE 110 diff --git a/code/restserver.cpp b/code/restserver.cpp index 11c5bb9..d2fe8cd 100644 --- a/code/restserver.cpp +++ b/code/restserver.cpp @@ -31,11 +31,14 @@ Eksoppsv #include "infoserver.h" #include +#include "oListInfo.h" +#include "TabList.h" +#include "generalresult.h" + using namespace restbed; vector< shared_ptr > RestServer::startedServers; - shared_ptr RestServer::construct() { shared_ptr obj(new RestServer()); startedServers.push_back(obj); @@ -43,7 +46,13 @@ shared_ptr RestServer::construct() { } void RestServer::remove(shared_ptr server) { -// xxx std::remove(startedServers.begin(), startedServers.end(), server); + //std::remove(startedServers.begin(), startedServers.end(), server); + for (size_t k = 0; k < startedServers.size(); k++) { + if (startedServers[k] == server) { + startedServers.erase(startedServers.begin() + k); + break; + } + } } RestServer::RestServer() : hasAnyRequest(false) { @@ -142,21 +151,18 @@ void RestServer::compute(oEvent &ref) { auto rq = getRequest(); if (!rq) return; - if (rq->parameters.empty()) { - rq->answer = "" - "MeOS Information Service" - "" - "" - "

MeOS

" - "" - ""; + + try { + computeInternal(ref, rq); } - else if (rq->parameters.count("get") > 0) { - string what = rq->parameters.find("get")->second; - getData(ref, what, rq->parameters, rq->answer); + catch (meosException &ex) { + rq->answer = "Error (MeOS): Error: " + ref.gdiBase().toUTF8(lang.tl(ex.wwhat())); } - else { - rq->answer = "Error (MeOS): Unknown request"; + catch (std::exception &ex) { + rq->answer = "Error (MeOS): General Error: " + string(ex.what()); + } + catch (...) { + rq->answer = "Error (MeOS): Unknown internal error."; } { @@ -166,39 +172,594 @@ void RestServer::compute(oEvent &ref) { waitForCompletion.notify_all(); } + +void RestServer::computeInternal(oEvent &ref, shared_ptr &rq) { + if (rq->parameters.empty()) { + rq->answer = "" + "" + "MeOS Information Service" + "" + "" + "

MeOS

" + ref.gdiBase().toUTF8(lang.tl(getMeosFullVersion())) + "

" + "

    \n"; + + vector lists; + TabList::getPublicLists(ref, lists); + map listMap; + ref.getListTypes(listMap, false); + for (auto &lp : lists) { + wstring n = lp.getName(); + if (n.empty()) { + n = listMap[lp.listCode].getName(); + } + lp.setName(n); + + int keyCand = lp.listCode * 100; + bool done = false; + for (int i = 0; i < 100; i++) { + if (!listCache.count(keyCand + i)) { + keyCand += i; + listCache[keyCand].first = lp; + done = true; + break; + } + else if(listCache[keyCand + i].first == lp) { + keyCand = keyCand + i; + done = true; + break; + } + } + if (!done) { + listCache[keyCand].first = lp; + listCache[keyCand].second.reset(); + } + + rq->answer += "
  • " + ref.gdiBase().toUTF8(n) + "
  • \n"; + } + // "
  • Resultat
  • " + // "
  • Startlista
  • " + rq->answer += "
\n"; + + HINSTANCE hInst = GetModuleHandle(0); + HRSRC hRes = FindResource(hInst, MAKEINTRESOURCE(132), RT_HTML); + HGLOBAL res = LoadResource(hInst, hRes); + char *html = (char *)LockResource(res); + int resSize = SizeofResource(hInst, hRes); + + if (html) { + string htmlS; + while (*html != 0 && resSize > 0) { + if (*html == '*') + htmlS += "<"; + else if (*html == '#') + htmlS += "&"; + else + htmlS += *html; + ++html; + resSize--; + } + + rq->answer += htmlS; + } + + rq->answer += "\n\n"; + } + else if (rq->parameters.count("get") > 0) { + string what = rq->parameters.find("get")->second; + getData(ref, what, rq->parameters, rq->answer); + } + else if (rq->parameters.count("html") > 0) { + string stype = rq->parameters.count("type") ? rq->parameters.find("type")->second : _EmptyString; + int type = atoi(stype.c_str()); + auto res = listCache.find(type); + + if (res != listCache.end()) { + gdioutput gdiPrint("print", ref.gdiBase().getScale(), ref.gdiBase().getCP()); + gdiPrint.clearPage(false); + + if (!res->second.second) { + res->second.second = make_shared(); + ref.generateListInfo(res->second.first, gdiPrint.getLineHeight(), *res->second.second); + } + ref.generateList(gdiPrint, true, *res->second.second, false); + wstring exportFile = getTempFile(); + gdiPrint.writeHTML(exportFile, ref.getName(), 30); + ifstream fin(exportFile.c_str()); + string rbf; + while (std::getline(fin, rbf)) { + rq->answer += rbf; + } + removeTempFile(exportFile); + } + else { + rq->answer = "Error (MeOS): Unknown list"; + } + } + else { + rq->answer = "Error (MeOS): Unknown request"; + } +} + +void writePerson(const GeneralResult::GeneralResultInfo &res, + xmlbuffer &reslist, + bool includeTeam, + bool includeCourse, + vector< pair > &rProp) { + int clsId = res.src->getClassId(true); + rProp.push_back(make_pair("cls", itow(clsId))); + if (includeCourse) { + pRunner pr = dynamic_cast(res.src); + if (pr && pr->getCourse(false)) + rProp.push_back(make_pair("course", itow(pr->getCourse(false)->getId()))); + } + bool hasTeam = res.src->getTeam() != 0 && + clsId == res.src->getClassId(false) && // No Qualification/Final + (clsId == 0 || res.src->getClassRef(false)->getQualificationFinal() == 0); // // No Qualification/Final + + if (includeTeam && hasTeam) + rProp.push_back(make_pair("team", itow(res.src->getTeam()->getId()))); + + if (hasTeam) + rProp.push_back(make_pair("leg", itow(1+pRunner(res.src)->getLegNumber()))); + + rProp.push_back(make_pair("stat", itow(res.status))); + + if (res.score > 0) + rProp.push_back(make_pair("score", itow(res.score))); + + rProp.push_back(make_pair("st", itow(10 * (res.src->getStartTime() + res.src->getEvent()->getZeroTimeNum())))); + + if (res.src->getClassRef(false) == 0 || !res.src->getClassRef(true)->getNoTiming()) { + if (res.time > 0) + rProp.push_back(make_pair("rt", itow(10 * res.time))); + if (res.place > 0) + rProp.push_back(make_pair("place", itow(res.place))); + } + + auto &subTag = reslist.startTag("person", rProp); + rProp.clear(); + rProp.push_back(make_pair("id", itow(res.src->getId()))); + subTag.write("name", rProp, res.src->getName()); + rProp.clear(); + + if (res.src->getClubRef()) { + rProp.push_back(make_pair("id", itow(res.src->getClubId()))); + subTag.write("org", rProp, res.src->getClub()); + rProp.clear(); + } + + subTag.endTag(); +} + void RestServer::getData(oEvent &oe, const string &what, const multimap ¶m, string &answer) { xmlbuffer out; out.setComplete(true); + bool okRequest = false; + if (what == "iofresult") { + wstring exportFile = getTempFile(); + bool useUTC = false; + set cls; + if (param.count("class") > 0) + getSelection(param.find("class")->second, cls); - if (what == "competition") { + oe.exportIOFSplits(oEvent::IOF30, exportFile.c_str(), false, useUTC, cls, -1, false, false, true, false); + ifstream fin(exportFile.c_str()); + string rbf; + while (std::getline(fin, rbf)) { + answer += rbf; + } + removeTempFile(exportFile); + okRequest = true; + } + else if (what == "iofstart") { + wstring exportFile = getTempFile(); + bool useUTC = false; + set cls; + if (param.count("class") > 0) + getSelection(param.find("class")->second, cls); + + oe.exportIOFStartlist(oEvent::IOF30, exportFile.c_str(), useUTC, cls, false, true, false); + ifstream fin(exportFile.c_str()); + string rbf; + while (std::getline(fin, rbf)) { + answer += rbf; + } + removeTempFile(exportFile); + okRequest = true; + } + else if (what == "competition") { InfoCompetition cmp(0); cmp.synchronize(oe); cmp.serialize(out, false); + okRequest = true; } else if (what == "class") { vector cls; oe.getClasses(cls, true); - + set ctrlW; + vector ctrl; + oe.getControls(ctrl, true); + for (size_t k = 0; k < ctrl.size(); k++) { + if (ctrl[k]->isValidRadio()) { + vector cc; + ctrl[k]->getCourseControls(cc); + ctrlW.insert(cc.begin(), cc.end()); + } + } for (auto c : cls) { InfoClass iCls(c->getId()); - set ctrl; - iCls.synchronize(*c, ctrl); + iCls.synchronize(*c, ctrlW); iCls.serialize(out, false); } + okRequest = true; + } + else if (what == "organization") { + vector clb; + oe.getClubs(clb, true); + for (auto c : clb) { + InfoOrganization iClb(c->getId()); + iClb.synchronize(*c); + iClb.serialize(out, false); + } + okRequest = true; } else if (what == "competitor") { vector r; set selection; - if (param.count("id") > 0) - getSelection(param.find("id")->second, selection); + if (param.count("class") > 0) + getSelection(param.find("class")->second, selection); oe.getRunners(selection.size() == 1 ? *selection.begin() : 0, -1, r, true); + { + vector r2; + r2.reserve(r.size()); + for (pRunner tr : r) { + pClass cls = tr->getClassRef(true); + if (cls && cls->getQualificationFinal() && tr->getLegNumber() != 0) + continue; + if (selection.empty() || (cls && selection.count(cls->getId()))) + r2.push_back(tr); + } + r.swap(r2); + } + for (auto c : r) { InfoCompetitor iR(c->getId()); iR.synchronize(false, *c); iR.serialize(out, false); } + okRequest = true; + } + else if (what == "team") { + vector teams; + set selection; + if (param.count("class") > 0) + getSelection(param.find("class")->second, selection); + + oe.getTeams(selection.size() == 1 ? *selection.begin() : 0, teams, true); + + if (selection.size() > 1) { + vector r2; + r2.reserve(teams.size()); + for (pTeam tr : teams) { + if (selection.count(tr->getClassId(false))) + r2.push_back(tr); + } + teams.swap(r2); + } + + for (auto c : teams) { + InfoTeam iT(c->getId()); + iT.synchronize(*c); + iT.serialize(out, false); + } + okRequest = true; + } + else if (what == "control") { + vector ctrl; + oe.getControls(ctrl, true); + vector< pair > prop(1); + prop[0].first = "id"; + for (pControl c : ctrl) { + int nd = c->getNumberDuplicates(); + for (int k = 0; k < nd; k++) { + prop[0].second = itow(oControl::getCourseControlIdFromIdIndex(c->getId(), k)); + if (nd > 1) + out.write("control", prop, c->getName() + L"-" + itow(k+1)); + else + out.write("control", prop, c->getName()); + } + } + okRequest = true; + } + else if (what == "result") { + okRequest = true; + vector r; + set selection; + pClass sampleClass = 0; + if (param.count("class") > 0) { + getSelection(param.find("class")->second, selection); + if (!selection.empty()) + sampleClass = oe.getClass(*selection.begin()); + } + + if (sampleClass == 0) { + vector tt; + oe.getClasses(tt, false); + if (!tt.empty()) + sampleClass = tt[0]; + } + + string resTag; + if (param.count("module") > 0) + resTag = param.find("module")->second; + + pair controlId(oPunch::PunchStart, oPunch::PunchFinish); + bool totalResult = false; + if (param.count("total")) { + const string &tot = param.find("total")->second; + totalResult = atoi(tot.c_str()) > 0 || _stricmp(tot.c_str(), "true") == 0; + } + + if (param.count("to")) { + const string &cid = param.find("to")->second; + + controlId.second = atoi(cid.c_str()); + if (controlId.second == 0) + controlId.second = oControl::getControlIdByName(oe, cid); + + if (controlId.second == 0) + throw meosException("Unknown control: " + cid); + } + + if (param.count("from")) { + const string &cid = param.find("from")->second; + + controlId.first = atoi(cid.c_str()); + if (controlId.first == 0) + controlId.first = oControl::getControlIdByName(oe, cid); + + if (controlId.first == 0) + throw meosException("Unknown control: " + cid); + } + + oListInfo::ResultType resType = oListInfo::Classwise; + wstring resTypeStr = L"classindividual"; + + ClassType ct = sampleClass ? sampleClass->getClassType() : ClassType::oClassIndividual; + bool team = ct == oClassPatrol || ct == oClassRelay; + int limit = 100000; + if (param.count("limit")) { + limit = atoi(param.find("limit")->second.c_str()); + if (limit <= 0) + throw meosException("Invalid limit: " + param.find("limit")->second); + } + + if (param.count("type")) { + string type = param.find("type")->second; + + if (type == "GlobalIndividual") { + resType = oListInfo::Global; + team = false; + } + else if (type == "ClassIndividual") { + resType = oListInfo::Classwise; + team = false; + } + else if (type == "CourseIndividual") { + resType = oListInfo::Coursewise; + team = false; + } + else if (type == "LegIndividual") { + resType = oListInfo::Legwise; + team = false; + } + else if (type == "GlobalTeam") { + resType = oListInfo::Global; + team = true; + } + else if (type == "ClassTeam") { + resType = oListInfo::Classwise; + team = true; + } + else + throw meosException("Unknown type: " + type); + + string2Wide(type, resTypeStr); + resTypeStr = canonizeName(resTypeStr.c_str()); + } + else if (team) { + resTypeStr = L"classteam"; + } + + int inputNumber = 0; + + + if (param.count("argument")) { + const string &arg = param.find("argument")->second; + inputNumber = atoi(arg.c_str()); + } + + vector results; + vector< pair > prop, noProp, rProp; + + prop.push_back(make_pair("type", resTypeStr)); + + int leg = -1; + if (param.count("leg") > 0) { + string legs = param.find("leg")->second; + leg = atoi(legs.c_str())-1; + if (leg < 0 || leg > 32) + throw meosException("Invalid leg: " + legs); + } + + if (ct == oClassRelay) { + if (leg == -1) + prop.push_back(make_pair("leg", L"Last")); + else + prop.push_back(make_pair("leg", itow(leg + 1))); + } + + if (!resTag.empty()) + prop.push_back(make_pair("module", oe.gdiBase().widen(resTag) + L"(" + itow(inputNumber) + L")")); + + if (controlId.first != oPunch::PunchStart) { + pair idIx = oControl::getIdIndexFromCourseControlId(controlId.first); + pControl ctrl = oe.getControl(idIx.first); + if (ctrl == 0) + throw meosException("Unknown control: " + itos(idIx.first)); + + wstring loc = ctrl->getName(); + if (idIx.second > 0) + loc += L"-" + itow(idIx.second + 1); + + prop.push_back(make_pair("from", loc)); + } + + if (controlId.second == oPunch::PunchFinish) + prop.push_back(make_pair("to", L"Finish")); + else { + pair idIx = oControl::getIdIndexFromCourseControlId(controlId.second); + pControl ctrl = oe.getControl(idIx.first); + if (ctrl == 0) + throw meosException("Unknown control: " + itos(idIx.first)); + + wstring loc = ctrl->getName(); + if (idIx.second > 0) + loc += L"-" + itow(idIx.second+1); + + prop.push_back(make_pair("to", loc)); + } + + if (!team) { + oe.getRunners(selection.size() == 1 ? *selection.begin() : 0, -1, r, false); + + { + vector r2; + r2.reserve(r.size()); + for (pRunner tr : r) { + pClass cls = tr->getClassRef(true); + if (cls && cls->getQualificationFinal() && tr->getLegNumber() != 0) + continue; + if (selection.empty() || (cls && selection.count(cls->getId()))) + r2.push_back(tr); + } + r.swap(r2); + } + + GeneralResult::calculateIndividualResults(r, controlId, totalResult, resTag, resType, inputNumber, oe, results); + + if (resType == oListInfo::Classwise) + sort(results.begin(), results.end()); + /*else if (resType == oListInfo::Coursewise) { + sort(results.begin(), results.end(), + [](const GeneralResult::GeneralResultInfo &a, const GeneralResult::GeneralResultInfo &b)-> + bool { + pCourse ac = dynamic_cast(*a.src).getCourse(false); + pCourse bc = dynamic_cast(*b.src).getCourse(false); + if (ac != bc) + return ac->getId() < bc->getId(); + + return a.compareResult(b); + } + ); + }*/ + auto &reslist = out.startTag("results", prop); + + int place = -1; + int cClass = -1; + int counter = 0; + for (const auto &res : results) { + if (res.src->getClassId(true) != cClass) { + counter = 0; + place = 1; + cClass = res.src->getClassId(true); + } + if (++counter > limit && (place != res.place || res.status != StatusOK)) + continue; + place = res.place; + writePerson(res, reslist, true, resType == oListInfo::Coursewise, rProp); + } + reslist.endTag(); + } + else { + //Teams + vector teams; + oe.getTeams(selection.size() == 1 ? *selection.begin() : 0, teams, true); + + if (selection.size() > 1) { + vector r2; + r2.reserve(teams.size()); + for (pTeam tr : teams) { + if (selection.count(tr->getClassId(true))) + r2.push_back(tr); + } + teams.swap(r2); + } + auto context = GeneralResult::calculateTeamResults(teams, leg, controlId, totalResult, resTag, resType, inputNumber, oe, results); + + sort(results.begin(), results.end()); + auto &reslist = out.startTag("results", prop); + + int place = -1; + int cClass = -1; + int counter = 0; + for (const auto &res : results) { + pTeam team = pTeam(res.src); + if (leg >= team->getNumRunners()) + continue; + + if (res.src->getClassId(true) != cClass) { + counter = 0; + place = 1; + cClass = res.src->getClassId(true); + } + if (++counter > limit && (place != res.place || res.status != StatusOK)) + continue; + place = res.place; + + rProp.push_back(make_pair("cls", itow(res.src->getClassId(true)))); + rProp.push_back(make_pair("stat", itow(res.status))); + if (res.score > 0) + rProp.push_back(make_pair("score", itow(res.score))); + + if (res.src->getStartTime() > 0) + rProp.push_back(make_pair("st", itow(10 * (res.src->getStartTime() + oe.getZeroTimeNum())))); + + if (res.src->getClassRef(false) == 0 || !res.src->getClassRef(true)->getNoTiming()) { + if (res.time > 0) + rProp.push_back(make_pair("rt", itow(10 * res.time))); + if (res.place > 0) + rProp.push_back(make_pair("place", itow(res.place))); + } + + auto &subTag = reslist.startTag("team", rProp); + rProp.clear(); + rProp.push_back(make_pair("id", itow(res.src->getId()))); + subTag.write("name", rProp, res.src->getName()); + rProp.clear(); + + if (res.src->getClubRef()) { + rProp.push_back(make_pair("id", itow(res.src->getClubId()))); + subTag.write("org", rProp, res.src->getClub()); + rProp.clear(); + } + + int subRes = res.getNumSubresult(*context); + GeneralResult::GeneralResultInfo out; + + for (int k = 0; k < subRes; k++) { + if (res.getSubResult(*context, k, out)) { + writePerson(out, subTag, false, false, rProp); + } + } + + subTag.endTag(); + } + reslist.endTag(); + + } } if (out.size() > 0) { xmlparser mem; @@ -208,7 +769,7 @@ void RestServer::getData(oEvent &oe, const string &what, const multimap &rq); + + map > > listCache; public: ~RestServer(); diff --git a/code/speakermonitor.cpp b/code/speakermonitor.cpp index 11fd840..8dff33f 100644 --- a/code/speakermonitor.cpp +++ b/code/speakermonitor.cpp @@ -194,7 +194,7 @@ void SpeakerMonitor::renderResult(gdioutput &gdi, int dx = 0; if (showClass) { - pClass pc = res.r->getClassRef(); + pClass pc = res.r->getClassRef(true); if (pc) { gdi.addStringUT(yp, xp + timeWidth, fontMediumPlus, pc->getName()); dx = classWidth; @@ -243,7 +243,7 @@ void SpeakerMonitor::renderResult(gdioutput &gdi, } else { if (showClass) { - pClass pc = res.r->getClassRef(); + pClass pc = res.r->getClassRef(true); if (pc) msg += L" (" + pc->getName() + L") "; else @@ -412,7 +412,7 @@ wstring getTimeDesc(int t1, int t2) { void SpeakerMonitor::getMessage(const oEvent::ResultEvent &res, wstring &message, deque &details) { - pClass cls = res.r->getClassRef(); + pClass cls = res.r->getClassRef(true); if (!cls) return; @@ -605,10 +605,10 @@ void SpeakerMonitor::getMessage(const oEvent::ResultEvent &res, details.push_back(share); } } - if (changeover && res.r->getTeam() && res.r->getTeam()->getClassRef() != 0) { + if (changeover && res.r->getTeam() && res.r->getTeam()->getClassRef(true) != 0) { wstring vxl = L"skickar ut X.#"; pTeam t = res.r->getTeam(); - pClass cls = t->getClassRef(); + pClass cls = t->getClassRef(true); bool second = false; int nextLeg = cls->getNextBaseLeg(res.r->getLegNumber()); if (nextLeg > 0) { diff --git a/code/swedish.lng b/code/swedish.lng index aaf5424..0f38a5b 100644 --- a/code/swedish.lng +++ b/code/swedish.lng @@ -2279,3 +2279,19 @@ ask:updatetimes = Vill du behÃ¥lla nuvarande starttider, om möjligt? Svara nej X har en tid (Y) som inte är kompatibel med förändringen = X har en tid (Y) som inte är kompatibel med förändringen warn:latestarttime = Att använda starttider mer än X timmar efter nolltiden rekommenderas inte eftersom äldre SI brickor endast har en 12-timmarsklocka.\n\nVill du ändÃ¥ använda starttiden? Anm. tid = Anm. tid +Ekonomihantering, X = Ekonomihantering, X +Manuellt gjorda justeringar = Manuellt gjorda justeringar +Antal förfrÃ¥gningar: X = Antal förfrÃ¥gningar: X +Genomsnittlig svarstid: X ms = Genomsnittlig svarstid: X ms +Informationsserver = Informationsserver +Längsta svarstid: X ms = Längsta svarstid: X ms +MeOS Informationsserver REST-API = MeOS Informationsserver REST-API +RestService = RestService +Server startad pÃ¥ port X = Server startad pÃ¥ port X +Testa servern = Testa servern +help:rest = MeOS REST API lÃ¥ter dig komma Ã¥t tävlingsdata via en webbanslutning. Du kan visa resultatlistor direkt i en webbläsare, men du kan ocksÃ¥ komma Ã¥t tävlingsdata och resultat i XML-format för vidare behandling andra program och appar. +Server startad pÃ¥ X = Server startad pÃ¥ X +Inconsistent qualification rule, X = Inkonsekvent kvalifikationsregel, X +help:LockStartList = MeOS uppdaterar inte löparna i en lÃ¥st klass även om kvalificeringsresultaten ändras. +Kval-Final-Schema = Kval-Final-Schema +LÃ¥s startlista = LÃ¥s startlista