823 lines
25 KiB
C++
823 lines
25 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 "oEvent.h"
|
|
#include "xmlparser.h"
|
|
#include <thread>
|
|
|
|
#include "restbed/restbed"
|
|
#include "meosexception.h"
|
|
#include "restserver.h"
|
|
#include "infoserver.h"
|
|
#include <chrono>
|
|
|
|
#include "oListInfo.h"
|
|
#include "TabList.h"
|
|
#include "generalresult.h"
|
|
|
|
using namespace restbed;
|
|
|
|
vector< shared_ptr<RestServer> > RestServer::startedServers;
|
|
|
|
shared_ptr<RestServer> RestServer::construct() {
|
|
shared_ptr<RestServer> obj(new RestServer());
|
|
startedServers.push_back(obj);
|
|
return obj;
|
|
}
|
|
|
|
void RestServer::remove(shared_ptr<RestServer> server) {
|
|
//std::remove(startedServers.begin(), startedServers.end(), server);
|
|
for (size_t k = 0; k < startedServers.size(); k++) {
|
|
if (startedServers[k] == server) {
|
|
startedServers.erase(startedServers.begin() + k);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
RestServer::RestServer() : hasAnyRequest(false) {
|
|
}
|
|
|
|
RestServer::~RestServer() {
|
|
stop();
|
|
}
|
|
|
|
class MeOSResource : public restbed::Resource {
|
|
RestServer *server;
|
|
|
|
public:
|
|
MeOSResource(RestServer *server) : server(server) {}
|
|
|
|
~MeOSResource() {
|
|
}
|
|
|
|
RestServer &getServer() const { return *server; }
|
|
};
|
|
|
|
static void method_handler(const shared_ptr< restbed::Session > session) {
|
|
RestServer &server = dynamic_cast<const MeOSResource &>(*session->get_resource()).getServer();
|
|
server.handleRequest(session);
|
|
}
|
|
|
|
void RestServer::handleRequest(const shared_ptr<restbed::Session> &session) {
|
|
const auto request = session->get_request();
|
|
size_t content_length = request->get_header("Content-Length", 0);
|
|
|
|
chrono::time_point<chrono::system_clock> start, end;
|
|
start = chrono::system_clock::now();
|
|
|
|
auto param = request->get_query_parameters();
|
|
auto answer = RestServer::addRequest(param);
|
|
{
|
|
unique_lock<mutex> mlock(lock);
|
|
if (!waitForCompletion.wait_for(mlock, 10s, [answer] {return answer->isCompleted(); })) {
|
|
answer->answer = "Error (MeOS): Internal timeout";
|
|
}
|
|
|
|
end = chrono::system_clock::now();
|
|
chrono::duration<double> elapsed_seconds = end - start;
|
|
responseTimes.push_back(int(1000 * elapsed_seconds.count()));
|
|
}
|
|
// lock.unlock();
|
|
|
|
session->fetch(content_length, [request, answer](const shared_ptr< Session > session, const Bytes & body)
|
|
{
|
|
//fprintf(stdout, "%.*s\n", (int)body.size(), body.data());
|
|
/*while (!answer->state) {
|
|
std::this_thread::yield();
|
|
}*/
|
|
session->close(restbed::OK, answer->answer, { { "Content-Length", itos(answer->answer.length()) },{ "Connection", "close" } });
|
|
});
|
|
}
|
|
|
|
void RestServer::startThread(int port) {
|
|
auto settings = make_shared<Settings>();
|
|
settings->set_port(port);
|
|
auto resource = make_shared<MeOSResource>(this);
|
|
resource->set_path("/meos");
|
|
|
|
resource->set_method_handler("GET", method_handler);
|
|
restService->publish(resource);
|
|
|
|
restService->start(settings);
|
|
}
|
|
|
|
void RestServer::startService(int port) {
|
|
if (service)
|
|
throw meosException("Server started");
|
|
|
|
restService.reset(new restbed::Service());
|
|
service = make_shared<thread>(&RestServer::startThread, this, port);
|
|
}
|
|
|
|
void RestServer::stop() {
|
|
if (restService)
|
|
restService->stop();
|
|
|
|
if (service && service->joinable())
|
|
service->join();
|
|
|
|
restService.reset();
|
|
service.reset();
|
|
}
|
|
|
|
void RestServer::computeRequested(oEvent &ref) {
|
|
for (auto &server : startedServers) {
|
|
server->compute(ref);
|
|
}
|
|
}
|
|
|
|
void RestServer::compute(oEvent &ref) {
|
|
auto rq = getRequest();
|
|
if (!rq)
|
|
return;
|
|
|
|
try {
|
|
computeInternal(ref, rq);
|
|
}
|
|
catch (meosException &ex) {
|
|
rq->answer = "Error (MeOS): Error: " + ref.gdiBase().toUTF8(lang.tl(ex.wwhat()));
|
|
}
|
|
catch (std::exception &ex) {
|
|
rq->answer = "Error (MeOS): General Error: " + string(ex.what());
|
|
}
|
|
catch (...) {
|
|
rq->answer = "Error (MeOS): Unknown internal error.";
|
|
}
|
|
|
|
{
|
|
lock_guard<mutex> lg(lock);
|
|
rq->state = true;
|
|
}
|
|
waitForCompletion.notify_all();
|
|
}
|
|
|
|
|
|
void RestServer::computeInternal(oEvent &ref, shared_ptr<RestServer::EventRequest> &rq) {
|
|
if (rq->parameters.empty()) {
|
|
rq->answer = "<html><head>"
|
|
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset = UTF-8\">"
|
|
"<title>MeOS Information Service</title>"
|
|
"</head>"
|
|
"<body>"
|
|
"<h2>MeOS</h2><p>" + ref.gdiBase().toUTF8(lang.tl(getMeosFullVersion())) + "<p>"
|
|
"<ul>\n";
|
|
|
|
vector<oListParam> lists;
|
|
TabList::getPublicLists(ref, lists);
|
|
map<EStdListType, oListInfo> listMap;
|
|
ref.getListTypes(listMap, false);
|
|
for (auto &lp : lists) {
|
|
wstring n = lp.getName();
|
|
if (n.empty()) {
|
|
n = listMap[lp.listCode].getName();
|
|
}
|
|
lp.setName(n);
|
|
|
|
int keyCand = lp.listCode * 100;
|
|
bool done = false;
|
|
for (int i = 0; i < 100; i++) {
|
|
if (!listCache.count(keyCand + i)) {
|
|
keyCand += i;
|
|
listCache[keyCand].first = lp;
|
|
done = true;
|
|
break;
|
|
}
|
|
else if(listCache[keyCand + i].first == lp) {
|
|
keyCand = keyCand + i;
|
|
done = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!done) {
|
|
listCache[keyCand].first = lp;
|
|
listCache[keyCand].second.reset();
|
|
}
|
|
|
|
rq->answer += "<li><a href=\"?html=1&type=" + itos(keyCand) + "\">" + ref.gdiBase().toUTF8(n) + "</a></li>\n";
|
|
}
|
|
// "<li><a href=\"?html=1&result=1\">Resultat</a></li>"
|
|
// "<li><a href=\"?html=1&startlist=1\">Startlista</a></li>"
|
|
rq->answer += "</ul>\n";
|
|
|
|
HINSTANCE hInst = GetModuleHandle(0);
|
|
HRSRC hRes = FindResource(hInst, MAKEINTRESOURCE(132), RT_HTML);
|
|
HGLOBAL res = LoadResource(hInst, hRes);
|
|
char *html = (char *)LockResource(res);
|
|
int resSize = SizeofResource(hInst, hRes);
|
|
|
|
if (html) {
|
|
string htmlS;
|
|
while (*html != 0 && resSize > 0) {
|
|
if (*html == '*')
|
|
htmlS += "<";
|
|
else if (*html == '#')
|
|
htmlS += "&";
|
|
else
|
|
htmlS += *html;
|
|
++html;
|
|
resSize--;
|
|
}
|
|
|
|
rq->answer += htmlS;
|
|
}
|
|
|
|
rq->answer += "\n</body></html>\n";
|
|
}
|
|
else if (rq->parameters.count("get") > 0) {
|
|
string what = rq->parameters.find("get")->second;
|
|
getData(ref, what, rq->parameters, rq->answer);
|
|
}
|
|
else if (rq->parameters.count("html") > 0) {
|
|
string stype = rq->parameters.count("type") ? rq->parameters.find("type")->second : _EmptyString;
|
|
int type = atoi(stype.c_str());
|
|
auto res = listCache.find(type);
|
|
|
|
if (res != listCache.end()) {
|
|
gdioutput gdiPrint("print", ref.gdiBase().getScale(), ref.gdiBase().getCP());
|
|
gdiPrint.clearPage(false);
|
|
|
|
if (!res->second.second) {
|
|
res->second.second = make_shared<oListInfo>();
|
|
ref.generateListInfo(res->second.first, gdiPrint.getLineHeight(), *res->second.second);
|
|
}
|
|
ref.generateList(gdiPrint, true, *res->second.second, false);
|
|
wstring exportFile = getTempFile();
|
|
gdiPrint.writeHTML(exportFile, ref.getName(), 30);
|
|
ifstream fin(exportFile.c_str());
|
|
string rbf;
|
|
while (std::getline(fin, rbf)) {
|
|
rq->answer += rbf;
|
|
}
|
|
removeTempFile(exportFile);
|
|
}
|
|
else {
|
|
rq->answer = "Error (MeOS): Unknown list";
|
|
}
|
|
}
|
|
else {
|
|
rq->answer = "Error (MeOS): Unknown request";
|
|
}
|
|
}
|
|
|
|
void writePerson(const GeneralResult::GeneralResultInfo &res,
|
|
xmlbuffer &reslist,
|
|
bool includeTeam,
|
|
bool includeCourse,
|
|
vector< pair<string, wstring> > &rProp) {
|
|
int clsId = res.src->getClassId(true);
|
|
rProp.push_back(make_pair("cls", itow(clsId)));
|
|
if (includeCourse) {
|
|
pRunner pr = dynamic_cast<pRunner>(res.src);
|
|
if (pr && pr->getCourse(false))
|
|
rProp.push_back(make_pair("course", itow(pr->getCourse(false)->getId())));
|
|
}
|
|
bool hasTeam = res.src->getTeam() != 0 &&
|
|
clsId == res.src->getClassId(false) && // No Qualification/Final
|
|
(clsId == 0 || res.src->getClassRef(false)->getQualificationFinal() == 0); // // No Qualification/Final
|
|
|
|
if (includeTeam && hasTeam)
|
|
rProp.push_back(make_pair("team", itow(res.src->getTeam()->getId())));
|
|
|
|
if (hasTeam)
|
|
rProp.push_back(make_pair("leg", itow(1+pRunner(res.src)->getLegNumber())));
|
|
|
|
rProp.push_back(make_pair("stat", itow(res.status)));
|
|
|
|
if (res.score > 0)
|
|
rProp.push_back(make_pair("score", itow(res.score)));
|
|
|
|
rProp.push_back(make_pair("st", itow(10 * (res.src->getStartTime() + res.src->getEvent()->getZeroTimeNum()))));
|
|
|
|
if (res.src->getClassRef(false) == 0 || !res.src->getClassRef(true)->getNoTiming()) {
|
|
if (res.time > 0)
|
|
rProp.push_back(make_pair("rt", itow(10 * res.time)));
|
|
if (res.place > 0)
|
|
rProp.push_back(make_pair("place", itow(res.place)));
|
|
}
|
|
|
|
auto &subTag = reslist.startTag("person", rProp);
|
|
rProp.clear();
|
|
rProp.push_back(make_pair("id", itow(res.src->getId())));
|
|
subTag.write("name", rProp, res.src->getName());
|
|
rProp.clear();
|
|
|
|
if (res.src->getClubRef()) {
|
|
rProp.push_back(make_pair("id", itow(res.src->getClubId())));
|
|
subTag.write("org", rProp, res.src->getClub());
|
|
rProp.clear();
|
|
}
|
|
|
|
subTag.endTag();
|
|
}
|
|
|
|
void RestServer::getData(oEvent &oe, const string &what, const multimap<string, string> ¶m, string &answer) {
|
|
xmlbuffer out;
|
|
out.setComplete(true);
|
|
bool okRequest = false;
|
|
if (what == "iofresult") {
|
|
wstring exportFile = getTempFile();
|
|
bool useUTC = false;
|
|
set<int> cls;
|
|
if (param.count("class") > 0)
|
|
getSelection(param.find("class")->second, cls);
|
|
|
|
oe.exportIOFSplits(oEvent::IOF30, exportFile.c_str(), false, useUTC, cls, -1, false, false, true, false);
|
|
ifstream fin(exportFile.c_str());
|
|
string rbf;
|
|
while (std::getline(fin, rbf)) {
|
|
answer += rbf;
|
|
}
|
|
removeTempFile(exportFile);
|
|
okRequest = true;
|
|
}
|
|
else if (what == "iofstart") {
|
|
wstring exportFile = getTempFile();
|
|
bool useUTC = false;
|
|
set<int> cls;
|
|
if (param.count("class") > 0)
|
|
getSelection(param.find("class")->second, cls);
|
|
|
|
oe.exportIOFStartlist(oEvent::IOF30, exportFile.c_str(), useUTC, cls, false, true, false);
|
|
ifstream fin(exportFile.c_str());
|
|
string rbf;
|
|
while (std::getline(fin, rbf)) {
|
|
answer += rbf;
|
|
}
|
|
removeTempFile(exportFile);
|
|
okRequest = true;
|
|
}
|
|
else if (what == "competition") {
|
|
InfoCompetition cmp(0);
|
|
cmp.synchronize(oe);
|
|
cmp.serialize(out, false);
|
|
okRequest = true;
|
|
}
|
|
else if (what == "class") {
|
|
vector<pClass> cls;
|
|
oe.getClasses(cls, true);
|
|
set<int> ctrlW;
|
|
vector<pControl> ctrl;
|
|
oe.getControls(ctrl, true);
|
|
for (size_t k = 0; k < ctrl.size(); k++) {
|
|
if (ctrl[k]->isValidRadio()) {
|
|
vector<int> cc;
|
|
ctrl[k]->getCourseControls(cc);
|
|
ctrlW.insert(cc.begin(), cc.end());
|
|
}
|
|
}
|
|
for (auto c : cls) {
|
|
InfoClass iCls(c->getId());
|
|
iCls.synchronize(false, *c, ctrlW);
|
|
iCls.serialize(out, false);
|
|
}
|
|
okRequest = true;
|
|
}
|
|
else if (what == "organization") {
|
|
vector<pClub> clb;
|
|
oe.getClubs(clb, true);
|
|
for (auto c : clb) {
|
|
InfoOrganization iClb(c->getId());
|
|
iClb.synchronize(*c);
|
|
iClb.serialize(out, false);
|
|
}
|
|
okRequest = true;
|
|
}
|
|
else if (what == "competitor") {
|
|
vector<pRunner> r;
|
|
set<int> selection;
|
|
if (param.count("class") > 0)
|
|
getSelection(param.find("class")->second, selection);
|
|
|
|
oe.getRunners(selection.size() == 1 ? *selection.begin() : 0, -1, r, true);
|
|
|
|
{
|
|
vector<pRunner> r2;
|
|
r2.reserve(r.size());
|
|
for (pRunner tr : r) {
|
|
pClass cls = tr->getClassRef(true);
|
|
if (cls && cls->getQualificationFinal() && tr->getLegNumber() != 0)
|
|
continue;
|
|
if (selection.empty() || (cls && selection.count(cls->getId())))
|
|
r2.push_back(tr);
|
|
}
|
|
r.swap(r2);
|
|
}
|
|
|
|
for (auto c : r) {
|
|
InfoCompetitor iR(c->getId());
|
|
iR.synchronize(false, false, *c);
|
|
iR.serialize(out, false);
|
|
}
|
|
okRequest = true;
|
|
}
|
|
else if (what == "team") {
|
|
vector<pTeam> teams;
|
|
set<int> selection;
|
|
if (param.count("class") > 0)
|
|
getSelection(param.find("class")->second, selection);
|
|
|
|
oe.getTeams(selection.size() == 1 ? *selection.begin() : 0, teams, true);
|
|
|
|
if (selection.size() > 1) {
|
|
vector<pTeam> r2;
|
|
r2.reserve(teams.size());
|
|
for (pTeam tr : teams) {
|
|
if (selection.count(tr->getClassId(false)))
|
|
r2.push_back(tr);
|
|
}
|
|
teams.swap(r2);
|
|
}
|
|
|
|
for (auto c : teams) {
|
|
InfoTeam iT(c->getId());
|
|
iT.synchronize(*c);
|
|
iT.serialize(out, false);
|
|
}
|
|
okRequest = true;
|
|
}
|
|
else if (what == "control") {
|
|
vector<pControl> ctrl;
|
|
oe.getControls(ctrl, true);
|
|
vector< pair<string, wstring> > prop(1);
|
|
prop[0].first = "id";
|
|
for (pControl c : ctrl) {
|
|
int nd = c->getNumberDuplicates();
|
|
for (int k = 0; k < nd; k++) {
|
|
prop[0].second = itow(oControl::getCourseControlIdFromIdIndex(c->getId(), k));
|
|
if (nd > 1)
|
|
out.write("control", prop, c->getName() + L"-" + itow(k+1));
|
|
else
|
|
out.write("control", prop, c->getName());
|
|
}
|
|
}
|
|
okRequest = true;
|
|
}
|
|
else if (what == "result") {
|
|
okRequest = true;
|
|
vector<pRunner> r;
|
|
set<int> selection;
|
|
pClass sampleClass = 0;
|
|
if (param.count("class") > 0) {
|
|
getSelection(param.find("class")->second, selection);
|
|
if (!selection.empty())
|
|
sampleClass = oe.getClass(*selection.begin());
|
|
}
|
|
|
|
if (sampleClass == 0) {
|
|
vector<pClass> tt;
|
|
oe.getClasses(tt, false);
|
|
if (!tt.empty())
|
|
sampleClass = tt[0];
|
|
}
|
|
|
|
string resTag;
|
|
if (param.count("module") > 0)
|
|
resTag = param.find("module")->second;
|
|
|
|
pair<int,int> controlId(oPunch::PunchStart, oPunch::PunchFinish);
|
|
bool totalResult = false;
|
|
if (param.count("total")) {
|
|
const string &tot = param.find("total")->second;
|
|
totalResult = atoi(tot.c_str()) > 0 || _stricmp(tot.c_str(), "true") == 0;
|
|
}
|
|
|
|
if (param.count("to")) {
|
|
const string &cid = param.find("to")->second;
|
|
|
|
controlId.second = atoi(cid.c_str());
|
|
if (controlId.second == 0)
|
|
controlId.second = oControl::getControlIdByName(oe, cid);
|
|
|
|
if (controlId.second == 0)
|
|
throw meosException("Unknown control: " + cid);
|
|
}
|
|
|
|
if (param.count("from")) {
|
|
const string &cid = param.find("from")->second;
|
|
|
|
controlId.first = atoi(cid.c_str());
|
|
if (controlId.first == 0)
|
|
controlId.first = oControl::getControlIdByName(oe, cid);
|
|
|
|
if (controlId.first == 0)
|
|
throw meosException("Unknown control: " + cid);
|
|
}
|
|
|
|
oListInfo::ResultType resType = oListInfo::Classwise;
|
|
wstring resTypeStr = L"classindividual";
|
|
|
|
ClassType ct = sampleClass ? sampleClass->getClassType() : ClassType::oClassIndividual;
|
|
bool team = ct == oClassPatrol || ct == oClassRelay;
|
|
int limit = 100000;
|
|
if (param.count("limit")) {
|
|
limit = atoi(param.find("limit")->second.c_str());
|
|
if (limit <= 0)
|
|
throw meosException("Invalid limit: " + param.find("limit")->second);
|
|
}
|
|
|
|
if (param.count("type")) {
|
|
string type = param.find("type")->second;
|
|
|
|
if (type == "GlobalIndividual") {
|
|
resType = oListInfo::Global;
|
|
team = false;
|
|
}
|
|
else if (type == "ClassIndividual") {
|
|
resType = oListInfo::Classwise;
|
|
team = false;
|
|
}
|
|
else if (type == "CourseIndividual") {
|
|
resType = oListInfo::Coursewise;
|
|
team = false;
|
|
}
|
|
else if (type == "LegIndividual") {
|
|
resType = oListInfo::Legwise;
|
|
team = false;
|
|
}
|
|
else if (type == "GlobalTeam") {
|
|
resType = oListInfo::Global;
|
|
team = true;
|
|
}
|
|
else if (type == "ClassTeam") {
|
|
resType = oListInfo::Classwise;
|
|
team = true;
|
|
}
|
|
else
|
|
throw meosException("Unknown type: " + type);
|
|
|
|
string2Wide(type, resTypeStr);
|
|
resTypeStr = canonizeName(resTypeStr.c_str());
|
|
}
|
|
else if (team) {
|
|
resTypeStr = L"classteam";
|
|
}
|
|
|
|
int inputNumber = 0;
|
|
|
|
|
|
if (param.count("argument")) {
|
|
const string &arg = param.find("argument")->second;
|
|
inputNumber = atoi(arg.c_str());
|
|
}
|
|
|
|
vector<GeneralResult::GeneralResultInfo> results;
|
|
vector< pair<string, wstring> > prop, noProp, rProp;
|
|
|
|
prop.push_back(make_pair("type", resTypeStr));
|
|
|
|
int leg = -1;
|
|
if (param.count("leg") > 0) {
|
|
string legs = param.find("leg")->second;
|
|
leg = atoi(legs.c_str())-1;
|
|
if (leg < 0 || leg > 32)
|
|
throw meosException("Invalid leg: " + legs);
|
|
}
|
|
|
|
if (ct == oClassRelay) {
|
|
if (leg == -1)
|
|
prop.push_back(make_pair("leg", L"Last"));
|
|
else
|
|
prop.push_back(make_pair("leg", itow(leg + 1)));
|
|
}
|
|
|
|
if (!resTag.empty())
|
|
prop.push_back(make_pair("module", oe.gdiBase().widen(resTag) + L"(" + itow(inputNumber) + L")"));
|
|
|
|
if (controlId.first != oPunch::PunchStart) {
|
|
pair<int, int> idIx = oControl::getIdIndexFromCourseControlId(controlId.first);
|
|
pControl ctrl = oe.getControl(idIx.first);
|
|
if (ctrl == 0)
|
|
throw meosException("Unknown control: " + itos(idIx.first));
|
|
|
|
wstring loc = ctrl->getName();
|
|
if (idIx.second > 0)
|
|
loc += L"-" + itow(idIx.second + 1);
|
|
|
|
prop.push_back(make_pair("from", loc));
|
|
}
|
|
|
|
if (controlId.second == oPunch::PunchFinish)
|
|
prop.push_back(make_pair("to", L"Finish"));
|
|
else {
|
|
pair<int, int> idIx = oControl::getIdIndexFromCourseControlId(controlId.second);
|
|
pControl ctrl = oe.getControl(idIx.first);
|
|
if (ctrl == 0)
|
|
throw meosException("Unknown control: " + itos(idIx.first));
|
|
|
|
wstring loc = ctrl->getName();
|
|
if (idIx.second > 0)
|
|
loc += L"-" + itow(idIx.second+1);
|
|
|
|
prop.push_back(make_pair("to", loc));
|
|
}
|
|
|
|
if (!team) {
|
|
oe.getRunners(selection.size() == 1 ? *selection.begin() : 0, -1, r, false);
|
|
|
|
{
|
|
vector<pRunner> r2;
|
|
r2.reserve(r.size());
|
|
for (pRunner tr : r) {
|
|
pClass cls = tr->getClassRef(true);
|
|
if (cls && cls->getQualificationFinal() && tr->getLegNumber() != 0)
|
|
continue;
|
|
if (selection.empty() || (cls && selection.count(cls->getId())))
|
|
r2.push_back(tr);
|
|
}
|
|
r.swap(r2);
|
|
}
|
|
|
|
GeneralResult::calculateIndividualResults(r, controlId, totalResult, resTag, resType, inputNumber, oe, results);
|
|
|
|
if (resType == oListInfo::Classwise)
|
|
sort(results.begin(), results.end());
|
|
/*else if (resType == oListInfo::Coursewise) {
|
|
sort(results.begin(), results.end(),
|
|
[](const GeneralResult::GeneralResultInfo &a, const GeneralResult::GeneralResultInfo &b)->
|
|
bool {
|
|
pCourse ac = dynamic_cast<const oRunner &>(*a.src).getCourse(false);
|
|
pCourse bc = dynamic_cast<const oRunner &>(*b.src).getCourse(false);
|
|
if (ac != bc)
|
|
return ac->getId() < bc->getId();
|
|
|
|
return a.compareResult(b);
|
|
}
|
|
);
|
|
}*/
|
|
auto &reslist = out.startTag("results", prop);
|
|
|
|
int place = -1;
|
|
int cClass = -1;
|
|
int counter = 0;
|
|
for (const auto &res : results) {
|
|
if (res.src->getClassId(true) != cClass) {
|
|
counter = 0;
|
|
place = 1;
|
|
cClass = res.src->getClassId(true);
|
|
}
|
|
if (++counter > limit && (place != res.place || res.status != StatusOK))
|
|
continue;
|
|
place = res.place;
|
|
writePerson(res, reslist, true, resType == oListInfo::Coursewise, rProp);
|
|
}
|
|
reslist.endTag();
|
|
}
|
|
else {
|
|
//Teams
|
|
vector<pTeam> teams;
|
|
oe.getTeams(selection.size() == 1 ? *selection.begin() : 0, teams, true);
|
|
|
|
if (selection.size() > 1) {
|
|
vector<pTeam> r2;
|
|
r2.reserve(teams.size());
|
|
for (pTeam tr : teams) {
|
|
if (selection.count(tr->getClassId(true)))
|
|
r2.push_back(tr);
|
|
}
|
|
teams.swap(r2);
|
|
}
|
|
auto context = GeneralResult::calculateTeamResults(teams, leg, controlId, totalResult, resTag, resType, inputNumber, oe, results);
|
|
|
|
sort(results.begin(), results.end());
|
|
auto &reslist = out.startTag("results", prop);
|
|
|
|
int place = -1;
|
|
int cClass = -1;
|
|
int counter = 0;
|
|
for (const auto &res : results) {
|
|
pTeam team = pTeam(res.src);
|
|
if (leg >= team->getNumRunners())
|
|
continue;
|
|
|
|
if (res.src->getClassId(true) != cClass) {
|
|
counter = 0;
|
|
place = 1;
|
|
cClass = res.src->getClassId(true);
|
|
}
|
|
if (++counter > limit && (place != res.place || res.status != StatusOK))
|
|
continue;
|
|
place = res.place;
|
|
|
|
rProp.push_back(make_pair("cls", itow(res.src->getClassId(true))));
|
|
rProp.push_back(make_pair("stat", itow(res.status)));
|
|
if (res.score > 0)
|
|
rProp.push_back(make_pair("score", itow(res.score)));
|
|
|
|
if (res.src->getStartTime() > 0)
|
|
rProp.push_back(make_pair("st", itow(10 * (res.src->getStartTime() + oe.getZeroTimeNum()))));
|
|
|
|
if (res.src->getClassRef(false) == 0 || !res.src->getClassRef(true)->getNoTiming()) {
|
|
if (res.time > 0)
|
|
rProp.push_back(make_pair("rt", itow(10 * res.time)));
|
|
if (res.place > 0)
|
|
rProp.push_back(make_pair("place", itow(res.place)));
|
|
}
|
|
|
|
auto &subTag = reslist.startTag("team", rProp);
|
|
rProp.clear();
|
|
rProp.push_back(make_pair("id", itow(res.src->getId())));
|
|
subTag.write("name", rProp, res.src->getName());
|
|
rProp.clear();
|
|
|
|
if (res.src->getClubRef()) {
|
|
rProp.push_back(make_pair("id", itow(res.src->getClubId())));
|
|
subTag.write("org", rProp, res.src->getClub());
|
|
rProp.clear();
|
|
}
|
|
|
|
int subRes = res.getNumSubresult(*context);
|
|
GeneralResult::GeneralResultInfo out;
|
|
|
|
for (int k = 0; k < subRes; k++) {
|
|
if (res.getSubResult(*context, k, out)) {
|
|
writePerson(out, subTag, false, false, rProp);
|
|
}
|
|
}
|
|
|
|
subTag.endTag();
|
|
}
|
|
reslist.endTag();
|
|
|
|
}
|
|
}
|
|
if (out.size() > 0) {
|
|
xmlparser mem;
|
|
mem.openMemoryOutput(false);
|
|
mem.startTag("MOPComplete", "xmlns", "http://www.melin.nu/mop");
|
|
out.commit(mem, 100000);
|
|
mem.endTag();
|
|
mem.getMemoryOutput(answer);
|
|
}
|
|
else if (!okRequest) {
|
|
answer = "Error (MeOS): Unknown command '" + what + "'";
|
|
}
|
|
}
|
|
|
|
void RestServer::getSelection(const string ¶m, set<int> &sel) {
|
|
vector<string> sw;
|
|
split(param, ";,", sw);
|
|
for (auto &s : sw) {
|
|
int id = atoi(s.c_str());
|
|
if (id > 0)
|
|
sel.insert(id);
|
|
}
|
|
}
|
|
|
|
shared_ptr<RestServer::EventRequest> RestServer::getRequest() {
|
|
shared_ptr<EventRequest> res;
|
|
if (hasAnyRequest) {
|
|
lock_guard<mutex> lg(lock);
|
|
if (!requests.empty()) {
|
|
res = requests.front();
|
|
requests.pop_front();
|
|
}
|
|
if (requests.empty())
|
|
hasAnyRequest = false;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
shared_ptr<RestServer::EventRequest> RestServer::addRequest(multimap<string, string> ¶m) {
|
|
auto rq = make_shared<EventRequest>();
|
|
rq->parameters.swap(param);
|
|
lock_guard<mutex> lg(lock);
|
|
requests.push_back(rq);
|
|
hasAnyRequest = true;
|
|
return rq;
|
|
}
|
|
|
|
void RestServer::getStatistics(Statistics &s) {
|
|
lock_guard<mutex> lg(lock);
|
|
s.numRequests = responseTimes.size();
|
|
s.maxResponseTime = 0;
|
|
s.averageResponseTime = 0;
|
|
for (int t : responseTimes) {
|
|
s.maxResponseTime = max(s.maxResponseTime, t);
|
|
s.averageResponseTime += t;
|
|
}
|
|
if (s.numRequests > 0) {
|
|
s.averageResponseTime /= s.numRequests;
|
|
}
|
|
}
|