1619 lines
53 KiB
C++
1619 lines
53 KiB
C++
/************************************************************************
|
|
MeOS - Orienteering Software
|
|
Copyright (C) 2009-2018 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 "stdafx.h"
|
|
|
|
#include <algorithm>
|
|
#include "generalresult.h"
|
|
#include "oEvent.h"
|
|
#include "meos_util.h"
|
|
#include "oListInfo.h"
|
|
#include "meosexception.h"
|
|
#include "localizer.h"
|
|
|
|
extern gdioutput *gdi_main;
|
|
|
|
|
|
GeneralResultCtr::GeneralResultCtr(const char *tagIn, const wstring &nameIn, GeneralResult *ptrIn) {
|
|
name = nameIn;
|
|
tag = tagIn;
|
|
ptr = ptrIn;
|
|
}
|
|
|
|
GeneralResultCtr::GeneralResultCtr(wstring &file, DynamicResult *ptrIn) {
|
|
ptr = ptrIn;
|
|
name = ptrIn->getName(false);
|
|
tag = ptrIn->getTag();
|
|
fileSource = file;
|
|
}
|
|
|
|
GeneralResultCtr::~GeneralResultCtr() {
|
|
delete ptr;
|
|
ptr = 0;
|
|
}
|
|
|
|
GeneralResultCtr::GeneralResultCtr(const GeneralResultCtr &ctr) {
|
|
ptr = ctr.ptr;
|
|
name = ctr.name;
|
|
tag = ctr.tag;
|
|
fileSource = ctr.fileSource;
|
|
ctr.ptr = 0;
|
|
}
|
|
|
|
void GeneralResultCtr::operator=(const GeneralResultCtr &ctr) {
|
|
if (this == &ctr)
|
|
return;
|
|
delete ptr;
|
|
name = ctr.name;
|
|
ptr = ctr.ptr;
|
|
ctr.ptr = 0;
|
|
}
|
|
|
|
bool GeneralResultCtr::isDynamic() const {
|
|
return !fileSource.empty();
|
|
}
|
|
|
|
GeneralResult::GeneralResult(void) {
|
|
context = 0;
|
|
}
|
|
|
|
GeneralResult::~GeneralResult(void) {
|
|
}
|
|
|
|
// Needed to generate template instances
|
|
void GRINSTANCE() {
|
|
vector<oRunner *> a;
|
|
vector<oTeam *> b;
|
|
GeneralResult gr;
|
|
gr.sort(a, SortByFinishTime);
|
|
gr.sort(b, SortByFinishTime);
|
|
}
|
|
|
|
|
|
void GeneralResult::setContext(const oListParam *contextIn) {
|
|
context = contextIn;
|
|
}
|
|
|
|
void GeneralResult::clearContext() {
|
|
context = 0;
|
|
}
|
|
|
|
int GeneralResult::getListParamTimeToControl() const {
|
|
if (context)
|
|
return context->useControlIdResultTo;
|
|
else
|
|
return 0; // No context in method editor
|
|
}
|
|
|
|
int GeneralResult::getListParamTimeFromControl() const {
|
|
if (context)
|
|
return context->useControlIdResultFrom;
|
|
else
|
|
return 0; // No context in method editor
|
|
}
|
|
|
|
struct GRSortInfo {
|
|
int principalSort;
|
|
int score;
|
|
oAbstractRunner *tr;
|
|
|
|
bool operator<(const GRSortInfo &other) const {
|
|
if (principalSort != other.principalSort)
|
|
return principalSort < other.principalSort;
|
|
else if (score != other.score)
|
|
return score < other.score;
|
|
|
|
const wstring &as = tr->getBib();
|
|
const wstring &bs = other.tr->getBib();
|
|
if (as != bs)
|
|
return compareBib(as, bs);
|
|
else
|
|
return tr->getName() < other.tr->getName();
|
|
}
|
|
};
|
|
|
|
void GeneralResult::calculateTeamResults(vector<oTeam *> &teams, oListInfo::ResultType resType, bool sortTeams, int inputNumber) const {
|
|
if (teams.empty())
|
|
return;
|
|
prepareCalculations(*teams[0]->oe, true, inputNumber);
|
|
|
|
//bool classSort = resType == oListInfo::Global ? false : true;
|
|
vector<GRSortInfo> teamScore(teams.size());
|
|
for (size_t k = 0; k < teams.size(); k++) {
|
|
if (resType == oListInfo::Classwise) {
|
|
teamScore[k].principalSort = teams[k]->Class ? teams[k]->Class->getSortIndex() * 50000
|
|
+ teams[k]->Class->getId() : 0;
|
|
}
|
|
else
|
|
teamScore[k].principalSort = 0;
|
|
|
|
prepareCalculations(*teams[k]);
|
|
teams[k]->tmpResult.runningTime = teams[k]->getStartTime(); //XXX
|
|
teams[k]->tmpResult.runningTime = deduceTime(*teams[k]);
|
|
teams[k]->tmpResult.status = deduceStatus(*teams[k]);
|
|
teams[k]->tmpResult.points = deducePoints(*teams[k]);
|
|
|
|
teamScore[k].score = score(*teams[k], teams[k]->tmpResult.status,
|
|
teams[k]->tmpResult.runningTime,
|
|
teams[k]->tmpResult.points);
|
|
|
|
storeOutput(teams[k]->tmpResult.outputTimes,
|
|
teams[k]->tmpResult.outputNumbers);
|
|
|
|
teamScore[k].tr = teams[k];
|
|
|
|
}
|
|
|
|
::sort(teamScore.begin(), teamScore.end());
|
|
int place = 1;
|
|
int iPlace = 1;
|
|
int leadtime = 0;
|
|
for (size_t k = 0; k < teamScore.size(); k++) {
|
|
if (k>0 && teamScore[k-1].principalSort != teamScore[k].principalSort) {
|
|
place = 1;
|
|
iPlace = 1;
|
|
}
|
|
else if (k>0 && teamScore[k-1].score != teamScore[k].score) {
|
|
place = iPlace;
|
|
}
|
|
|
|
if (teamScore[k].tr->tmpResult.status == StatusOK) {
|
|
teamScore[k].tr->tmpResult.place = place;
|
|
iPlace++;
|
|
if (place == 1) {
|
|
leadtime = teamScore[k].tr->tmpResult.runningTime;
|
|
teamScore[k].tr->tmpResult.timeAfter = 0;
|
|
}
|
|
else {
|
|
teamScore[k].tr->tmpResult.timeAfter = teamScore[k].tr->tmpResult.runningTime - leadtime;
|
|
}
|
|
}
|
|
else {
|
|
teamScore[k].tr->tmpResult.place = 0;
|
|
teamScore[k].tr->tmpResult.timeAfter = 0;
|
|
}
|
|
}
|
|
|
|
if (sortTeams) {
|
|
for (size_t k = 0; k < teamScore.size(); k++) {
|
|
teams[k] = (oTeam *)teamScore[k].tr;
|
|
}
|
|
}
|
|
}
|
|
|
|
void GeneralResult::sortTeamMembers(vector<oRunner *> &runners) const {
|
|
vector<GRSortInfo> runnerScore(runners.size());
|
|
for (size_t k = 0; k < runners.size(); k++) {
|
|
runnerScore[k].principalSort = 0;
|
|
runnerScore[k].score = runners[k]->tmpResult.internalScore;
|
|
//runnerScore[k].score = score(*runners[k], runners[k]->tmpResult.status,
|
|
// runners[k]->tmpResult.runningTime,
|
|
// runners[k]->tmpResult.points);
|
|
|
|
runnerScore[k].tr = runners[k];
|
|
}
|
|
|
|
::sort(runnerScore.begin(), runnerScore.end());
|
|
|
|
for (size_t k = 0; k < runners.size(); k++) {
|
|
runners[k] = pRunner(runnerScore[k].tr);
|
|
}
|
|
}
|
|
|
|
template<class T> void GeneralResult::sort(vector<T *> &rt, SortOrder so) const {
|
|
PrincipalSort ps = ClassWise;
|
|
|
|
if (so == CourseResult || so == CourseStartTime)
|
|
ps = CourseWise;
|
|
|
|
else if (so == SortByName || so == SortByFinishTimeReverse ||
|
|
so == SortByFinishTime || so == SortByStartTime)
|
|
ps = None;
|
|
|
|
vector< pair<int, oAbstractRunner *> > arr(rt.size());
|
|
const int maxT = 3600 * 100;
|
|
for(size_t k = 0; k < rt.size(); k++) {
|
|
arr[k].first = 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<oRunner *>(rt[k]);
|
|
arr[k].first = r && r->getCourse(false) ? r->getCourse(false)->getId() : 0;
|
|
}
|
|
arr[k].second = rt[k];
|
|
int ord = 0;
|
|
const oAbstractRunner::TempResult &tr = rt[k]->getTempResult(0);
|
|
if (so == SortByFinishTime || so == ClassFinishTime) {
|
|
ord = tr.getFinishTime();
|
|
if (ord == 0 || tr.getStatus()>1)
|
|
ord = maxT;
|
|
}
|
|
else if (so == SortByFinishTimeReverse) {
|
|
ord = tr.getFinishTime();
|
|
if (ord == 0 || tr.getStatus()>1)
|
|
ord = maxT;
|
|
else
|
|
ord = maxT - ord;
|
|
}
|
|
else if (so == SortByStartTime || so == ClassStartTime ||
|
|
so == ClassStartTimeClub) {
|
|
ord = tr.getStartTime();
|
|
}
|
|
|
|
arr[k].first = arr[k].first * maxT * 10 + ord;
|
|
}
|
|
|
|
stable_sort(arr.begin(), arr.end());
|
|
|
|
for(size_t k = 0; k < rt.size(); k++) {
|
|
rt[k] = (T*)arr[k].second;
|
|
}
|
|
}
|
|
|
|
void GeneralResult::calculateIndividualResults(vector<oRunner *> &runners,
|
|
oListInfo::ResultType resType,
|
|
bool sortRunners,
|
|
int inputNumber) const {
|
|
|
|
if (runners.empty())
|
|
return;
|
|
prepareCalculations(*runners[0]->oe, false, inputNumber);
|
|
//bool classSort = resType == oListInfo::Global ? false : true;
|
|
vector<GRSortInfo> runnerScore(runners.size());
|
|
for (size_t k = 0; k < runners.size(); k++) {
|
|
const oRunner *r = runners[k];
|
|
if (resType == oListInfo::Classwise) {
|
|
runnerScore[k].principalSort = r->Class ? r->Class->getSortIndex() * 50000
|
|
+ r->Class->getId() : 0;
|
|
}
|
|
else if (resType == oListInfo::Legwise) {
|
|
runnerScore[k].principalSort = r->Class ? r->Class->getSortIndex() * 50000
|
|
+ r->Class->getId() : 0;
|
|
|
|
int ln = r->getLegNumber();
|
|
const oTeam *pt = r->getTeam();
|
|
if (pt) {
|
|
const oClass *tcls = pt->getClassRef(false);
|
|
if (tcls && tcls->getClassType() == oClassRelay) {
|
|
int dummy;
|
|
tcls->splitLegNumberParallel(r->getLegNumber(), ln, dummy);
|
|
}
|
|
}
|
|
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;
|
|
|
|
int from = getListParamTimeFromControl();
|
|
if (from <= 0) {
|
|
runners[k]->tmpResult.startTime = r->getStartTime();
|
|
}
|
|
else {
|
|
int rt;
|
|
RunnerStatus stat;
|
|
runners[k]->getSplitTime(from, stat, rt);
|
|
if (stat == StatusOK)
|
|
runners[k]->tmpResult.startTime = runners[k]->getStartTime() + rt;
|
|
else
|
|
runners[k]->tmpResult.startTime = runners[k]->getStartTime();
|
|
}
|
|
prepareCalculations(*runners[k]);
|
|
runners[k]->tmpResult.runningTime = deduceTime(*runners[k], runners[k]->tmpResult.startTime);
|
|
runners[k]->tmpResult.status = deduceStatus(*runners[k]);
|
|
runners[k]->tmpResult.points = deducePoints(*runners[k]);
|
|
|
|
runnerScore[k].score = score(*runners[k], runners[k]->tmpResult.status,
|
|
runners[k]->tmpResult.runningTime,
|
|
runners[k]->tmpResult.points, false);
|
|
|
|
storeOutput(runners[k]->tmpResult.outputTimes,
|
|
runners[k]->tmpResult.outputNumbers);
|
|
|
|
runnerScore[k].tr = runners[k];
|
|
|
|
}
|
|
|
|
::sort(runnerScore.begin(), runnerScore.end());
|
|
int place = 1;
|
|
int iPlace = 1;
|
|
int leadtime = 0;
|
|
for (size_t k = 0; k < runnerScore.size(); k++) {
|
|
if (k>0 && runnerScore[k-1].principalSort != runnerScore[k].principalSort) {
|
|
place = 1;
|
|
iPlace = 1;
|
|
}
|
|
else if (k>0 && runnerScore[k-1].score != runnerScore[k].score) {
|
|
place = iPlace;
|
|
}
|
|
|
|
if (runnerScore[k].tr->tmpResult.status == StatusOK) {
|
|
runnerScore[k].tr->tmpResult.place = place;
|
|
iPlace++;
|
|
if (place == 1) {
|
|
leadtime = runnerScore[k].tr->tmpResult.runningTime;
|
|
runnerScore[k].tr->tmpResult.timeAfter = 0;
|
|
}
|
|
else {
|
|
runnerScore[k].tr->tmpResult.timeAfter = runnerScore[k].tr->tmpResult.runningTime - leadtime;
|
|
}
|
|
}
|
|
else {
|
|
runnerScore[k].tr->tmpResult.place = 0;
|
|
runnerScore[k].tr->tmpResult.timeAfter = 0;
|
|
}
|
|
}
|
|
|
|
if (sortRunners) {
|
|
for (size_t k = 0; k < runnerScore.size(); k++) {
|
|
runners[k] = (oRunner *)runnerScore[k].tr;
|
|
}
|
|
}
|
|
}
|
|
|
|
void GeneralResult::prepareCalculations(oEvent &oe, bool prepareForTeam, int inputNumber) const {
|
|
}
|
|
|
|
void GeneralResult::prepareCalculations(oTeam &team) const {
|
|
int nr = team.getNumRunners();
|
|
for (int j = 0; j < nr; j++) {
|
|
pRunner r = team.getRunner(j);
|
|
if (r) {
|
|
prepareCalculations(*r);
|
|
r->tmpResult.runningTime = deduceTime(*r, r->getStartTime()); //XXX
|
|
r->tmpResult.status = deduceStatus(*r);
|
|
r->tmpResult.place = 0;//XXX?
|
|
r->tmpResult.timeAfter = 0;//XXX?
|
|
r->tmpResult.points = deducePoints(*r);
|
|
r->tmpResult.internalScore = score(*r, r->tmpResult.status,
|
|
r->tmpResult.runningTime,
|
|
r->tmpResult.points, true);
|
|
|
|
storeOutput(r->tmpResult.outputTimes,
|
|
r->tmpResult.outputNumbers);
|
|
}
|
|
}
|
|
}
|
|
|
|
void GeneralResult::prepareCalculations(oRunner &runner) const {
|
|
int from = getListParamTimeFromControl();
|
|
runner.tmpResult.startTime = runner.getStartTime();
|
|
|
|
if (from>0) {
|
|
int rt;
|
|
RunnerStatus stat;
|
|
runner.getSplitTime(from, stat, rt);
|
|
if (stat == StatusOK)
|
|
runner.tmpResult.startTime += rt;
|
|
}
|
|
}
|
|
|
|
void GeneralResult::storeOutput(vector<int> ×, vector<int> &numbers) const {
|
|
}
|
|
|
|
int GeneralResult::score(oTeam &team, RunnerStatus st, int rt, int points) const {
|
|
return (100*RunnerStatusOrderMap[st] + team.getNumShortening()) * 900000 + rt;
|
|
}
|
|
|
|
RunnerStatus GeneralResult::deduceStatus(oTeam &team) const {
|
|
return team.getStatus();
|
|
}
|
|
|
|
int GeneralResult::deduceTime(oTeam &team) const {
|
|
return team.getRunningTime();
|
|
}
|
|
|
|
int GeneralResult::deducePoints(oTeam &team) const {
|
|
return team.getRogainingPoints(false);
|
|
}
|
|
|
|
int GeneralResult::score(oRunner &runner, RunnerStatus st, int time, int points, bool asTeamMember) const {
|
|
if (asTeamMember) {
|
|
return runner.getLegNumber();
|
|
}
|
|
else
|
|
return (RunnerStatusOrderMap[st]*100 + runner.getNumShortening()) * 900000 + time;
|
|
}
|
|
|
|
RunnerStatus GeneralResult::deduceStatus(oRunner &runner) const {
|
|
return runner.getStatus();
|
|
}
|
|
|
|
int GeneralResult::deduceTime(oRunner &runner, int startTime) const {
|
|
return runner.getRunningTime();
|
|
}
|
|
|
|
int GeneralResult::deducePoints(oRunner &runner) const {
|
|
return runner.getRogainingPoints(false);
|
|
}
|
|
|
|
int ResultAtControl::score(oTeam &team, RunnerStatus st, int time, int points) const {
|
|
return GeneralResult::score(team, st, time, points);
|
|
}
|
|
|
|
RunnerStatus ResultAtControl::deduceStatus(oTeam &team) const {
|
|
return GeneralResult::deduceStatus(team);
|
|
}
|
|
|
|
int ResultAtControl::deduceTime(oTeam &team) const {
|
|
return GeneralResult::deduceTime(team);
|
|
}
|
|
|
|
int ResultAtControl::deducePoints(oTeam &team) const {
|
|
return GeneralResult::deducePoints(team);
|
|
}
|
|
|
|
int ResultAtControl::score(oRunner &runner, RunnerStatus st, int time, int points, bool asTeamMember) const {
|
|
if (asTeamMember)
|
|
return runner.getLegNumber();
|
|
const int TK = 3600 * 100;
|
|
if (st == StatusOK)
|
|
return time;
|
|
else
|
|
return TK + st;
|
|
}
|
|
|
|
RunnerStatus TotalResultAtControl::deduceStatus(oRunner &runner) const {
|
|
RunnerStatus singleStat = ResultAtControl::deduceStatus(runner);
|
|
if (singleStat != StatusOK)
|
|
return singleStat;
|
|
|
|
RunnerStatus inputStatus = StatusOK;
|
|
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(false)) {
|
|
// Find base leg
|
|
int legIx = runner.getLegNumber();
|
|
const pClass cls = t->getClassRef(false);
|
|
while (legIx > 0 && (cls->isParallel(legIx) || cls->isOptional(legIx)))
|
|
legIx--;
|
|
if (legIx > 0)
|
|
inputStatus = t->getLegStatus(legIx-1, true);
|
|
}
|
|
else {
|
|
inputStatus = t->getInputStatus();
|
|
}
|
|
}
|
|
else {
|
|
inputStatus = runner.getInputStatus();
|
|
}
|
|
|
|
return inputStatus; // Single status is OK.
|
|
}
|
|
|
|
int TotalResultAtControl::deduceTime(oRunner &runner, int startTime) const {
|
|
int singleTime = ResultAtControl::deduceTime(runner, startTime);
|
|
|
|
if (singleTime == 0)
|
|
return 0;
|
|
|
|
int inputTime = 0;
|
|
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(false)) {
|
|
// Find base leg
|
|
int legIx = runner.getLegNumber();
|
|
const pClass cls = t->getClassRef(false);
|
|
while (legIx > 0 && (cls->isParallel(legIx) || cls->isOptional(legIx)))
|
|
legIx--;
|
|
if (legIx > 0)
|
|
inputTime = t->getLegRunningTime(legIx-1, true);
|
|
}
|
|
else {
|
|
inputTime = t->getInputTime();
|
|
}
|
|
}
|
|
else {
|
|
inputTime = runner.getInputTime();
|
|
}
|
|
|
|
return singleTime + inputTime;
|
|
}
|
|
|
|
int TotalResultAtControl::score(oRunner &runner, RunnerStatus st, int time, int points, bool asTeamMember) const {
|
|
if (asTeamMember)
|
|
return runner.getLegNumber();
|
|
const int TK = 3600 * 100;
|
|
RunnerStatus inputStatus = StatusOK;
|
|
|
|
if (runner.getTeam()) {
|
|
const pTeam t = runner.getTeam();
|
|
if (runner.getLegNumber()>0) {
|
|
inputStatus = t->getLegStatus(runner.getLegNumber()-1, true);
|
|
}
|
|
else {
|
|
inputStatus = t->getInputStatus();
|
|
}
|
|
}
|
|
else {
|
|
inputStatus = runner.getInputStatus();
|
|
}
|
|
|
|
if (st != StatusUnknown)
|
|
st = max(inputStatus, st);
|
|
|
|
if (st == StatusOK) {
|
|
return time;
|
|
}
|
|
else
|
|
return TK + st;
|
|
}
|
|
|
|
RunnerStatus ResultAtControl::deduceStatus(oRunner &runner) const {
|
|
int fc = getListParamTimeToControl();
|
|
if (fc > 0) {
|
|
RunnerStatus stat;
|
|
int rt;
|
|
runner.getSplitTime(fc, stat, rt);
|
|
return stat;
|
|
}
|
|
RunnerStatus st = runner.getStatus();
|
|
if (st == StatusUnknown && runner.getRunningTime() > 0)
|
|
return StatusOK;
|
|
return st;
|
|
}
|
|
|
|
int ResultAtControl::deduceTime(oRunner &runner, int startTime) const {
|
|
int fc = getListParamTimeToControl();
|
|
|
|
if (fc > 0) {
|
|
RunnerStatus stat;
|
|
int rt;
|
|
runner.getSplitTime(fc, stat, rt);
|
|
|
|
if (stat == StatusOK)
|
|
return runner.getStartTime() + rt - startTime;
|
|
}
|
|
else if (runner.getFinishTime() > 0) {
|
|
return runner.getFinishTime() - startTime;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ResultAtControl::deducePoints(oRunner &runner) const {
|
|
return 0;
|
|
}
|
|
|
|
int DynamicResult::instanceCount = 0;
|
|
map<string, DynamicResult::DynamicMethods> DynamicResult::symb2Method;
|
|
map<DynamicResult::DynamicMethods, pair<string, string> > DynamicResult::method2SymbName;
|
|
|
|
DynamicResult::DynamicResult() {
|
|
builtIn = false;
|
|
readOnly = false;
|
|
isCompiled = false;
|
|
methods.resize(_Mlast);
|
|
instanceCount++;
|
|
|
|
if (method2SymbName.empty()) {
|
|
addSymbol(MDeduceRStatus, "RunnerStatus", "Status calculation for runner");
|
|
addSymbol(MDeduceRTime, "RunnerTime", "Time calculation for runner");
|
|
addSymbol(MDeduceRPoints, "RunnerPoints", "Point calculation for runner");
|
|
addSymbol(MRScore, "RunnerScore", "Result score calculation for runner");
|
|
|
|
addSymbol(MDeduceTStatus, "TeamStatus", "Status calculation for team");
|
|
addSymbol(MDeduceTTime, "TeamTime", "Time calculation for team");
|
|
addSymbol(MDeduceTPoints, "TeamPoints", "Point calculation for team");
|
|
addSymbol(MTScore, "TeamScore", "Result score calculation for team");
|
|
}
|
|
}
|
|
|
|
DynamicResult::DynamicResult(const DynamicResult &resIn) {
|
|
instanceCount++;
|
|
isCompiled = false;
|
|
name = resIn.name;
|
|
tag = resIn.tag;
|
|
readOnly = false;
|
|
|
|
description = resIn.description;
|
|
origin = resIn.origin;
|
|
timeStamp = resIn.timeStamp;
|
|
annotation = resIn.annotation;
|
|
builtIn = resIn.builtIn;
|
|
methods.resize(_Mlast);
|
|
for (size_t k = 0; k < methods.size(); k++) {
|
|
methods[k].source = resIn.methods[k].source;
|
|
methods[k].description = resIn.methods[k].description;
|
|
}
|
|
}
|
|
|
|
void DynamicResult::operator=(const DynamicResult &resIn) {
|
|
isCompiled = false;
|
|
name = resIn.name;
|
|
tag = resIn.tag;
|
|
description = resIn.description;
|
|
origin = resIn.origin;
|
|
timeStamp = resIn.timeStamp;
|
|
annotation = resIn.annotation;
|
|
readOnly = resIn.readOnly;
|
|
builtIn = resIn.builtIn;
|
|
methods.resize(_Mlast);
|
|
for (size_t k = 0; k < methods.size(); k++) {
|
|
methods[k].source = resIn.methods[k].source;
|
|
methods[k].description = resIn.methods[k].description;
|
|
methods[k].pn = 0;
|
|
}
|
|
|
|
}
|
|
|
|
void DynamicResult::addSymbol(DynamicMethods method, const char *symb, const char *name) {
|
|
if (method2SymbName.count(method) || symb2Method.count(symb))
|
|
throw meosException("Method symbol used");
|
|
method2SymbName[method] = make_pair(symb, name);
|
|
symb2Method[symb] = method;
|
|
}
|
|
|
|
DynamicResult::~DynamicResult() {
|
|
instanceCount--;
|
|
if (instanceCount == 0) {
|
|
method2SymbName.clear();
|
|
symb2Method.clear();
|
|
}
|
|
}
|
|
|
|
DynamicResult::MethodInfo::MethodInfo() {
|
|
pn = 0;
|
|
}
|
|
|
|
DynamicResult::MethodInfo::~MethodInfo() {
|
|
}
|
|
|
|
const ParseNode *DynamicResult::getMethod(DynamicMethods method) const {
|
|
return methods[method].pn;
|
|
}
|
|
|
|
const string &DynamicResult::getMethodSource(DynamicMethods method) const {
|
|
return methods[method].source;
|
|
}
|
|
|
|
void DynamicResult::setMethodSource(DynamicMethods method, const string &source) {
|
|
methods[method].source = source;
|
|
methods[method].pn = 0;
|
|
methods[method].pn = parser.parse(source);
|
|
}
|
|
|
|
RunnerStatus DynamicResult::toStatus(int status) const {
|
|
switch (status) {
|
|
case StatusUnknown:
|
|
return StatusUnknown;
|
|
case StatusOK:
|
|
return StatusOK;
|
|
case StatusMP:
|
|
return StatusMP;
|
|
case StatusDNF:
|
|
return StatusDNF;
|
|
case StatusDNS:
|
|
return StatusDNS;
|
|
case StatusCANCEL:
|
|
return StatusCANCEL;
|
|
case StatusNotCompetiting:
|
|
return StatusNotCompetiting;
|
|
case StatusDQ:
|
|
return StatusDQ;
|
|
case StatusMAX:
|
|
return StatusMAX;
|
|
default:
|
|
throw meosException("Unknown status code X#" + itos(status));
|
|
}
|
|
}
|
|
|
|
int DynamicResult::score(oTeam &team, RunnerStatus st, int time, int points) const {
|
|
if (getMethod(MTScore)) {
|
|
parser.addSymbol("ComputedTime", time);
|
|
parser.addSymbol("ComputedStatus", st);
|
|
parser.addSymbol("ComputedPoints", points);
|
|
return getMethod(MTScore)->evaluate(parser);
|
|
}
|
|
else if (getMethodSource(MTScore).empty())
|
|
return GeneralResult::score(team, st, time, points);
|
|
else throw meosException("Syntax error");
|
|
}
|
|
|
|
RunnerStatus DynamicResult::deduceStatus(oTeam &team) const {
|
|
if (getMethod(MDeduceTStatus))
|
|
return toStatus(getMethod(MDeduceTStatus)->evaluate(parser));
|
|
else if (getMethodSource(MDeduceTStatus).empty())
|
|
return GeneralResult::deduceStatus(team);
|
|
else throw meosException("Syntax error");
|
|
}
|
|
|
|
int DynamicResult::deduceTime(oTeam &team) const {
|
|
if (getMethod(MDeduceTTime))
|
|
return getMethod(MDeduceTTime)->evaluate(parser);
|
|
else if (getMethodSource(MDeduceTTime).empty())
|
|
return GeneralResult::deduceTime(team);
|
|
else throw meosException("Syntax error");
|
|
}
|
|
|
|
int DynamicResult::deducePoints(oTeam &team) const {
|
|
if (getMethod(MDeduceTPoints))
|
|
return getMethod(MDeduceTPoints)->evaluate(parser);
|
|
else if (getMethodSource(MDeduceTPoints).empty())
|
|
return GeneralResult::deducePoints(team);
|
|
else throw meosException("Syntax error");
|
|
}
|
|
|
|
int DynamicResult::score(oRunner &runner, RunnerStatus st, int time, int points, bool asTeamMember) const {
|
|
if (getMethod(MRScore)) {
|
|
parser.addSymbol("ComputedTime", time);
|
|
parser.addSymbol("ComputedStatus", st);
|
|
parser.addSymbol("ComputedPoints", points);
|
|
return getMethod(MRScore)->evaluate(parser);
|
|
}
|
|
else if (getMethodSource(MRScore).empty())
|
|
return GeneralResult::score(runner, st, time, points, asTeamMember);
|
|
else throw meosException("Syntax error");
|
|
}
|
|
|
|
RunnerStatus DynamicResult::deduceStatus(oRunner &runner) const {
|
|
if (getMethod(MDeduceRStatus))
|
|
return toStatus(getMethod(MDeduceRStatus)->evaluate(parser));
|
|
else if (getMethodSource(MDeduceRStatus).empty())
|
|
return GeneralResult::deduceStatus(runner);
|
|
else throw meosException("Syntax error");
|
|
}
|
|
|
|
int DynamicResult::deduceTime(oRunner &runner, int startTime) const {
|
|
if (getMethod(MDeduceRTime))
|
|
return getMethod(MDeduceRTime)->evaluate(parser);
|
|
else if (getMethodSource(MDeduceRTime).empty())
|
|
return GeneralResult::deduceTime(runner, startTime);
|
|
else throw meosException("Syntax error");
|
|
}
|
|
|
|
int DynamicResult::deducePoints(oRunner &runner) const {
|
|
if (getMethod(MDeduceRPoints))
|
|
return getMethod(MDeduceRPoints)->evaluate(parser);
|
|
else if (getMethodSource(MDeduceRPoints).empty())
|
|
return GeneralResult::deducePoints(runner);
|
|
else throw meosException("Syntax error");
|
|
|
|
}
|
|
|
|
void DynamicResult::save(const wstring &file) const {
|
|
xmlparser xml;
|
|
xml.openOutput(file.c_str(), true);
|
|
save(xml);
|
|
xml.closeOut();
|
|
}
|
|
|
|
extern oEvent *gEvent;
|
|
|
|
void DynamicResult::save(xmlparser &xml) const {
|
|
xml.startTag("MeOSResultCalculationSet");
|
|
xml.write("Name", name);
|
|
xml.write("Tag", gdioutput::widen(tag));
|
|
xml.write("Description", description);
|
|
if (origin.empty())
|
|
origin = gEvent->getName() + L" (" + getLocalDate() + L")";
|
|
xml.write("Origin", origin);
|
|
xml.write("Date", getLocalTime());
|
|
// xml.write("Tag", tag);
|
|
// xml.write("UID", getUniqueId());
|
|
|
|
for (map<string, DynamicMethods>::const_iterator it = symb2Method.begin(); it != symb2Method.end(); ++it) {
|
|
if (!methods[it->second].source.empty()) {
|
|
xml.startTag("Rule", "name", it->first);
|
|
xml.write("Description", methods[it->second].description);
|
|
xml.write("Method", methods[it->second].source);
|
|
xml.endTag();
|
|
}
|
|
}
|
|
xml.endTag();
|
|
}
|
|
|
|
void DynamicResult::clear() {
|
|
parser.clear();
|
|
for (size_t k = 0; k < methods.size(); k++) {
|
|
methods[k].pn = 0;
|
|
methods[k].source.clear();
|
|
methods[k].description.clear();
|
|
}
|
|
}
|
|
|
|
void DynamicResult::load(const wstring &file) {
|
|
xmlparser xml;
|
|
xml.read(file.c_str());
|
|
xmlobject xDef = xml.getObject("MeOSResultCalculationSet");
|
|
load(xDef);
|
|
}
|
|
|
|
void DynamicResult::load(const xmlobject &xDef) {
|
|
if (!xDef)
|
|
throw meosException("Ogiltigt filformat");
|
|
|
|
clear();
|
|
|
|
xDef.getObjectString("Name", name);
|
|
xDef.getObjectString("Description", description);
|
|
xDef.getObjectString("Tag", tag);
|
|
|
|
xDef.getObjectString("Origin", origin);
|
|
xDef.getObjectString("Date", timeStamp);
|
|
//xDef.getObjectString("UID", uniqueIndex);
|
|
xmlList rules;
|
|
xDef.getObjects("Rule", rules);
|
|
for (size_t k = 0; k < rules.size(); k++) {
|
|
string rn;
|
|
rules[k].getObjectString("name", rn);
|
|
map<string, DynamicMethods>::const_iterator res = symb2Method.find(rn);
|
|
if (res == symb2Method.end())
|
|
throw meosException("Unknown result rule X.#" + rn);
|
|
|
|
rules[k].getObjectString("Description", methods[res->second].description);
|
|
rules[k].getObjectString("Method", methods[res->second].source);
|
|
}
|
|
}
|
|
|
|
void DynamicResult::compile(bool forceRecompile) const {
|
|
if (isCompiled)
|
|
return;
|
|
|
|
for (size_t k = 0; k < methods.size(); k++) {
|
|
methods[k].pn = 0;
|
|
}
|
|
parser.clear();
|
|
|
|
pair<wstring, wstring> err;
|
|
for (size_t k = 0; k < methods.size(); k++) {
|
|
if (!methods[k].source.empty()) {
|
|
try {
|
|
methods[k].pn = parser.parse(methods[k].source);
|
|
}
|
|
catch (const meosException &ex) {
|
|
if (err.first.empty()) {
|
|
err.first = gdioutput::widen(method2SymbName[DynamicMethods(k)].second);
|
|
err.second = lang.tl(ex.wwhat());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!err.first.empty()) {
|
|
throw meosException(L"Error in result module X, method Y (Z)#" + name + L"#" + err.first + L"#" + err.second);
|
|
}
|
|
}
|
|
|
|
void DynamicResult::getMethodTypes(vector< pair<DynamicMethods, string> > &mt) const {
|
|
mt.clear();
|
|
for (map<DynamicMethods, pair<string, string> >::const_iterator it = method2SymbName.begin(); it != method2SymbName.end(); ++it)
|
|
mt.push_back(make_pair(it->first, it->second.second));
|
|
|
|
return;
|
|
}
|
|
|
|
void DynamicResult::declareSymbols(DynamicMethods m, bool clear) const {
|
|
if (clear)
|
|
parser.clearSymbols();
|
|
const bool isRunner = m == MRScore ||
|
|
m == MDeduceRPoints ||
|
|
m == MDeduceRStatus ||
|
|
m == MDeduceRTime;
|
|
|
|
parser.declareSymbol("Status", "Runner/team status", false);
|
|
parser.declareSymbol("Start", "Runner/team start time", false);
|
|
parser.declareSymbol("Finish", "Runner/team finish time", false);
|
|
parser.declareSymbol("Time", "Runner/team running time", false);
|
|
parser.declareSymbol("Place", "Runner/team place", false);
|
|
parser.declareSymbol("Points", "Runner/team rogaining points", false);
|
|
parser.declareSymbol("PointReduction", "Automatic rogaining point reduction", false);
|
|
parser.declareSymbol("PointOvertime", "Runner/team rogaining overtime", false);
|
|
parser.declareSymbol("PointGross", "Rogaining points before automatic reduction", false);
|
|
|
|
parser.declareSymbol("PointAdjustment", "Runner/team rogaining points adjustment", false);
|
|
parser.declareSymbol("TimeAdjustment", "Runner/team time adjustment", false);
|
|
|
|
parser.declareSymbol("TotalStatus", "Runner/team total status", false);
|
|
parser.declareSymbol("TotalTime", "Runner/team total running time", false);
|
|
parser.declareSymbol("TotalPlace", "Runner/team total place", false);
|
|
|
|
parser.declareSymbol("InputStatus", "Runner/team input status", false);
|
|
parser.declareSymbol("InputTime", "Runner/team input running time", false);
|
|
parser.declareSymbol("InputPlace", "Runner/team input place", false);
|
|
parser.declareSymbol("InputPoints", "Runner/team input points", false);
|
|
|
|
parser.declareSymbol("Fee", "Runner/team fee", false);
|
|
|
|
parser.declareSymbol("ClubId", "Club id number", false);
|
|
parser.declareSymbol("DistrictId", "District id number", false);
|
|
parser.declareSymbol("Bib", "Nummerlapp", false);
|
|
|
|
parser.declareSymbol("InputNumber", "User input number", false);
|
|
parser.declareSymbol("Shorten", "Number of shortenings", false);
|
|
|
|
if (isRunner) {
|
|
parser.declareSymbol("CardPunches", "Runner's card, punch codes", true);
|
|
parser.declareSymbol("CardTimes", "Runner's card, punch times", true);
|
|
parser.declareSymbol("CardControls", "Runner's card, matched control ids (-1 for unmatched punches)", true);
|
|
|
|
parser.declareSymbol("Course", "Runner's course", true);
|
|
parser.declareSymbol("CourseId", "Runner's course id", false);
|
|
parser.declareSymbol("CourseLength", "Length of course", false);
|
|
|
|
parser.declareSymbol("SplitTimes", "Runner's split times", true);
|
|
parser.declareSymbol("SplitTimesAccumulated", "Runner's total running time to control", true);
|
|
|
|
parser.declareSymbol("LegTimeDeviation", "Deviation +/- from expected time on course leg", true);
|
|
parser.declareSymbol("LegTimeAfter", "Time after leg winner", true);
|
|
parser.declareSymbol("LegPlace", "Place on course leg", true);
|
|
parser.declareSymbol("Leg", "Leg number in team, zero indexed", false);
|
|
parser.declareSymbol("BirthYear", "Year of birth", false);
|
|
}
|
|
else {
|
|
parser.declareSymbol("RunnerStatus", "Status for each team member", true);
|
|
parser.declareSymbol("RunnerTime", "Running time for each team member", true);
|
|
parser.declareSymbol("RunnerStart", "Start time for each team member", true);
|
|
parser.declareSymbol("RunnerFinish", "Finish time for each team member", true);
|
|
parser.declareSymbol("RunnerPoints", "Rogaining points for each team member", true);
|
|
|
|
parser.declareSymbol("RunnerCardPunches", "Punch codes for each team member", true, true);
|
|
parser.declareSymbol("RunnerCardTimes", "Punch times for each team member", true, true);
|
|
parser.declareSymbol("RunnerCardControls", "Matched control ids (-1 for unmatched) for each team member", true, true);
|
|
|
|
parser.declareSymbol("RunnerCourse", "Runner's course", true, true);
|
|
parser.declareSymbol("RunnerSplitTimes", "Runner's split times", true, true);
|
|
|
|
parser.declareSymbol("RunnerOutputTimes", "Runner's method output times", true, true);
|
|
parser.declareSymbol("RunnerOutputNumbers", "Runner's method output numbers", true, true);
|
|
|
|
parser.declareSymbol("PatrolRogainingScore", "Patrol score, rogaining", false);
|
|
parser.declareSymbol("PatrolRogainingReduction", "Patrol score reduction", false);
|
|
parser.declareSymbol("PatrolRogainingOvertime", "Patrol overtime", false);
|
|
}
|
|
|
|
parser.declareSymbol("MaxTime", "Maximum allowed running time", false);
|
|
|
|
parser.declareSymbol("StatusUnknown", "Status code for an unknown result", false);
|
|
parser.declareSymbol("StatusOK", "Status code for a valid result", false);
|
|
parser.declareSymbol("StatusMP", "Status code for a missing punch", false);
|
|
parser.declareSymbol("StatusDNF", "Status code for not finishing", false);
|
|
parser.declareSymbol("StatusDNS", "Status code for not starting", false);
|
|
parser.declareSymbol("StatusCANCEL", "Status code for cancelled entry", false);
|
|
parser.declareSymbol("StatusMAX", "Status code for a time over the maximum", false);
|
|
parser.declareSymbol("StatusDQ", "Status code for disqualification", false);
|
|
parser.declareSymbol("StatusNotCompetiting", "Status code for not competing", false);
|
|
|
|
parser.declareSymbol("ShortestClassTime", "Shortest time in class", false);
|
|
|
|
if (m == MRScore || m == MTScore) {
|
|
parser.declareSymbol("ComputedTime", "Time as computed by your time method", false);
|
|
parser.declareSymbol("ComputedPoints", "Points as computed by your point method", false);
|
|
parser.declareSymbol("ComputedStatus", "Status as computed by your status method", false);
|
|
}
|
|
}
|
|
|
|
void DynamicResult::getSymbols(vector< pair<wstring, size_t> > &symb) const {
|
|
parser.getSymbols(symb);
|
|
}
|
|
|
|
void DynamicResult::getSymbolInfo(int ix, wstring &name, wstring &desc) const {
|
|
parser.getSymbolInfo(ix, name, desc);
|
|
}
|
|
|
|
void DynamicResult::prepareCalculations(oEvent &oe, bool prepareForTeam, int inputNumber) const {
|
|
compile(false);
|
|
oe.calculateResults(oEvent::RTClassResult);
|
|
oe.calculateResults(oEvent::RTTotalResult);
|
|
|
|
declareSymbols(MRScore, true);
|
|
if (prepareForTeam) {
|
|
declareSymbols(MTScore, false);
|
|
vector<pTeam> t;
|
|
oe.getTeams(0, t, false);
|
|
for (size_t k = 0; k < t.size(); k++)
|
|
t[k]->resetResultCalcCache();
|
|
|
|
oe.calculateTeamResults(false);
|
|
oe.calculateTeamResults(true);
|
|
}
|
|
parser.addSymbol("StatusUnknown", StatusUnknown);
|
|
parser.addSymbol("StatusOK", StatusOK);
|
|
parser.addSymbol("StatusMP", StatusMP);
|
|
parser.addSymbol("StatusDNF", StatusDNF);
|
|
parser.addSymbol("StatusCANCEL", StatusCANCEL);
|
|
parser.addSymbol("StatusDNS", StatusDNS);
|
|
parser.addSymbol("StatusMAX", StatusMAX);
|
|
parser.addSymbol("StatusDQ", StatusDQ);
|
|
parser.addSymbol("StatusNotCompetiting", StatusNotCompetiting);
|
|
|
|
parser.addSymbol("MaxTime", oe.getMaximalTime());
|
|
parser.addSymbol("InputNumber", inputNumber);
|
|
}
|
|
|
|
void DynamicResult::prepareCommon(oAbstractRunner &runner) const {
|
|
parser.clearVariables();
|
|
int st = runner.getStatus();
|
|
int ft = runner.getFinishTime();
|
|
if (st == StatusUnknown && ft>0)
|
|
st = StatusOK;
|
|
parser.addSymbol("Status", st);
|
|
parser.addSymbol("Start", runner.getStartTime());
|
|
parser.addSymbol("Finish", ft);
|
|
parser.addSymbol("Time", runner.getRunningTime());
|
|
parser.addSymbol("Place", runner.getPlace());
|
|
parser.addSymbol("Points", runner.getRogainingPoints(false));
|
|
parser.addSymbol("PointReduction", runner.getRogainingReduction());
|
|
parser.addSymbol("PointOvertime", runner.getRogainingOvertime());
|
|
parser.addSymbol("PointGross", runner.getRogainingPointsGross());
|
|
|
|
parser.addSymbol("PointAdjustment", runner.getPointAdjustment());
|
|
parser.addSymbol("TimeAdjustment", runner.getTimeAdjustment());
|
|
|
|
parser.addSymbol("TotalStatus", runner.getTotalStatus());
|
|
parser.addSymbol("TotalTime", runner.getTotalRunningTime());
|
|
parser.addSymbol("TotalPlace", runner.getTotalPlace());
|
|
|
|
parser.addSymbol("InputStatus", runner.getInputStatus());
|
|
parser.addSymbol("InputTime", runner.getInputTime());
|
|
parser.addSymbol("InputPlace", runner.getInputPlace());
|
|
parser.addSymbol("InputPoints", runner.getInputPoints());
|
|
parser.addSymbol("Shorten", runner.getNumShortening());
|
|
|
|
parser.addSymbol("Fee", runner.getDCI().getInt("Fee"));
|
|
|
|
const pClub pc = runner.getClubRef();
|
|
if (pc) {
|
|
parser.addSymbol("ClubId", pc->getId());
|
|
parser.addSymbol("DistrictId", pc->getDCI().getInt("District"));
|
|
}
|
|
else {
|
|
parser.addSymbol("ClubId", 0);
|
|
parser.addSymbol("DistrictId", 0);
|
|
}
|
|
parser.addSymbol("Bib", _wtoi(runner.getBib().c_str()));
|
|
}
|
|
|
|
void DynamicResult::prepareCalculations(oTeam &team) const {
|
|
GeneralResult::prepareCalculations(team);
|
|
prepareCommon(team);
|
|
int nr = team.getNumRunners();
|
|
vector<int> status(nr), time(nr), start(nr), finish(nr), points(nr);
|
|
vector< vector<int> > runnerOutputTimes(nr);
|
|
vector< vector<int> > runnerOutputNumbers(nr);
|
|
|
|
for (int k = 0; k < nr; k++) {
|
|
pRunner r = team.getRunner(k);
|
|
if (r) {
|
|
oAbstractRunner::TempResult &res = r->getTempResult();
|
|
status[k] = res.getStatus();
|
|
time[k] = res.getRunningTime();
|
|
if (time[k] > 0 && status[k] == StatusUnknown)
|
|
status[k] = StatusOK;
|
|
start[k] = res.getStartTime();
|
|
finish[k] = res.getFinishTime();
|
|
points[k] = res.getPoints();
|
|
runnerOutputTimes[k] = res.outputTimes;
|
|
runnerOutputNumbers[k] = res.outputNumbers;
|
|
}
|
|
}
|
|
parser.removeSymbol("CardControls");
|
|
parser.removeSymbol("CardPunches");
|
|
parser.removeSymbol("CardTimes");
|
|
parser.removeSymbol("Course");
|
|
parser.removeSymbol("CourseLength");
|
|
parser.removeSymbol("CourseId");
|
|
parser.removeSymbol("LegPlace");
|
|
parser.removeSymbol("LegTimeAfter");
|
|
parser.removeSymbol("LegTimeDeviation");
|
|
parser.removeSymbol("Leg");
|
|
parser.removeSymbol("SplitTimes");
|
|
parser.removeSymbol("SplitTimesAccumulated");
|
|
parser.removeSymbol("LegTimeDeviation");
|
|
parser.removeSymbol("BirthYear");
|
|
|
|
parser.addSymbol("RunnerOutputNumbers", runnerOutputNumbers);
|
|
parser.addSymbol("RunnerOutputTimes", runnerOutputTimes);
|
|
|
|
parser.addSymbol("RunnerStatus", status);
|
|
parser.addSymbol("RunnerTime", time);
|
|
parser.addSymbol("RunnerStart", start);
|
|
parser.addSymbol("RunnerFinish", finish);
|
|
parser.addSymbol("RunnerPoints", points);
|
|
|
|
parser.addSymbol("PatrolRogainingScore", team.getRogainingPatrolPoints(false));
|
|
parser.addSymbol("PatrolRogainingReduction", team.getRogainingPatrolReduction());
|
|
parser.addSymbol("PatrolRogainingOvertime", team.getRogainingPatrolOvertime());
|
|
|
|
parser.addSymbol("RunnerCardPunches", team.getResultCache(oTeam::RCCCardPunches));
|
|
parser.addSymbol("RunnerCardTimes", team.getResultCache(oTeam::RCCCardTimes));
|
|
parser.addSymbol("RunnerCardControls", team.getResultCache(oTeam::RCCCardControls));
|
|
|
|
parser.addSymbol("RunnerCourse", team.getResultCache(oTeam::RCCCourse));
|
|
parser.addSymbol("RunnerSplitTimes", team.getResultCache(oTeam::RCCSplitTime));
|
|
|
|
pClass cls = team.getClassRef(true);
|
|
if (cls) {
|
|
int nl = max<int>(1, cls->getNumStages()-1);
|
|
parser.addSymbol("ShortestClassTime", cls->getTotalLegLeaderTime(nl, false));
|
|
}
|
|
}
|
|
|
|
void DynamicResult::prepareCalculations(oRunner &runner) const {
|
|
GeneralResult::prepareCalculations(runner);
|
|
prepareCommon(runner);
|
|
pCard pc = runner.getCard();
|
|
if (pc) {
|
|
vector<pPunch> punches;
|
|
pc->getPunches(punches);
|
|
int np = 0;
|
|
for (size_t k = 0; k < punches.size(); k++) {
|
|
if (punches[k]->getTypeCode() >= 30)
|
|
np++;
|
|
}
|
|
vector<int> times(np);
|
|
vector<int> codes(np);
|
|
vector<int> controls(np);
|
|
int ip = 0;
|
|
for (size_t k = 0; k < punches.size(); k++) {
|
|
if (punches[k]->getTypeCode() >= 30) {
|
|
times[ip] = punches[k]->getAdjustedTime();
|
|
codes[ip] = punches[k]->getTypeCode();
|
|
controls[ip] = punches[k]->isUsedInCourse() ? punches[k]->getControlId() : -1;
|
|
ip++;
|
|
}
|
|
}
|
|
parser.addSymbol("CardPunches", codes);
|
|
parser.addSymbol("CardTimes", times);
|
|
parser.addSymbol("CardControls", controls);
|
|
|
|
pTeam t = runner.getTeam();
|
|
if (t) {
|
|
int leg = runner.getLegNumber();
|
|
t->setResultCache(oTeam::RCCCardTimes, leg, times);
|
|
t->setResultCache(oTeam::RCCCardPunches, leg, codes);
|
|
t->setResultCache(oTeam::RCCCardControls, leg, controls);
|
|
}
|
|
}
|
|
else {
|
|
vector<int> e;
|
|
parser.addSymbol("CardPunches", e);
|
|
parser.addSymbol("CardTimes", e);
|
|
parser.addSymbol("CardControls", e);
|
|
}
|
|
|
|
pCourse crs = runner.getCourse(true);
|
|
const vector<SplitData> &sp = runner.getSplitTimes(false);
|
|
|
|
if (crs) {
|
|
vector<int> eCrs;
|
|
vector<int> eSplitTime;
|
|
vector<int> eAccTime;
|
|
eCrs.reserve(crs->getNumControls());
|
|
eSplitTime.reserve(crs->getNumControls());
|
|
eAccTime.reserve(crs->getNumControls());
|
|
int start = runner.getStartTime();
|
|
int st = runner.getStartTime();
|
|
for (int k = 0; k < crs->getNumControls(); k++) {
|
|
pControl ctrl = crs->getControl(k);
|
|
if (ctrl->isSingleStatusOK()) {
|
|
eCrs.push_back(ctrl->getFirstNumber());
|
|
if (size_t(k) < sp.size()) {
|
|
if (sp[k].status == SplitData::OK) {
|
|
eAccTime.push_back(sp[k].time-start);
|
|
eSplitTime.push_back(sp[k].time-st);
|
|
st = sp[k].time;
|
|
}
|
|
else if (sp[k].status == SplitData::NoTime) {
|
|
eAccTime.push_back(st-start);
|
|
eSplitTime.push_back(0);
|
|
}
|
|
else if (sp[k].status == SplitData::Missing) {
|
|
eAccTime.push_back(0);
|
|
eSplitTime.push_back(-1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (runner.getFinishTime() > 0) {
|
|
eAccTime.push_back(runner.getFinishTime()-start);
|
|
eSplitTime.push_back(runner.getFinishTime()-st);
|
|
}
|
|
else if (!eAccTime.empty()) {
|
|
eAccTime.push_back(0);
|
|
eSplitTime.push_back(-1);
|
|
}
|
|
|
|
parser.addSymbol("CourseLength", crs->getLength());
|
|
parser.addSymbol("CourseId", crs->getId());
|
|
parser.addSymbol("Course", eCrs);
|
|
parser.addSymbol("SplitTimes", eSplitTime);
|
|
parser.addSymbol("SplitTimesAccumulated", eAccTime);
|
|
pTeam t = runner.getTeam();
|
|
if (t) {
|
|
int leg = runner.getLegNumber();
|
|
t->setResultCache(oTeam::RCCCourse, leg, eCrs);
|
|
t->setResultCache(oTeam::RCCSplitTime, leg, eSplitTime);
|
|
}
|
|
}
|
|
else {
|
|
vector<int> e;
|
|
parser.addSymbol("CourseLength", -1);
|
|
parser.addSymbol("CourseId", 0);
|
|
parser.addSymbol("Course", e);
|
|
parser.addSymbol("SplitTimes", e);
|
|
parser.addSymbol("SplitTimesAccumulated", e);
|
|
}
|
|
|
|
pClass cls = runner.getClassRef(true);
|
|
if (cls) {
|
|
int nl = runner.getLegNumber();
|
|
parser.addSymbol("ShortestClassTime", cls->getBestLegTime(nl));
|
|
}
|
|
vector<int> delta;
|
|
vector<int> place;
|
|
vector<int> after;
|
|
runner.getSplitAnalysis(delta);
|
|
runner.getLegTimeAfter(after);
|
|
runner.getLegPlaces(place);
|
|
|
|
parser.addSymbol("LegTimeDeviation", delta);
|
|
parser.addSymbol("LegTimeAfter", after);
|
|
parser.addSymbol("LegPlace", place);
|
|
parser.addSymbol("Leg", runner.getLegNumber());
|
|
parser.addSymbol("BirthYear", runner.getBirthYear());
|
|
}
|
|
|
|
void DynamicResult::storeOutput(vector<int> ×, vector<int> &numbers) const {
|
|
parser.takeVariable("OutputTimes", times);
|
|
parser.takeVariable("OutputNumbers", numbers);
|
|
}
|
|
|
|
int checksum(const string &str);
|
|
int checksum(const wstring &str);
|
|
|
|
long long DynamicResult::getHashCode() const {
|
|
long long hc = 1;
|
|
for (size_t k = 0; k < methods.size(); k++) {
|
|
hc = hc * 997 + checksum(methods[k].source);
|
|
}
|
|
return hc;
|
|
}
|
|
|
|
string DynamicResult::undecorateTag(const string &inputTag) {
|
|
int ix = inputTag.rfind("-v");
|
|
if (ix > 0 && ix != inputTag.npos)
|
|
return inputTag.substr(0, ix);
|
|
else
|
|
return inputTag;
|
|
}
|
|
|
|
wstring DynamicResult::getName(bool withAnnotation) const {
|
|
if (annotation.empty() || !withAnnotation)
|
|
return name;
|
|
else
|
|
return name + L" (" + annotation + L")";
|
|
}
|
|
|
|
void DynamicResult::debugDumpVariables(gdioutput &gdi, bool includeSymbols) const {
|
|
gdi.fillDown();
|
|
gdi.dropLine();
|
|
int c1 = gdi.getCX();
|
|
int c2 = c1 + gdi.scaleLength(170);
|
|
if (includeSymbols) {
|
|
gdi.addString("", 1, "Symboler");
|
|
parser.dumpSymbols(gdi, c1, c2);
|
|
gdi.dropLine();
|
|
}
|
|
else {
|
|
gdi.addString("", 1, "Variabler");
|
|
parser.dumpVariables(gdi, c1, c2);
|
|
gdi.dropLine();
|
|
}
|
|
}
|
|
|
|
void GeneralResult::calculateIndividualResults(vector<pRunner> &runners,
|
|
const pair<int, int> & controlId,
|
|
bool totalResults,
|
|
const string &resTag,
|
|
oListInfo::ResultType resType,
|
|
int inputNumber,
|
|
oEvent &oe,
|
|
vector<GeneralResultInfo> &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<GeneralResult> 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<GeneralResult>();
|
|
else if (!totalResults)
|
|
specialInstance = make_shared<ResultAtControl>();
|
|
else
|
|
specialInstance = make_shared<TotalResultAtControl>();
|
|
|
|
gResult = specialInstance.get();
|
|
}
|
|
gResult->setContext(¶m);
|
|
gResult->calculateIndividualResults(runners, resType, true, inputNumber);
|
|
gResult->clearContext();
|
|
map<int, bool> 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::BaseResultContext>
|
|
GeneralResult::calculateTeamResults(vector<pTeam> &teams,
|
|
int leg,
|
|
const pair<int, int> &controlId,
|
|
bool totalResults,
|
|
const string &resTag,
|
|
oListInfo::ResultType resType,
|
|
int inputNumber,
|
|
oEvent &oe,
|
|
vector<GeneralResultInfo> &results) {
|
|
shared_ptr<BaseResultContext> out = make_shared<BaseResultContext>();
|
|
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<int> 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;
|
|
}
|