/************************************************************************ MeOS - Orienteering Software Copyright (C) 2009-2024 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 ************************************************************************/ // oControl.cpp: implementation of the oControl class. // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include #include "oControl.h" #include "oEvent.h" #include "gdioutput.h" #include "meos_util.h" #include #include "Localizer.h" #include "Table.h" #include "MeOSFeatures.h" #include ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// using namespace std; oControl::oControl(oEvent* poe) : oBase(poe) { getDI().initData(); nNumbers = 0; Status = ControlStatus::StatusOK; tMissedTimeMax = 0; tMissedTimeTotal = 0; tNumVisitorsActual = 0; tNumVisitorsExpected = 0; tMissedTimeMedian = 0; tMistakeQuotient = 0; tNumRunnersRemaining = 0; tStatDataRevision = -1; tHasFreePunchLabel = false; tNumberDuplicates = 0; } oControl::oControl(oEvent* poe, int id) : oBase(poe) { Id = id; getDI().initData(); nNumbers = 0; Status = ControlStatus::StatusOK; tMissedTimeMax = 0; tMissedTimeTotal = 0; tNumVisitorsActual = 0; tNumVisitorsExpected = 0; tMistakeQuotient = 0; tMissedTimeMedian = 0; tNumRunnersRemaining = 0; tStatDataRevision = -1; tHasFreePunchLabel = false; tNumberDuplicates = 0; } oControl::~oControl() { } pair oControl::getIdIndexFromCourseControlId(int courseControlId) { return make_pair(courseControlId % 100000, courseControlId / 100000); } int oControl::getCourseControlIdFromIdIndex(int controlId, int index) { assert(controlId < 100000); return controlId + index * 100000; } bool oControl::write(xmlparser &xml) { if (Removed) return true; xml.startTag("Control"); xml.write("Id", Id); xml.write("Updated", getStamp()); xml.write("Name", Name); xml.write("Numbers", codeNumbers()); xml.write("Status", int(Status)); getDI().write(xml); xml.endTag(); return true; } void oControl::set(int pId, int pNumber, wstring pName) { Id=pId; Numbers[0]=pNumber; nNumbers=1; Name=pName; updateChanged(); } void oControl::setStatus(ControlStatus st){ if (st!=Status){ Status=st; updateChanged(); } } void oControl::setName(wstring name) { if (name != getName()) { Name = name; updateChanged(); } } void oControl::set(const xmlobject* xo) { xmlList xl; xo->getObjects(xl); nNumbers = 0; Numbers[0] = 0; xmlList::const_iterator it; for (it = xl.begin(); it != xl.end(); ++it) { if (it->is("Id")) { Id = it->getInt(); } else if (it->is("Number")) { Numbers[0] = it->getInt(); nNumbers = 1; } else if (it->is("Numbers")) { decodeNumbers(it->getRawStr()); } else if (it->is("Status")) { Status = (ControlStatus)it->getInt(); } else if (it->is("Name")) { Name = it->getWStr(); if (Name.size() > 1 && Name.at(0) == '%') { Name = lang.tl(Name.substr(1)); } } else if (it->is("Updated")) { Modified.setStamp(it->getRawStr()); } else if (it->is("oData")) { getDI().set(*it); } } } int oControl::getFirstNumber() const { if (nNumbers > 0) return Numbers[0]; else return 0; } wstring oControl::getString() { wstring num; if (Status == ControlStatus::StatusMultiple) num = codeNumbers('+'); else if (Status == ControlStatus::StatusRogaining || Status == ControlStatus::StatusRogainingRequired) num = codeNumbers('|') + L", " + itow(getRogainingPoints()) + L"p"; else num = codeNumbers('|'); if (Status == ControlStatus::StatusBad || Status == ControlStatus::StatusBadNoTiming) return L"\u26A0" + num; if (Status == ControlStatus::StatusOptional) return L"\u2b59" + num; return num; } wstring oControl::getLongString() { if (Status == ControlStatus::StatusOK || Status == ControlStatus::StatusNoTiming) { if (nNumbers == 1) return codeNumbers('|'); else return wstring(lang.tl("VALFRI(")) + codeNumbers(',') + L")"; } else if (Status == ControlStatus::StatusMultiple) { return wstring(lang.tl("ALLA(")) + codeNumbers(',') + L")"; } else if (Status == ControlStatus::StatusRogaining || Status == ControlStatus::StatusRogainingRequired) return wstring(lang.tl("RG(")) + codeNumbers(',') + L"|" + itow(getRogainingPoints()) + L"p)"; else return wstring(lang.tl("TRASIG(")) + codeNumbers(',') + L")"; } bool oControl::hasNumber(int i) { for (int n = 0; n < nNumbers; n++) if (Numbers[n] == i) { // Mark this number as checked checkedNumbers[n] = true; return true; } if (nNumbers > 0) return false; else return true; } bool oControl::uncheckNumber(int i) { for(int n=0;n0) return false; else return true; } int oControl::getNumMulti() { if (Status== ControlStatus::StatusMultiple) return nNumbers; else return 1; } wstring oControl::codeNumbers(char sep) const { wstring n; wchar_t bf[16]; for(int i=0;i0 && cid<1024 && nNumbers<32) Numbers[nNumbers++]=cid; } if (nNumbers==0){ Numbers[0] = Id; nNumbers = 1; return false; } else return true; } bool oControl::setNumbers(const wstring &numbers) { int nn=nNumbers; int bf[32]; if (unsigned(nNumbers)<32) memcpy(bf, Numbers, sizeof(int)*nNumbers); string nnumbers(numbers.begin(), numbers.end()); bool success=decodeNumbers(nnumbers); if (!success) { memcpy(Numbers, bf, sizeof(int)*nn); nNumbers = nn; } if (nNumbers!=nn || memcmp(bf, Numbers, sizeof(int)*nNumbers)!=0) { updateChanged(); oe->punchIndex.clear(); } return success; } wstring oControl::getName() const { if (!Name.empty()) return Name; else { wchar_t bf[16]; swprintf_s(bf, L"[%d]", Id); return bf; } } wstring oControl::getIdS() const { if (!Name.empty()) return Name; else { wchar_t bf[16]; swprintf_s(bf, L"%d", Id); return bf; } } oDataContainer &oControl::getDataBuffers(pvoid &data, pvoid &olddata, pvectorstr &strData) const { data = (pvoid)oData; olddata = (pvoid)oDataOld; strData = 0; return *oe->oControlData; } const vector>& oEvent::fillControls(vector< pair>& out, oEvent::ControlType type) { out.clear(); synchronizeList({ oListId::oLControlId, oListId::oLCardId, oListId::oLPunchId }); Controls.sort(); if (type == oEvent::ControlType::CourseControl) { vector dmy; getControls(dmy, true); // Update data } map, pControl> existingTypeUnits; wstring b; wchar_t bf[256]; for (auto it = Controls.begin(); it != Controls.end(); ++it) { if (!it->Removed) { b.clear(); if (type == oEvent::ControlType::All) { if (it->isUnit()) { existingTypeUnits.emplace(make_pair(it->getUnitType(), it->getUnitCode()), &*it); continue; } if (oControl::isSpecialControl(it->Status)) { b += it->Name; } else { if (it->Status == oControl::ControlStatus::StatusOK) b += L"OK\t"; else if (it->Status == oControl::ControlStatus::StatusNoTiming) b += L"\u231B\t"; else if (it->Status == oControl::ControlStatus::StatusMultiple) b += L"\u25ef\u25ef\t"; else if (it->Status == oControl::ControlStatus::StatusRogaining) b += L"R\u25ef\t"; else if (it->Status == oControl::ControlStatus::StatusRogainingRequired) b += L"R!\u25ef\t"; else if (it->Status == oControl::ControlStatus::StatusBad) b += L"\u26A0\t"; else if (it->Status == oControl::ControlStatus::StatusBadNoTiming) b += L"\u26A0\u231B\t"; else if (it->Status == oControl::ControlStatus::StatusOptional) b += L"\u26aa\u2b59\t"; else b += L"[ ]\t"; b += it->codeNumbers(' '); if (it->nNumbers == 0 || it->Id != it->Numbers[0]) { b += L" (" + itow(it->Id) + L")"; } if (it->Status == oControl::ControlStatus::StatusRogaining || it->Status == oControl::ControlStatus::StatusRogainingRequired) b += L"\t(" + itow(it->getRogainingPoints()) + L"p)"; else if (it->Name.length() > 0) { b += L"\t(" + it->Name + L")"; } } out.emplace_back(b, it->Id); } else if (type == oEvent::ControlType::RealControl) { if (oControl::isSpecialControl(it->Status)) continue; swprintf_s(bf, lang.tl("Kontroll %s").c_str(), it->codeNumbers(' ').c_str()); b = bf; if (!it->Name.empty()) b += L" (" + it->Name + L")"; out.emplace_back(b, it->Id); } else if (type == oEvent::ControlType::CourseControl) { if (oControl::isSpecialControl(it->Status)) continue; for (int i = 0; i < it->getNumberDuplicates(); i++) { swprintf_s(bf, lang.tl("Kontroll %s").c_str(), it->codeNumbers(' ').c_str()); b = bf; if (it->getNumberDuplicates() > 1) b += L"-" + itow(i + 1); if (!it->Name.empty()) b += L" (" + it->Name + L")"; out.emplace_back(b, oControl::getCourseControlIdFromIdIndex(it->Id, i)); } } } } if (type == oEvent::ControlType::All) { vector> typeUnit; oe->getExistingUnits(typeUnit); for (auto& tu : typeUnit) { auto res = existingTypeUnits.find(tu); if (res == existingTypeUnits.end()) { wstring name; if (tu.first == oPunch::SpecialPunch::PunchFinish) name = lang.tl(L"Målenhet", true) + L" " + itow(tu.second); else if (tu.first == oPunch::SpecialPunch::PunchStart) name = lang.tl(L"Startenhet", true) + L" " + itow(tu.second); else if (tu.first == oPunch::SpecialPunch::PunchCheck) name = lang.tl(L"Checkenhet", true) + L" " + itow(tu.second); out.emplace_back(name, tu.first * 1100000 + tu.second); } else { wstring name = res->second->getName() + L"\t" + res->second->getTimeAdjustS(); out.emplace_back(name, res->second->getId()); } } } return out; } const vector< pair > &oEvent::fillControlTypes(vector< pair > &out) { oControlList::iterator it; synchronizeList(oListId::oLControlId); out.clear(); //gdi.clearList(name); out.clear(); set sicodes; for (it=Controls.begin(); it != Controls.end(); ++it){ if (!it->Removed) { for (int k=0;knNumbers;k++) sicodes.insert(it->Numbers[k]); } } set::iterator sit; wchar_t bf[32]; /*gdi.addItem(name, lang.tl("Check"), oPunch::PunchCheck); gdi.addItem(name, lang.tl("Start"), oPunch::PunchStart); gdi.addItem(name, lang.tl("Mål"), oPunch::PunchFinish);*/ out.push_back(make_pair(lang.tl("Check"), oPunch::PunchCheck)); out.push_back(make_pair(lang.tl("Start"), oPunch::PunchStart)); out.push_back(make_pair(lang.tl("Mål"), oPunch::PunchFinish)); for (sit = sicodes.begin(); sit!=sicodes.end(); ++sit) { swprintf_s(bf, lang.tl("Kontroll %s").c_str(), itow(*sit).c_str()); //gdi.addItem(name, bf, *sit); out.push_back(make_pair(bf, *sit)); } return out; } void oControl::setupCache() const { if (tCache.dataRevision != oe->dataRevision) { tCache.timeAdjust = getDCI().getInt("TimeAdjust"); tCache.minTime = getDCI().getInt("MinTime"); tCache.dataRevision = oe->dataRevision; } } void oControl::clearCache() { tCache.dataRevision = -1; } int oControl::getMinTime() const { if (Status == ControlStatus::StatusNoTiming || Status == ControlStatus::StatusBadNoTiming) return 0; setupCache(); return tCache.minTime; } int oControl::getTimeAdjust() const { setupCache(); return tCache.timeAdjust; } wstring oControl::getTimeAdjustS() const { return formatTimeMS(getTimeAdjust(), false, SubSecond::Auto); } wstring oControl::getMinTimeS() const { if (getMinTime()>0) return formatTimeMS(getMinTime(), false, SubSecond::Auto); else return makeDash(L"-"); } int oControl::getRogainingPoints() const { return getDCI().getInt("Rogaining"); } wstring oControl::getRogainingPointsS() const { int pt = getRogainingPoints(); return pt != 0 ? itow(pt) : L""; } bool oControl::setTimeAdjust(int v) { if (v == NOTIME) v = 0; return getDI().setInt("TimeAdjust", v); } void oControl::setRadio(bool r) { // 1 means radio, 2 means no radio, 0 means default getDI().setInt("Radio", r ? 1 : 2); } bool oControl::isValidRadio() const { int flag = getDCI().getInt("Radio"); if (flag == 0) return (tHasFreePunchLabel || hasName()) && getStatus() == oControl::ControlStatus::StatusOK; else return flag == 1; } bool oControl::setTimeAdjust(const wstring &s) { return setTimeAdjust(convertAbsoluteTimeMS(s)); } void oControl::setMinTime(int v) { if (v<0 || v == NOTIME) v = 0; getDI().setInt("MinTime", v); } void oControl::setMinTime(const wstring &s) { setMinTime(convertAbsoluteTimeMS(s)); } void oControl::setRogainingPoints(int v) { getDI().setInt("Rogaining", v); } void oControl::setRogainingPoints(const string &s) { setRogainingPoints(atoi(s.c_str())); } void oControl::startCheckControl() { //Mark all numbers as unchecked. for (int k=0;k &mp, bool supportRogaining) const { if (controlCompleted(supportRogaining)) return; for (int k=0;kgetRevision()) oe->setupControlStatistics(); return tMissedTimeTotal; } int oControl::getMissedTimeMax() const { if (tStatDataRevision != oe->getRevision()) oe->setupControlStatistics(); return tMissedTimeMax; } int oControl::getMissedTimeMedian() const { if (tStatDataRevision != oe->getRevision()) oe->setupControlStatistics(); return tMissedTimeMedian; } int oControl::getMistakeQuotient() const { if (tStatDataRevision != oe->getRevision()) oe->setupControlStatistics(); return tMistakeQuotient; } int oControl::getNumVisitors(bool actulaVisits) const { if (tStatDataRevision != oe->getRevision()) oe->setupControlStatistics(); if (actulaVisits) return tNumVisitorsActual; else return tNumVisitorsExpected; } int oControl::getNumRunnersRemaining() const { if (tStatDataRevision != oe->getRevision()) oe->setupControlStatistics(); return tNumRunnersRemaining; } void oEvent::setupControlStatistics() const { // Reset all times for (oControlList::const_iterator it = Controls.begin(); it != Controls.end(); ++it) { it->tMissedTimeMax = 0; it->tMissedTimeTotal = 0; it->tNumVisitorsActual = 0; it->tNumVisitorsExpected = 0; it->tNumRunnersRemaining = 0; it->tMissedTimeMedian = 0; it->tMistakeQuotient = 0; it->tStatDataRevision = dataRevision; // Mark as up-to-date } map > > lostPerControl; // First is "actual" misses, vector delta; for (auto it = Runners.begin(); it != Runners.end(); ++it) { if (it->isRemoved()) continue; pCourse pc = it->getCourse(true); if (!pc) continue; it->getSplitAnalysis(delta); int nc = pc->getNumControls(); if (unsigned(nc) < delta.size()) { for (int i = 0; igetControl(i); if (ctrl && delta[i]>0) { if (delta[i] < 10 * timeConstMinute) ctrl->tMissedTimeTotal += delta[i]; else ctrl->tMissedTimeTotal += 10 * timeConstMinute; // Use max 10 minutes ctrl->tMissedTimeMax = max(ctrl->tMissedTimeMax, delta[i]); } if (delta[i] > 0) { lostPerControl[ctrl->getId()].second.push_back(delta[i]); ++lostPerControl[ctrl->getId()].first; } ctrl->tNumVisitorsActual++; } } if (!it->isVacant() && it->getStatus() != StatusDNS && it->getStatus() != StatusCANCEL && it->getStatus() != StatusNotCompetiting) { for (int i = 0; i < nc; i++) { pControl ctrl = pc->getControl(i); ctrl->tNumVisitorsExpected++; if (it->getStatus() == StatusUnknown) ctrl->tNumRunnersRemaining++; } } } for (oControlList::const_iterator it = Controls.begin(); it != Controls.end(); ++it) { if (!it->isRemoved()) { int id = it->getId(); auto res = lostPerControl.find(id); if (res != lostPerControl.end()) { if (!res->second.second.empty()) { sort(res->second.second.begin(), res->second.second.end()); int nMistakes = res->second.second.size(); int avg; if (nMistakes % 2 == 1) avg = res->second.second[nMistakes / 2]; else avg = (res->second.second[nMistakes / 2] + res->second.second[nMistakes / 2 -1]) / 2; it->tMissedTimeMedian = avg; } if (it->tNumVisitorsActual > 0) it->tMistakeQuotient = (int)round((100.00 * res->second.first) / double(it->tNumVisitorsActual)); else it->tMistakeQuotient = 0; } else { it->tMistakeQuotient = 0; it->tMissedTimeMedian = 0; } } } } bool oEvent::hasRogaining() const { oControlList::const_iterator it; for (it=Controls.begin(); it != Controls.end(); ++it) { if (!it->Removed && it->isRogaining(true)) return true; } return false; } const wstring oControl::getStatusS() const { //enum ControlStatus {StatusOK=0, StatusBad=1, StatusMultiple=2, // StatusStart = 4, StatusFinish = 5, StatusRogaining = 6}; switch (getStatus()) { case ControlStatus::StatusOK: return lang.tl("OK"); case ControlStatus::StatusBad: return lang.tl("Trasig"); case ControlStatus::StatusOptional: return lang.tl("Valfri"); case ControlStatus::StatusMultiple: return lang.tl("Multipel"); case ControlStatus::StatusRogaining: return lang.tl("Rogaining"); case ControlStatus::StatusRogainingRequired: return lang.tl("Rogaining Obligatorisk"); case ControlStatus::StatusStart: return lang.tl("Start"); case ControlStatus::StatusCheck: return lang.tl("Check"); case ControlStatus::StatusFinish: return lang.tl("Mål"); case ControlStatus::StatusNoTiming: return lang.tl("Utan tidtagning"); case ControlStatus::StatusBadNoTiming: return lang.tl("Försvunnen"); default: return lang.tl("Okänd"); } } void oEvent::fillControlStatus(gdioutput &gdi, const string& id) const { vector< pair > d; oe->fillControlStatus(d); gdi.setItems(id, d); } const vector< pair > &oEvent::fillControlStatus(vector< pair > &out) const { out.clear(); out.emplace_back(lang.tl(L"OK"), size_t(oControl::ControlStatus::StatusOK)); out.emplace_back(lang.tl(L"Multipel"), size_t(oControl::ControlStatus::StatusMultiple)); if (getMeOSFeatures().hasFeature(MeOSFeatures::Rogaining)) { out.emplace_back(lang.tl(L"Rogaining"), size_t(oControl::ControlStatus::StatusRogaining)); out.emplace_back(lang.tl(L"Rogaining Obligatorisk"), size_t(oControl::ControlStatus::StatusRogainingRequired)); } out.emplace_back(lang.tl(L"Utan tidtagning"), size_t(oControl::ControlStatus::StatusNoTiming)); out.emplace_back(lang.tl(L"Trasig"), size_t(oControl::ControlStatus::StatusBad)); out.emplace_back(lang.tl(L"Försvunnen"), size_t(oControl::ControlStatus::StatusBadNoTiming)); out.emplace_back(lang.tl(L"Valfri"), size_t(oControl::ControlStatus::StatusOptional)); return out; } const shared_ptr &oControl::getTable(oEvent *oe) { if (!oe->hasTable("control")) { auto table = make_shared
(oe, 20, L"Kontroller", "controls"); table->addColumn("Id", 70, true, true); table->addColumn("Ändrad", 70, false); table->addColumn("Namn", 150, false); table->addColumn("Status", 70, false); table->addColumn("Stämpelkoder", 100, true); table->addColumn("Antal löpare", 70, true, true); table->addColumn("Bomtid (max)", 70, true, true); table->addColumn("Bomtid (medel)", 70, true, true); table->addColumn("Bomtid (median)", 70, true, true); oe->oControlData->buildTableCol(table.get()); oe->setTable("control", table); table->setTableProp(Table::CAN_DELETE); } return oe->getTable("control"); } void oEvent::generateControlTableData(Table &table, oControl *addControl) { if (addControl) { addControl->addTableRow(table); return; } synchronizeList(oListId::oLControlId); oControlList::iterator it; for (it=Controls.begin(); it != Controls.end(); ++it){ if (!it->isRemoved()){ it->addTableRow(table); } } } void oControl::addTableRow(Table &table) const { oControl &it = *pControl(this); table.addRow(getId(), &it); int row = 0; table.set(row++, it, TID_ID, itow(getId()), false); table.set(row++, it, TID_MODIFIED, getTimeStamp(), false); table.set(row++, it, TID_CONTROL, getName(), true); bool canEdit = !isSpecialControl(getStatus()); table.set(row++, it, TID_STATUS, getStatusS(), canEdit, cellSelection); table.set(row++, it, TID_CODES, codeNumbers(), true); int nv = getNumVisitors(true); table.set(row++, it, 50, itow(nv), false); table.set(row++, it, 51, nv > 0 ? formatTime(getMissedTimeMax(), SubSecond::Off) : L"-", false); table.set(row++, it, 52, nv > 0 ? formatTime(getMissedTimeTotal()/nv, SubSecond::Off) : L"-", false); table.set(row++, it, 53, nv > 0 ? formatTime(getMissedTimeMedian(), SubSecond::Off) : L"-", false); oe->oControlData->fillTableCol(it, table, true); } pair oControl::inputData(int id, const wstring &input, int inputId, wstring &output, bool noUpdate) { synchronize(false); if (id>1000) { return oe->oControlData->inputData(this, id, input, inputId, output, noUpdate); } switch(id) { case TID_CONTROL: setName(input); synchronize(); output=getName(); break; case TID_STATUS: setStatus(ControlStatus(inputId)); synchronize(true); output = getStatusS(); break; case TID_CODES: setNumbers(input); synchronize(true); output = codeNumbers(); break; } return make_pair(0, false); } void oControl::fillInput(int id, vector< pair > &out, size_t &selected) { if (id>1000) { oe->oControlData->fillInput(this, id, 0, out, selected); return; } if (id==TID_STATUS) { oe->fillControlStatus(out); selected = size_t(getStatus()); } } void oControl::remove() { if (oe) oe->removeControl(Id); } bool oControl::canRemove() const { return !oe->isControlUsed(Id); } void oEvent::getControls(vector &c, bool calculateCourseControls) const { c.clear(); if (calculateCourseControls) { unordered_map cById; for (oControlList::const_iterator it = Controls.begin(); it != Controls.end(); ++it) { if (it->isRemoved()) continue; it->tNumberDuplicates = 0; cById[it->getId()] = pControl(&*it); } for (oCourseList::const_iterator it = Courses.begin(); it != Courses.end(); ++it) { map count; for (int i = 0; i < it->nControls; i++) { ++count[it->Controls[i]->getId()]; } for (map::iterator it = count.begin(); it != count.end(); ++it) { unordered_map::iterator res = cById.find(it->first); if (res != cById.end()) { res->second->tNumberDuplicates = max(res->second->tNumberDuplicates, it->second); } } } } for (oControlList::const_iterator it = Controls.begin(); it != Controls.end(); ++it) { if (it->isRemoved()) continue; c.push_back(pControl(&*it)); } } void oControl::getNumbers(vector &numbers) const { numbers.resize(nNumbers); for (int i = 0; i < nNumbers; i++) { numbers[i] = Numbers[i]; } } void oControl::changedObject() { if (oe) oe->globalModification = true; oe->sqlControls.changed = true; } int oControl::getNumberDuplicates() const { return tNumberDuplicates; } void oControl::getCourseControls(vector &cc) const { cc.resize(tNumberDuplicates); for (int i = 0; i < tNumberDuplicates; i++) { cc[i] = getCourseControlIdFromIdIndex(Id, i); } } void oControl::getCourses(vector &crs) const { crs.clear(); for (oCourseList::const_iterator it = oe->Courses.begin(); it != oe->Courses.end(); it++) { if (it->isRemoved()) continue; if (it->hasControl(this)) crs.push_back(pCourse(&*it)); } } void oControl::getClasses(vector &cls) const { vector crs; getCourses(crs); std::set cid; for (size_t k = 0; k< crs.size(); k++) { cid.insert(crs[k]->getId()); } for (oClassList::const_iterator it = oe->Classes.begin(); it != oe->Classes.end(); it++) { if (it->isRemoved()) continue; if (it->hasAnyCourse(cid)) 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; } bool oControl::isUnit() const { if (isSpecialControl(getStatus())) { return getUnitCode() > 0; } return false; } int oControl::getUnitCode() const { return getDCI().getInt("Unit"); } oPunch::SpecialPunch oControl::getUnitType() const { switch (getStatus()) { case ControlStatus::StatusFinish: return oPunch::SpecialPunch::PunchFinish; case ControlStatus::StatusStart: return oPunch::SpecialPunch::PunchStart; case ControlStatus::StatusCheck: return oPunch::SpecialPunch::PunchCheck; } throw exception(); } void oEvent::clearUnitAdjustmentCache() { typeUnitPunchTimeAdjustment.first = -1; } int oEvent::getUnitAdjustment(oPunch::SpecialPunch type, int unit) const { if (typeUnitPunchTimeAdjustment.first != dataRevision) { typeUnitPunchTimeAdjustment.second.clear(); for (auto& c : Controls) { if (!c.isRemoved() && c.isUnit()) { int adjust = c.getTimeAdjust(); if (adjust != 0 && adjust != NOTIME) typeUnitPunchTimeAdjustment.second.emplace(make_pair(c.getUnitType(), c.getUnitCode()), adjust); } } typeUnitPunchTimeAdjustment.first = dataRevision; } auto res = typeUnitPunchTimeAdjustment.second.find(make_pair(type, unit)); return res != typeUnitPunchTimeAdjustment.second.end() ? res->second : 0; } pControl oEvent::getControl(int Id) const { return const_cast(this)->getControl(Id, false, false); } pControl oEvent::getControlByType(int type) const { for (auto& c : Controls) { if (!c.isRemoved() && c.getFirstNumber() == type) return pControl(&c); } return nullptr; } pControl oEvent::getControl(int Id, bool create, bool includeVirtual) { oControlList::const_iterator it; for (it = Controls.begin(); it != Controls.end(); ++it) { if (it->Id == Id && !it->isRemoved()) return pControl(&*it); } if (!create && Id > 1100000 && includeVirtual) { int unit = Id % 10000; int type = (Id - unit) / 1100000; if ((type == oPunch::SpecialPunch::PunchFinish || type == oPunch::SpecialPunch::PunchStart || type == oPunch::SpecialPunch::PunchCheck) && Id == type * 1100000 + unit) { tmpControl = make_shared(this); tmpControl->setLocalObject(); string name; oControl::ControlStatus st; switch (type) { case oPunch::SpecialPunch::PunchFinish: name = "Målenhet"; st = oControl::ControlStatus::StatusFinish; break; case oPunch::SpecialPunch::PunchStart: name = "Startenhet"; st = oControl::ControlStatus::StatusStart; break; case oPunch::SpecialPunch::PunchCheck: name = "Checkenhet"; st = oControl::ControlStatus::StatusCheck; break; default: throw 0; } tmpControl->setName(lang.tl(name) + L" " + itow(unit)); tmpControl->getDI().setInt("Unit", unit); tmpControl->setNumbers(itow(type)); tmpControl->Id = Id; tmpControl->setStatus(st); return tmpControl.get(); } } if (!create || Id <= 0) return nullptr; //Not found. Auto add... return addControl(Id, Id, L""); } void oEvent::getExistingUnits(vector>& typeUnit) { oFreePunch::rehashPunches(*oe, 0, nullptr); typeUnit.clear(); set startUnit, finishUnit, checkUnit; auto start = punchIndex.find(oPunch::SpecialPunch::PunchStart); if (start != punchIndex.end()) { for (auto& p : start->second) { int pu = p.second->getPunchUnit(); if (pu != 0) startUnit.insert(pu); } } auto finish = punchIndex.find(oPunch::SpecialPunch::PunchFinish); if (finish != punchIndex.end()) { for (auto& p : finish->second) { int pu = p.second->getPunchUnit(); if (pu != 0) finishUnit.insert(pu); } } auto check = punchIndex.find(oPunch::SpecialPunch::PunchCheck); if (check != punchIndex.end()) { for (auto& p : check->second) { int pu = p.second->getPunchUnit(); if (pu != 0) checkUnit.insert(pu); } } for (auto& c : Cards) { if (c.isRemoved()) continue; for (auto &p : c.punches) { if (p.getTypeCode() == oPunch::SpecialPunch::PunchStart) { int pu = p.getPunchUnit(); if (pu != 0) startUnit.insert(pu); } else if (p.getTypeCode() == oPunch::SpecialPunch::PunchFinish) { int pu = p.getPunchUnit(); if (pu != 0) finishUnit.insert(pu); } if (p.getTypeCode() == oPunch::SpecialPunch::PunchCheck) { int pu = p.getPunchUnit(); if (pu != 0) checkUnit.insert(pu); } } } for (int u : startUnit) typeUnit.emplace_back(oPunch::SpecialPunch::PunchStart, u); for (int u : finishUnit) typeUnit.emplace_back(oPunch::SpecialPunch::PunchFinish, u); for (int u : checkUnit) typeUnit.emplace_back(oPunch::SpecialPunch::PunchCheck, u); }