/************************************************************************
MeOS - Orienteering Software
Copyright (C) 2009-2022 Melin Software HB
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
Melin Software HB - software@melin.nu - www.melin.nu
Eksoppsvägen 16, SE-75646 UPPSALA, Sweden
************************************************************************/
#include "stdafx.h"
#include "resource.h"
#include
#include
#include
#include
#include
#include "oEvent.h"
#include "gdioutput.h"
#include "onlineinput.h"
#include "meos_util.h"
#include
#include "gdiconstants.h"
#include "meosException.h"
#include "Download.h"
#include "xmlparser.h"
#include "progress.h"
#include "csvparser.h"
#include "machinecontainer.h"
#include "SportIdent.h"
#include "TabSI.h"
int AutomaticCB(gdioutput *gdi, int type, void *data);
static int OnlineCB(gdioutput *gdi, int type, void *data) {
switch (type) {
case GUI_BUTTON: {
//Make a copy
ButtonInfo bu=*static_cast(data);
OnlineInput &ores = dynamic_cast(*AutoMachine::getMachine(bu.getExtraInt()));
return ores.processButton(*gdi, bu);
}
case GUI_LISTBOX:{
}
}
return 0;
}
OnlineInput::~OnlineInput() {
}
int OnlineInput::processButton(gdioutput &gdi, ButtonInfo &bi) {
if (bi.id == "SaveMapping") {
int ctrl = gdi.getTextNo("Code");
if (ctrl<1)
throw meosException("Ogiltig kontrollkod");
ListBoxInfo lbi;
if (!gdi.getSelectedItem("Function", lbi))
throw meosException("Ogiltig funktion");
specialPunches[ctrl] = (oPunch::SpecialPunch)lbi.data;
fillMappings(gdi);
}
else if (bi.id == "RemoveMapping") {
set sel;
gdi.getSelection("Mappings", sel);
for (set::iterator it = sel.begin(); it != sel.end(); ++it) {
specialPunches.erase(*it);
}
fillMappings(gdi);
}
else if (bi.id == "UseROC") {
useROCProtocol = gdi.isChecked(bi.id);
if (useROCProtocol) {
gdi.setText("URL", L"http://roc.olresultat.se/getpunches.asp");
}
else {
gdi.check("UseUnitId", false);
gdi.setTextTranslate("CmpID_label", L"Tävlingens ID-nummer:", true);
useUnitId = false;
}
gdi.setInputStatus("UseUnitId", useROCProtocol);
}
else if (bi.id == "UseUnitId") {
useUnitId = gdi.isChecked(bi.id);
if (useUnitId)
gdi.setTextTranslate("CmpID_label", L"Enhetens ID-nummer (MAC):", true);
else
gdi.setTextTranslate("CmpID_label", L"Tävlingens ID-nummer:", true);
}
return 0;
}
void OnlineInput::fillMappings(gdioutput &gdi) const{
gdi.clearList("Mappings");
for (map::const_iterator it = specialPunches.begin(); it != specialPunches.end(); ++it) {
gdi.addItem("Mappings", itow(it->first) + L" -> " + oPunch::getType(it->second), it->first);
}
}
void OnlineInput::settings(gdioutput &gdi, oEvent &oe, State state) {
int iv = interval;
if (state == State::Create) {
iv = 10;
url = oe.getPropertyString("MIPURL", L"");
}
wstring time;
if (iv>0)
time = itow(iv);
settingsTitle(gdi, "Inmatning online");
startCancelInterval(gdi, "Save", state, IntervalSecond, time);
gdi.addInput("URL", url, 40, 0, L"URL:", L"Till exempel X#http://www.input.org/online.php");
gdi.addCheckbox("UseROC", "Använd ROC-protokoll", OnlineCB, useROCProtocol).setExtra(getId());
gdi.addCheckbox("UseUnitId", "Använd enhets-id istället för tävlings-id", OnlineCB, useROCProtocol & useUnitId).setExtra(getId());
gdi.setInputStatus("UseUnitId", useROCProtocol);
gdi.addInput("CmpID", itow(cmpId), 10, 0, L"Tävlingens ID-nummer:");
gdi.dropLine(1);
gdi.addString("", boldText, "Kontrollmappning");
gdi.dropLine(0.5);
gdi.fillRight();
gdi.addInput("Code", L"", 4, 0, L"Kod:");
gdi.addSelection("Function", 80, 200, 0, L"Funktion:");
gdi.addItem("Function", lang.tl("Mål"), oPunch::PunchFinish);
gdi.addItem("Function", lang.tl("Start"), oPunch::PunchStart);
gdi.addItem("Function", lang.tl("Check"), oPunch::PunchCheck);
gdi.dropLine();
gdi.addButton("SaveMapping", "Lägg till", OnlineCB).setExtra(getId());
gdi.popX();
gdi.dropLine(2);
gdi.addListBox("Mappings", 150, 100, 0, L"Definierade mappningar:", L"", true);
gdi.dropLine();
gdi.addButton("RemoveMapping", "Ta bort", OnlineCB).setExtra(getId());
fillMappings(gdi);
gdi.setCY(gdi.getHeight());
gdi.popX();
gdi.addString("", 10, "help:onlineinput");
}
void OnlineInput::save(oEvent &oe, gdioutput &gdi, bool doProcess) {
AutoMachine::save(oe, gdi, doProcess);
int iv=gdi.getTextNo("Interval");
const wstring &xurl=gdi.getText("URL");
if (!xurl.empty())
oe.setProperty("MIPURL", xurl);
cmpId = gdi.getTextNo("CmpID");
unitId = gdi.getText("CmpID");
if (xurl.empty()) {
throw meosException("URL måste anges.");
}
url = xurl;
if (doProcess) {
process(gdi, &oe, SyncNone);
interval = iv;
}
}
void OnlineInput::status(gdioutput &gdi)
{
AutoMachine::status(gdi);
gdi.fillRight();
gdi.addString("", 0, "URL:");
gdi.addStringUT(0, url);
gdi.popX();
gdi.dropLine(1);
gdi.addString("", 0, "Antal hämtade uppdateringar X (Y kb)#" +
itos(importCounter-1) + "#" + itos(bytesImported/1024));
gdi.popX();
gdi.fillDown();
gdi.dropLine(2);
for (size_t k = 0; k < info.size(); k++) {
gdi.addString("", 0, info[k]);
}
gdi.fillRight();
gdi.dropLine(1);
gdi.addButton("Stop", "Stoppa automaten", AutomaticCB).setExtra(getId());
gdi.fillDown();
gdi.addButton("OnlineInput", "Inställningar...", AutomaticCB).setExtra(getId());
gdi.popX();
}
void OnlineInput::saveMachine(oEvent &oe, const wstring &guiInterval) {
auto &cnt = oe.getMachineContainer().set(getTypeString(), getMachineName());
cnt.set("url", url);
cnt.set("cmpId", cmpId);
cnt.set("unitId", unitId);
cnt.set("ROC", useROCProtocol);
cnt.set("useId", useUnitId);
int iv = _wtoi(guiInterval.c_str());
cnt.set("interval", iv);
vector pm;
for (auto &v : specialPunches) {
pm.push_back(v.first);
pm.push_back(v.second);
}
cnt.set("map", pm);
}
void OnlineInput::loadMachine(oEvent &oe, const wstring &name) {
auto *cnt = oe.getMachineContainer().get(getTypeString(), name);
if (!cnt)
return;
AutoMachine::loadMachine(oe, name);
url = cnt->getString("url");
cmpId = cnt->getInt("cmpId");
unitId = cnt->getString("unitId");
useROCProtocol = cnt->getInt("ROC") != 0;
useUnitId = cnt->getInt("useId") != 0;
interval = cnt->getInt("interval");
specialPunches.clear();
vector pm = cnt->getVectorInt("map");
for (size_t j = 0; j + 1 < pm.size(); j+=2) {
specialPunches[pm[j]] = oPunch::SpecialPunch(pm[j + 1]);
}
}
void OnlineInput::process(gdioutput &gdi, oEvent *oe, AutoSyncType ast) {
oe->autoSynchronizeLists(true);
try {
Download dwl;
dwl.initInternet();
ProgressWindow pw(0);
vector > key;
wstring q;
if (useROCProtocol) {
if (!useUnitId)
q = L"?unitId=" + itow(cmpId) + L"&lastId=" + itow(lastImportedId) + L"&date=" + oe->getDate() + L"&time=" + oe->getZeroTime();
else
q = L"?unitId=" + unitId + L"&lastId=" + itow(lastImportedId) + L"&date=" + oe->getDate() + L"&time=" + oe->getZeroTime();
}
else {
pair mk1(L"competition", itow(cmpId));
key.push_back(mk1);
pair mk2(L"lastid", itow(lastImportedId));
key.push_back(mk2);
}
wstring result = getTempFile();
dwl.downloadFile(url + q, result, key);
dwl.downLoadNoThread();
if (!useROCProtocol) {
xmlobject res;
xmlparser xml;
try {
xml.read(result);
res = xml.getObject("MIPData");
}
catch(std::exception &) {
throw meosException("Onlineservern svarade felaktigt.");
}
xmlList entries;
res.getObjects("entry", entries);
processEntries(*oe, entries);
xmlList cards;
res.getObjects("card", cards);
processCards(gdi, *oe, cards);
xmlList punches;
res.getObjects("p", punches);
processPunches(*oe, punches);
xmlList teamlineup;
res.getObjects("team", teamlineup);
processTeamLineups(*oe, teamlineup);
lastImportedId = res.getAttrib("lastid").getInt();
}
else {
csvparser csv;
list< vector > rocData;
csv.parse(result, rocData);
processPunches(*oe, rocData);
}
struct _stat st;
_wstat(result.c_str(), &st);
bytesImported += st.st_size;
removeTempFile(result);
}
catch (meosException &ex) {
if (ast == SyncNone)
throw;
else
gdi.addInfoBox("", wstring(L"Online Input Error X#") + ex.wwhat(), 5000);
}
catch(std::exception &ex) {
if (ast == SyncNone)
throw;
else
gdi.addInfoBox("", wstring(L"Online Input Error X#")+gdi.widen(ex.what()), 5000);
}
importCounter++;
}
void OnlineInput::processPunches(oEvent &oe, const xmlList &punches) {
for (size_t k = 0; k < punches.size(); k++) {
int code = punches[k].getObjectInt("code");
wstring startno;
punches[k].getObjectString("sno", startno);
if (specialPunches.count(code))
code = specialPunches[code];
pRunner r = 0;
int card = punches[k].getObjectInt("card");
int time = punches[k].getObjectInt("time") / 10;
time = oe.getRelativeTime(formatTimeHMS(time));
if (startno.length() > 0)
r = oe.getRunnerByBibOrStartNo(startno, false);
else
r = oe.getRunnerByCardNo(card, time, oEvent::CardLookupProperty::Any);
wstring rname;
if (r) {
rname = r->getName();
card = r->getCardNo();
}
else {
rname=lang.tl("Okänd");
}
if (time < 0) {
time = 0;
addInfo(L"Ogiltig tid");
}
oe.addFreePunch(time, code, card, true);
addInfo(L"Löpare: X, kontroll: Y, kl Z#" + rname + L"#" + oPunch::getType(code) + L"#" + oe.getAbsTime(time));
}
}
void OnlineInput::processPunches(oEvent &oe, list< vector > &rocData) {
for (list< vector >::iterator it = rocData.begin(); it != rocData.end(); ++it) {
vector &line = *it;
if (line.size() == 4) {
int punchId = _wtoi(line[0].c_str());
int code = _wtoi(line[1].c_str());
int card = _wtoi(line[2].c_str());
wstring timeS = line[3].substr(11);
int time = oe.getRelativeTime(timeS);
if (specialPunches.count(code))
code = specialPunches[code];
pRunner r = oe.getRunnerByCardNo(card, time, oEvent::CardLookupProperty::Any);
wstring rname;
if (r) {
rname = r->getName();
card = r->getCardNo();
}
else {
rname=lang.tl("Okänd");
}
if (time < 0) {
time = 0;
addInfo(L"Ogiltig tid");
}
oe.addFreePunch(time, code, card, true);
lastImportedId = max(lastImportedId, punchId);
addInfo(L"Löpare: X, kontroll: Y, kl Z#" + rname + L"#" + oPunch::getType(code) + L"#" + oe.getAbsTime(time));
}
else
throw meosException("Onlineservern svarade felaktigt.");
}
}
void OnlineInput::processCards(gdioutput &gdi, oEvent &oe, const xmlList &cards) {
for (size_t k = 0; k < cards.size(); k++) {
SICard sic(ConvertedTimeStatus::Hour24);
sic.CardNumber = cards[k].getObjectInt("number");
if (cards[k].getObject("finish"))
sic.FinishPunch.Time = cards[k].getObject("finish").getObjectInt("time") / 10;
if (cards[k].getObject("start"))
sic.StartPunch.Time = cards[k].getObject("start").getObjectInt("time") / 10;
xmlList punches;
cards[k].getObjects("p", punches);
for (size_t j = 0; j < punches.size(); j++) {
sic.Punch[j].Code = punches[j].getObjectInt("code");
sic.Punch[j].Time = punches[j].getObjectInt("time") / 10;
}
sic.nPunch = punches.size();
TabSI::getSI(gdi).addCard(sic);
}
}
void OnlineInput::processTeamLineups(oEvent &oe, const xmlList &updates) {
}
void OnlineInput::processEntries(oEvent &oe, const xmlList &entries) {
map raceId2R;
vector runners;
oe.getRunners(0, 0, runners);
for (auto &r : runners) {
int stored = r->getDCI().getInt("RaceId");
if (stored > 0 && (stored & (1 << 30))) {
int v = (stored & ~(1 << 30));
raceId2R[v] = r;
}
}
for (auto &entry : entries) {
pClass cls = nullptr;
int classId = entry.getObjectInt("classid");
if (classId > 0)
cls = oe.getClass(classId);
if (!cls) {
wstring clsName;
entry.getObjectString("classname", clsName);
cls = oe.getClass(clsName);
}
if (!cls)
continue;
int fee = entry.getObjectInt("fee");
bool paid = entry.getObjectBool("paid");
xmlobject xname = entry.getObject("name");
int birthyear = 0;
if (xname) {
birthyear = xname.getObjectInt("birthyear");
}
wstring name;
entry.getObjectString("name", name);
if (name.empty())
continue;
wstring club;
entry.getObjectString("club", club);
int cardNo = 0;
bool hiredCard = false;
xmlobject card = entry.getObject("card");
if (card) {
cardNo = card.getInt();
hiredCard = card.getObjectBool("hired");
}
if (!hiredCard && oe.hasHiredCardData())
hiredCard = oe.isHiredCard(cardNo);
pRunner r = nullptr;
int id = entry.getObjectInt("id");
if (id > 0) {
auto res = raceId2R.find(id);
if (res != raceId2R.end()) {
r = res->second;
}
}
else {
if (cardNo != 0) {
r = oe.getRunnerByCardNo(cardNo, 0, oEvent::CardLookupProperty::Any);
if (r && !r->matchName(name))
r = nullptr;
}
if (r == nullptr) {
r = oe.getRunnerByName(name, club);
if (r && r->getCardNo() != cardNo)
r = nullptr;
}
}
if (r == nullptr)
r = oe.addRunner(name, club, cls->getId(), cardNo, birthyear, true);
else {
r->setName(name, false);
r->setClub(club);
r->setBirthYear(birthyear);
r->setCardNo(cardNo, false, false);
r->setClassId(cls->getId(), true);
}
if (fee == 0)
fee = r->getDefaultFee();
r->getDI().setInt("Fee", fee);
int toPay = fee;
int cf = 0;
if (hiredCard) {
cf = r->getEvent()->getBaseCardFee();
if (cf > 0)
toPay += cf;
}
r->setFlag(oRunner::FlagAddedViaAPI, true);
r->getDI().setInt("CardFee", cf);
r->getDI().setInt("Paid", paid ? toPay : 0);
if (id > 0) {
r->getDI().setInt("RaceId", id | (1 << 30));
raceId2R[id] = r;
}
r->synchronize(true);
}
}