meos-2024/code/oClass.h
2021-12-13 19:28:59 +01:00

747 lines
23 KiB
C++

#pragma once
/************************************************************************
MeOS - Orienteering Software
Copyright (C) 2009-2021 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 <http://www.gnu.org/licenses/>.
Melin Software HB - software@melin.nu - www.melin.nu
Eksoppsvägen 16, SE-75646 UPPSALA, Sweden
************************************************************************/
#include "oCourse.h"
#include <vector>
#include <set>
#include <map>
#include "inthashmap.h"
class oClass;
typedef oClass* pClass;
class oDataInterface;
class GeneralResult;
const int MaxClassId = 1000000;
enum PersonSex;
enum StartTypes {
STTime=0,
STChange,
STDrawn,
STHunting,
ST_max
};
enum { nStartTypes = ST_max };
enum LegTypes {
LTNormal=0,
LTParallel,
LTExtra,
LTSum,
LTIgnore,
LTParallelOptional,
LTGroup,
LT_max,
};
enum { nLegTypes = LT_max };
enum BibMode {
BibUndefined = -1,
BibSame = 0,
BibAdd = 1,
BibFree = 2,
BibLeg = 3,
};
enum AutoBibType {
AutoBibManual = 0,
AutoBibConsecutive = 1,
AutoBibNone = 2,
AutoBibExplicit = 3
};
enum ClassSplitMethod {
SplitRandom,
SplitClub,
SplitRank,
SplitRankEven,
SplitResult,
SplitTime,
SplitResultEven,
SplitTimeEven,
};
enum ClassSeedMethod {
SeedRank,
SeedResult,
SeedTime,
SeedPoints
};
#ifdef DODECLARETYPESYMBOLS
const char *StartTypeNames[4]={"ST", "CH", "DR", "HU"};
const char *LegTypeNames[7]={"NO", "PA", "EX", "SM", "IG", "PO", "GP"};
#endif
struct oLegInfo {
StartTypes startMethod;
LegTypes legMethod;
bool isParallel() const {return legMethod == LTParallel || legMethod == LTParallelOptional;}
bool isOptional() const {return legMethod == LTParallelOptional || legMethod == LTExtra || legMethod == LTIgnore;}
//Interpreteation depends. Can be starttime/first start
//or number of earlier legs to consider.
int legStartData;
int legRestartTime;
int legRopeTime;
int duplicateRunner;
// Transient, deducable data
int trueSubLeg;
int trueLeg;
string displayLeg;
oLegInfo():startMethod(STTime), legMethod(LTNormal), legStartData(0),
legRestartTime(0), legRopeTime(0), duplicateRunner(-1) {}
string codeLegMethod() const;
void importLegMethod(const string &courses);
};
struct ClassResultInfo {
ClassResultInfo() : nUnknown(0), nFinished(0), lastStartTime(0) {}
int nUnknown;
int nFinished;
int lastStartTime;
};
class QualificationFinal;
enum ClassType {
oClassIndividual = 1,
oClassPatrol = 2,
oClassRelay = 3,
oClassIndividRelay = 4,
oClassKnockout = 5
};
enum ClassMetaType {ctElite, ctNormal, ctYouth, ctTraining,
ctExercise, ctOpen, ctUnknown};
class Table;
class oClass : public oBase
{
public:
enum class ClassStatus {Normal, Invalid, InvalidRefund};
enum class AllowRecompute {Yes, No, NoUseOld };
static void getSplitMethods(vector< pair<wstring, size_t> > &methods);
static void getSeedingMethods(vector< pair<wstring, size_t> > &methods);
protected:
wstring Name;
pCourse Course;
vector< vector<pCourse> > MultiCourse;
vector< oLegInfo > legInfo;
//First: best time on leg
//Second: Total leader time (total leader)
struct LeaderInfo {
private:
int bestTimeOnLeg;
int bestTimeOnLegComputed; // Computed be default result module
int totalLeaderTime;
int totalLeaderTimeComputed; // Computed be default result module
int totalLeaderTimeInput; //Team total including input
int totalLeaderTimeInputComputed; //Team total including input
int inputTime;
bool complete = false;
public:
LeaderInfo() {
reset();
}
void reset() {
bestTimeOnLeg = -1;
bestTimeOnLegComputed = -1;
totalLeaderTime = -1;
totalLeaderTimeComputed = -1;
inputTime = -1;
totalLeaderTimeInput = -1;
totalLeaderTimeInputComputed = -1;
complete = false;
}
void updateFrom(const LeaderInfo& i) {
if (i.complete) {
if (i.bestTimeOnLeg != -1)
bestTimeOnLeg = i.bestTimeOnLeg;
if (i.bestTimeOnLegComputed != -1)
bestTimeOnLegComputed = i.bestTimeOnLegComputed;
if (i.totalLeaderTime != -1)
totalLeaderTime = i.totalLeaderTime;
if (i.totalLeaderTimeComputed != -1)
totalLeaderTimeComputed = i.totalLeaderTimeComputed;
if (i.inputTime != -1)
inputTime = i.inputTime;
if (i.totalLeaderTimeInput != -1)
totalLeaderTimeInput = i.totalLeaderTimeInput;
if (i.totalLeaderTimeInputComputed != -1)
totalLeaderTimeInputComputed = i.totalLeaderTimeInputComputed;
}
}
enum class Type {
Leg,
Total,
TotalInput,
Input,
};
int getInputTime() const {
return inputTime;
}
void resetComputed(Type t);
bool update(int rt, Type t);
bool updateComputed(int rt, Type t);
int getLeader(Type t, bool computed) const;
void setComplete() {
complete = true;
}
// For non-team classes, input is the same as total input and computed total input
void copyInputToTotalInput();
};
void updateLeaderTimes() const;
LeaderInfo &getLeaderInfo(AllowRecompute recompute, int leg) const;
mutable int leaderTimeVersion = -1;
mutable vector<LeaderInfo> tLeaderTime;
mutable vector<LeaderInfo> tLeaderTimeOld;
mutable map<int, int> tBestTimePerCourse;
int tSplitRevision;
map<int, vector<int> > tSplitAnalysisData;
map<int, vector<int> > tCourseLegLeaderTime;
map<int, vector<int> > tCourseAccLegLeaderTime;
mutable vector<int> tFirstStart;
mutable vector<int> tLastStart;
mutable ClassStatus tStatus;
mutable int tStatusRevision;
// A map with places for given times on given legs
inthashmap *tLegTimeToPlace;
inthashmap *tLegAccTimeToPlace;
void insertLegPlace(int from, int to, int time, int place);
void insertAccLegPlace(int courseId, int controlNo, int time, int place);
// For sub split times
int tLegLeaderTime;
mutable int tNoTiming;
mutable int tIgnoreStartPunch;
// Sort classes for this index
mutable int tSortIndex;
mutable int tMaxTime;
mutable bool isInitialized = false;
// True when courses was changed on this client. Used to update course pool bindings
bool tCoursesChanged;
// Used to force show of full multi course dialog
bool tShowMultiDialog;
static const int dataSize = 512;
int getDISize() const {return dataSize;}
BYTE oData[dataSize];
BYTE oDataOld[dataSize];
vector< vector<wstring> > oDataStr;
//Multicourse data
string codeMultiCourse() const;
//Fill courseId with id:s of used courses.
void importCourses(const vector< vector<int> > &multi);
static void parseCourses(const string &courses, vector< vector<int> > &multi, set<int> &courseId);
set<int> &getMCourseIdSet(set<int> &in) const;
//Multicourse leg methods
string codeLegMethod() const;
void importLegMethod(const string &courses);
//bool sqlChanged;
/** Pairs of changed entities. (Leg number, control id (or PunchFinish))
(-1,-1) means all (leg, -1) means all on leg.
*/
map< int, set<int> > sqlChangedControlLeg;
map< int, set<int> > sqlChangedLegControl;
void markSQLChanged(int leg, int control);
void addTableRow(Table &table) const;
pair<int, bool> inputData(int id, const wstring &input, int inputId,
wstring &output, bool noUpdate) override;
void fillInput(int id, vector<pair<wstring, size_t>> &elements, size_t &selected) override;
void exportIOFStart(xmlparser &xml);
/** Setup transient data */
void reinitialize(bool force) const;
/** Recalculate derived data */
void apply();
void calculateSplits();
void clearSplitAnalysis();
/** Map to correct leg number for diff class/runner class (for example qual/final)*/
int mapLeg(int inputLeg) const {
if (inputLeg > 0 && legInfo.size() <= 1)
return 0; // The case with different class for team/runner. Leg is an index in another class.
return inputLeg;
}
/** Info about the result in the class for each leg.
Use oEvent::analyseClassResultStatus to setup */
mutable vector<ClassResultInfo> tResultInfo;
/** Get/calculate sort index from candidate */
int getSortIndex(int candidate) const;
/** Get internal data buffers for DI */
oDataContainer &getDataBuffers(pvoid &data, pvoid &olddata, pvectorstr &strData) const;
void changedObject();
static long long setupForkKey(const vector<int> indices, const vector< vector< vector<int> > > &courseKeys, vector<int> &ws);
mutable vector<pClass> virtualClasses;
pClass parentClass;
mutable shared_ptr<QualificationFinal> qualificatonFinal;
int tMapsRemaining;
mutable int tMapsUsed;
mutable int tMapsUsedNoVacant;
// First is data revision, second is key
mutable pair<int, map<string, int>> tTypeKeyToRunnerCount;
enum CountKeyType {
All,
Finished,
ExpectedStarting,
DNS,
IncludeNotCompeting
};
static string getCountTypeKey(int leg, CountKeyType type, bool countVacant);
void configureInstance(int instance, bool allowCreation) const;
public:
static const shared_ptr<Table> &getTable(oEvent *oe);
enum TransferFlags {
FlagManualName = 1,
FlagManualFees = 2,
};
bool hasFlag(TransferFlags flag) const;
void setFlag(TransferFlags flag, bool state);
/** The master class in a qualification/final scheme. */
const pClass getParentClass() const { return parentClass; }
const QualificationFinal *getQualificationFinal() const {
reinitialize(false);
return qualificatonFinal.get();
}
void clearQualificationFinal() const;
bool isQualificationFinalClass() const {
return parentClass && parentClass->isQualificationFinalBaseClass();
}
bool isQualificationFinalBaseClass() const {
return qualificatonFinal != nullptr;
}
bool isTeamClass() const {
int ns = getNumStages();
return ns > 0 && getNumDistinctRunners() == 1;
}
/** 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);
int getDrawInterval() const;
void setDrawInterval(int st);
int getDrawVacant() const;
void setDrawVacant(int st);
int getDrawNumReserved() const;
void setDrawNumReserved(int st);
enum class DrawSpecified {
FixedTime = 1, Vacant = 2, Extra = 4
};
void setDrawSpecification(const vector<DrawSpecified> &ds);
set<DrawSpecified> getDrawSpecification() const;
/** Return an actual linear index for this class. */
int getLinearIndex(int index, bool isLinear) const;
/** Split class into subclasses. */
void splitClass(ClassSplitMethod method, const vector<int> &parts, vector<int> &outClassId);
void mergeClass(int classIdSec);
void drawSeeded(ClassSeedMethod seed, int leg, int firstStart, int interval, const vector<int> &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 hasClassGlobalDependence() const;
// Autoassign new bibs
static void extractBibPatterns(oEvent &oe, map<int, pair<wstring, int> > &patterns);
pair<int, wstring> getNextBib(map<int, pair<wstring, int> > &patterns); // Version that calculates next free bib from cached data (fast, no gap usage)
pair<int, wstring> oClass::getNextBib(); // Version that calculates next free bib (slow, but reuses gaps)
bool usesCourse(const oCourse &crs) const;
/** Returns an (overestimate) of the actual number of forks.*/
int getNumForks() const;
bool checkStartMethod();
static int extractBibPattern(const wstring &bibInfo, wchar_t pattern[32]);
bool isParallel(size_t leg) const {
if (leg < legInfo.size())
return legInfo[leg].isParallel();
else
return false;
}
bool isOptional(size_t leg) const {
if (leg < legInfo.size())
return legInfo[leg].isOptional();
else
return false;
}
pClass getVirtualClass(int instance, bool allowCreation);
const pClass getVirtualClass(int instance) const;
ClassStatus getClassStatus() const;
static void fillClassStatus(vector<pair<wstring, wstring>> &statusClass);
ClassMetaType interpretClassType() const;
int getMaximumRunnerTime() const;
void remove();
bool canRemove() const;
void forceShowMultiDialog(bool force) {tShowMultiDialog = force;}
/// Return first and last start of runners in class
void getStartRange(int leg, int &firstStart, int &lastStart) const;
/** Return true if pure rogaining class, with time limit (sort results by points) */
bool isRogaining() const;
/** Get the place for the specified leg (CommonControl of course == start/finish) and time. 0 if not found */
int getLegPlace(int from, int to, int time) const;
/** Get accumulated leg place */
int getAccLegPlace(int courseId, int controlNo, int time) const;
/** Get cached sort index */
int getSortIndex() const {return tSortIndex;};
/// Guess type from class name
void assignTypeFromName();
bool isSingleRunnerMultiStage() const;
bool wasSQLChanged(int leg, int control) const;// {return sqlChanged;}
void getStatistics(const set<int> &feeLock, int &entries, int &started) const;
int getBestInputTime(AllowRecompute recompute, int leg) const;
int getBestLegTime(AllowRecompute recompute, int leg, bool computedTime) const;
int getBestTimeCourse(AllowRecompute recompute, int courseId) const;
int getTotalLegLeaderTime(AllowRecompute recompute, int leg, bool computedTime, bool includeInput) const;
wstring getInfo() const;
// Returns true if the class has a pool of courses
bool hasCoursePool() const;
// Set whether to use a pool or not
void setCoursePool(bool p);
// Get the best matching course from a pool
pCourse selectCourseFromPool(int leg, const SICard &card) const;
// Update changed course pool
void updateChangedCoursePool();
/** Reset cache of leader times */
void resetLeaderTime() const;
ClassType getClassType() const;
bool startdataIgnored(int i) const;
bool restartIgnored(int i) const;
StartTypes getStartType(int leg) const;
LegTypes getLegType(int leg) const;
int getStartData(int leg) const;
int getRestartTime(int leg) const;
int getRopeTime(int leg) const;
wstring getStartDataS(int leg) const;
wstring getRestartTimeS(int leg) const;
wstring getRopeTimeS(int leg) const;
// Get the index of the base leg for this leg (=first race of leg's runner)
int getLegRunner(int leg) const;
// Get the index of this leg for its runner.
int getLegRunnerIndex(int leg) const;
// Set the runner index for the specified leg. (Used when several legs are run be the same person)
void setLegRunner(int leg, int runnerNo);
// Get number of races run by the runner of given leg
int getNumMultiRunners(int leg) const;
// Get number of legs, not counting parallel legs
int getNumLegNoParallel() const;
// Split a linear leg index into non-parallel leg number and order
// number on the leg (zero-indexed). Returns true if the legNumber is parallel
bool splitLegNumberParallel(int leg, int &legNumber, int &legOrder) const;
// The inverse of splitLegNumberParallel. Return -1 on invalid input.
int getLegNumberLinear(int legNumber, int legOrder) const;
//Get the number of parallel runners on a given leg (before and after)
int getNumParallel(int leg) const;
// Get the linear leg number of the next (non-parallel with this) leg
int getNextBaseLeg(int leg) const;
// Get the linear leg number of the preceeding leg
int getPreceedingLeg(int leg) const;
/// Get a string 1, 2a, etc describing the number of the leg
wstring getLegNumber(int leg) const;
// Return the number of distinct runners for one
// "team" in this class.
int getNumDistinctRunners() const;
// Return the minimal number of runners in team
int getNumDistinctRunnersMinimal() const;
void setStartType(int leg, StartTypes st, bool noThrow);
void setLegType(int leg, LegTypes lt);
bool setStartData(int leg, const wstring &s);
bool setStartData(int leg, int value);
void setRestartTime(int leg, const wstring &t);
void setRopeTime(int leg, const wstring &t);
void setNoTiming(bool noResult);
bool getNoTiming() const;
void setIgnoreStartPunch(bool ignoreStartPunch);
bool ignoreStartPunch() const;
void setFreeStart(bool freeStart);
bool hasFreeStart() const;
void setDirectResult(bool directResult);
bool hasDirectResult() const;
bool isCourseUsed(int Id) const;
wstring getLength(int leg) const;
// True if the multicourse structure is in use
bool hasMultiCourse() const {return MultiCourse.size()>0;}
// True if there is a true multicourse usage.
bool hasTrueMultiCourse() const;
unsigned getNumStages() const {return MultiCourse.size();}
/** Get the set of true legs, identifying parallell legs etc. Returns indecs into
legInfo of the last leg of the true leg (first), and true leg (second).*/
struct TrueLegInfo {
protected:
TrueLegInfo(int first_, int second_) : first(first_), second(second_) {}
friend class oClass;
public:
int first;
int second;
int nonOptional; // Index of a leg with a non-optional runner of that leg (which e.g. defines the course)
};
void getTrueStages(vector<TrueLegInfo> &stages) const;
unsigned getLastStageIndex() const {return max<signed>(MultiCourse.size(), 1)-1;}
void setNumStages(int no);
bool operator<(const oClass &b) {return tSortIndex<b.tSortIndex || (tSortIndex == b.tSortIndex && Id < b.Id);}
// Get total number of runners running this class.
// Use checkFirstLeg to only check the number of runners running leg 1.
int getNumRunners(bool checkFirstLeg, bool noCountVacant, bool noCountNotCompeting) const;
void getNumResults(int leg, int &total, int &finished, int &dns) const;
//Get remaining maps for class (or int::minvalue)
int getNumRemainingMaps(bool forceRecalculate) const;
void setNumberMaps(int nm);
int getNumberMaps(bool rawAttribute = false) const;
const wstring &getName() const {return Name;}
void setName(const wstring &name, bool manualSet);
void Set(const xmlobject *xo);
bool Write(xmlparser &xml);
bool fillStageCourses(gdioutput &gdi, int stage,
const string &name) const;
static void fillStartTypes(gdioutput &gdi, const string &name, bool firstLeg);
static void fillLegTypes(gdioutput &gdi, const string &name);
pCourse getCourse(bool getSampleFromRunner = false) const;
void getCourses(int leg, vector<pCourse> &courses) const;
pCourse getCourse(int leg, unsigned fork=0, bool getSampleFromRunner = false) const;
int getCourseId() const {if (Course) return Course->getId(); else return 0;}
void setCourse(pCourse c);
bool addStageCourse(int stage, int courseId, int index);
bool addStageCourse(int stage, pCourse pc, int index);
void clearStageCourses(int stage);
bool moveStageCourse(int stage, int index, int offset);
bool removeStageCourse(int stage, int courseId, int position);
void getAgeLimit(int &low, int &high) const;
void setAgeLimit(int low, int high);
int getExpectedAge() const;
PersonSex getSex() const;
void setSex(PersonSex sex);
wstring getStart() const;
void setStart(const wstring &start);
int getBlock() const;
void setBlock(int block);
bool getAllowQuickEntry() const;
void setAllowQuickEntry(bool quick);
AutoBibType getAutoBibType() const;
BibMode getBibMode() const;
void setBibMode(BibMode bibMode);
wstring getType() const;
void setType(const wstring &type);
// Get class default fee from competition, depending on type(?)
// a non-zero fee is changed only if resetFee is true
void addClassDefaultFee(bool resetFee);
// Get entry fee depending on date and age
int getEntryFee(const wstring &date, int age) const;
/// Get all class fees
vector<pair<wstring, size_t>> oClass::getAllFees() const;
// Clear cached data
void clearCache(bool recalculate);
// Check if forking is fair
bool checkForking(vector< vector<int> > &legOrder,
vector< vector<int> > &forks,
set< pair<int, int> > &unfairLegs) const;
// Automatically setup forkings using the specified courses.
// Returns <number of forkings created, number of courses used>
pair<int, int> autoForking(const vector< vector<int> > &inputCourses);
bool hasUnorderedLegs() const;
void setUnorderedLegs(bool order);
void getParallelCourseGroup(int leg, int startNo, vector< pair<int, pCourse> > &group) const;
// Returns 0 for no parallel selection (= normal mode)
pCourse selectParallelCourse(const oRunner &r, const SICard &sic);
void getParallelRange(int leg, int &parLegRangeMin, int &parLegRangeMax) const;
bool hasAnyCourse(const set<int> &crsId) const;
GeneralResult *getResultModule() const;
void setResultModule(const string &tag);
const string &getResultModuleTag() const;
void merge(const oBase &input, const oBase *base) final;
oClass(oEvent *poe);
oClass(oEvent *poe, int id);
virtual ~oClass();
void clearDuplicate();
friend class oAbstractRunner;
friend class oEvent;
friend class oRunner;
friend class oTeam;
friend class MeosSQL;
friend class TabSpeaker;
};
static const oClass::DrawSpecified DrawKeys[4] = { oClass::DrawSpecified::FixedTime,
oClass::DrawSpecified::Vacant,
oClass::DrawSpecified::Extra };