976 lines
26 KiB
C++
976 lines
26 KiB
C++
/************************************************************************
|
|
MeOS - Orienteering Software
|
|
Copyright (C) 2009-2017 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
|
|
|
|
************************************************************************/
|
|
|
|
// oEvent.cpp: implementation of the oEvent class.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
#include "stdafx.h"
|
|
|
|
#include <vector>
|
|
#include "oEvent.h"
|
|
#include "gdioutput.h"
|
|
#include "oDataContainer.h"
|
|
#include "csvparser.h"
|
|
|
|
#include "TabAuto.h"
|
|
|
|
#include "random.h"
|
|
#include "SportIdent.h"
|
|
#include "meosdb/sqltypes.h"
|
|
#include "meosexception.h"
|
|
#include "meos_util.h"
|
|
|
|
#include "meos.h"
|
|
#include <cassert>
|
|
typedef bool (__cdecl* OPENDB_FCN)(void);
|
|
typedef int (__cdecl* SYNCHRONIZE_FCN)(oBase *obj);
|
|
|
|
|
|
|
|
bool oEvent::connectToServer()
|
|
{
|
|
if (isThreadReconnecting())
|
|
return false;
|
|
|
|
#ifdef BUILD_DB_DLL
|
|
if (msOpenDatabase)
|
|
return true;
|
|
#endif
|
|
|
|
#ifdef BUILD_DB_DLL
|
|
hMod=LoadLibrary("meosdb.dll");
|
|
|
|
msOpenDatabase = (SYNCHRONIZE_FCN)GetProcAddress(hMod, "msOpenDatabase");
|
|
msConnectToServer = (SYNCHRONIZE_FCN)GetProcAddress(hMod, "msConnectToServer");
|
|
msSynchronizeUpdate = (SYNCHRONIZE_FCN)GetProcAddress(hMod, "msSynchronizeUpdate");
|
|
msSynchronizeRead = (SYNCHRONIZE_FCN)GetProcAddress(hMod, "msSynchronizeRead");
|
|
msSynchronizeList = (SYNCHRONIZELIST_FCN)GetProcAddress(hMod, "msSynchronizeList");
|
|
msRemove = (SYNCHRONIZE_FCN)GetProcAddress(hMod, "msRemove");
|
|
msMonitor = (SYNCHRONIZE_FCN)GetProcAddress(hMod, "msMonitor");
|
|
msDropDatabase = (SYNCHRONIZE_FCN)GetProcAddress(hMod, "msDropDatabase");
|
|
msUploadRunnerDB = (SYNCHRONIZE_FCN)GetProcAddress(hMod, "msUploadRunnerDB");
|
|
|
|
msGetErrorState = (ERRORMESG_FCN)GetProcAddress(hMod, "msGetErrorState");
|
|
msResetConnection = (OPENDB_FCN)GetProcAddress(hMod, "msResetConnection");
|
|
msReConnect = (OPENDB_FCN)GetProcAddress(hMod, "msReConnect");
|
|
|
|
if (!msOpenDatabase || !msSynchronizeUpdate || !msSynchronizeRead
|
|
|| !msConnectToServer || !msReConnect || !msGetErrorState
|
|
|| !msMonitor || !msDropDatabase || !msUploadRunnerDB) {
|
|
MessageBox(NULL, "Cannot load MySQL library.", NULL, MB_OK);
|
|
// handle the error
|
|
FreeLibrary(hMod);
|
|
hMod=0;
|
|
return false;//SOME_ERROR_CODE;
|
|
}
|
|
#else/*
|
|
msOpenDatabase = (SYNCHRONIZE_FCN)::msOpenDatabase;
|
|
msConnectToServer = (SYNCHRONIZE_FCN)::msConnectToServer;
|
|
msSynchronizeUpdate = (SYNCHRONIZE_FCN)::msSynchronizeUpdate;
|
|
msSynchronizeRead = (SYNCHRONIZE_FCN)::msSynchronizeRead;
|
|
msSynchronizeList = (SYNCHRONIZELIST_FCN)::msSynchronizeList;
|
|
msRemove = (SYNCHRONIZE_FCN)::msRemove;
|
|
msMonitor = (SYNCHRONIZE_FCN)::msMonitor;
|
|
msDropDatabase = (SYNCHRONIZE_FCN)::msDropDatabase;
|
|
msUploadRunnerDB = (SYNCHRONIZE_FCN)::msUploadRunnerDB;
|
|
msGetErrorState = (ERRORMESG_FCN)::msGetErrorState;
|
|
msResetConnection = (OPENDB_FCN)::msResetConnection;
|
|
msReConnect = (OPENDB_FCN)::msReConnect;*/
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
void oEvent::startReconnectDaemon()
|
|
{
|
|
if (isThreadReconnecting())
|
|
return;
|
|
|
|
char bf[256];
|
|
msGetErrorState(bf);
|
|
|
|
MySQLReconnect msqlr(lang.tl("warning:dbproblem#" + string(bf)));
|
|
msqlr.interval=5;
|
|
HasDBConnection = false;
|
|
HasPendingDBConnection = true;
|
|
tabAutoAddMachinge(msqlr);
|
|
|
|
gdibase.setDBErrorState(false);
|
|
gdibase.setWindowTitle(oe->getTitleName());
|
|
if (!isReadOnly()) {
|
|
// Do not show in kiosk-mode
|
|
gdibase.alert("warning:dbproblem#" + string(bf));
|
|
}
|
|
}
|
|
|
|
bool oEvent::msSynchronize(oBase *ob)
|
|
{
|
|
//if (!msSynchronizeRead) return true;
|
|
if (!HasDBConnection && !HasPendingDBConnection)
|
|
return true;
|
|
|
|
int ret = msSynchronizeRead(ob);
|
|
|
|
char err[256];
|
|
if (msGetErrorState(err))
|
|
gdibase.addInfoBox("sqlerror", err, 15000);
|
|
|
|
if (ret==0) {
|
|
verifyConnection();
|
|
return false;
|
|
}
|
|
|
|
if (typeid(*ob)==typeid(oTeam)) {
|
|
static_cast<pTeam>(ob)->apply(false, 0, false);
|
|
}
|
|
|
|
if (ret==1) {
|
|
gdibase.RemoveFirstInfoBox("sqlwarning");
|
|
gdibase.addInfoBox("sqlwarning", "Varning: ändringar i X blev överskrivna#" + ob->getInfo(), 5000);
|
|
}
|
|
return ret!=0;
|
|
}
|
|
|
|
void oEvent::resetChangeStatus(bool onlyChangable)
|
|
{
|
|
if (HasDBConnection) {
|
|
if (!onlyChangable) {
|
|
// The object here has no dependeces that makes it necessary to set/rest
|
|
|
|
for (list<oFreePunch>::iterator it=oe->punches.begin();
|
|
it!=oe->punches.end(); ++it)
|
|
it->resetChangeStatus();
|
|
|
|
//synchronize changed objects
|
|
for (list<oCard>::iterator it=oe->Cards.begin();
|
|
it!=oe->Cards.end(); ++it)
|
|
it->resetChangeStatus();
|
|
|
|
for (list<oClub>::iterator it=oe->Clubs.begin();
|
|
it!=oe->Clubs.end(); ++it)
|
|
it->resetChangeStatus();
|
|
|
|
for (list<oControl>::iterator it=oe->Controls.begin();
|
|
it!=oe->Controls.end(); ++it)
|
|
it->resetChangeStatus();
|
|
|
|
for (list<oCourse>::iterator it=oe->Courses.begin();
|
|
it!=oe->Courses.end(); ++it)
|
|
it->resetChangeStatus();
|
|
|
|
for (list<oClass>::iterator it=oe->Classes.begin();
|
|
it!=oe->Classes.end(); ++it)
|
|
it->resetChangeStatus();
|
|
}
|
|
|
|
for (list<oRunner>::iterator it=oe->Runners.begin();
|
|
it!=oe->Runners.end(); ++it)
|
|
it->resetChangeStatus();
|
|
|
|
for (list<oTeam>::iterator it=oe->Teams.begin();
|
|
it!=oe->Teams.end(); ++it)
|
|
it->resetChangeStatus();
|
|
}
|
|
}
|
|
|
|
void oEvent::storeChangeStatus(bool onlyChangable)
|
|
{
|
|
if (HasDBConnection) {
|
|
if (!onlyChangable) {
|
|
// The object here has no dependeces that makes it necessary to set/rest
|
|
for (list<oFreePunch>::iterator it=oe->punches.begin();
|
|
it!=oe->punches.end(); ++it)
|
|
it->storeChangeStatus();
|
|
|
|
for (list<oCard>::iterator it=oe->Cards.begin();
|
|
it!=oe->Cards.end(); ++it)
|
|
it->storeChangeStatus();
|
|
|
|
for (list<oClub>::iterator it=oe->Clubs.begin();
|
|
it!=oe->Clubs.end(); ++it)
|
|
it->storeChangeStatus();
|
|
|
|
for (list<oControl>::iterator it=oe->Controls.begin();
|
|
it!=oe->Controls.end(); ++it)
|
|
it->storeChangeStatus();
|
|
|
|
for (list<oCourse>::iterator it=oe->Courses.begin();
|
|
it!=oe->Courses.end(); ++it)
|
|
it->storeChangeStatus();
|
|
|
|
for (list<oClass>::iterator it=oe->Classes.begin();
|
|
it!=oe->Classes.end(); ++it)
|
|
it->storeChangeStatus();
|
|
}
|
|
|
|
for (list<oRunner>::iterator it=oe->Runners.begin();
|
|
it!=oe->Runners.end(); ++it)
|
|
it->storeChangeStatus();
|
|
|
|
for (list<oTeam>::iterator it=oe->Teams.begin();
|
|
it!=oe->Teams.end(); ++it)
|
|
it->storeChangeStatus();
|
|
}
|
|
}
|
|
|
|
|
|
bool oEvent::synchronizeList(oListId id, bool preSyncEvent, bool postSyncEvent) {
|
|
if (!HasDBConnection)
|
|
return true;
|
|
|
|
if (preSyncEvent) {
|
|
msSynchronize(this);
|
|
resetSQLChanged(true, false);
|
|
}
|
|
|
|
if ( !msSynchronizeList(this, id) ) {
|
|
verifyConnection();
|
|
return false;
|
|
}
|
|
|
|
if (id == oLPunchId)
|
|
advanceInformationPunches.clear();
|
|
|
|
if (postSyncEvent) {
|
|
reEvaluateChanged();
|
|
resetChangeStatus();
|
|
return true;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool oEvent::needReEvaluate() {
|
|
return sqlChangedRunners |
|
|
sqlChangedClasses |
|
|
sqlChangedCourses |
|
|
sqlChangedControls |
|
|
sqlChangedCards |
|
|
sqlChangedTeams;
|
|
}
|
|
|
|
void oEvent::resetSQLChanged(bool resetAllTeamsRunners, bool cleanClasses) {
|
|
sqlChangedRunners = false;
|
|
sqlChangedClasses = false;
|
|
sqlChangedCourses = false;
|
|
sqlChangedControls = false;
|
|
sqlChangedClubs = false;
|
|
sqlChangedCards = false;
|
|
sqlChangedPunches = false;
|
|
sqlChangedTeams = false;
|
|
|
|
if (resetAllTeamsRunners) {
|
|
for (list<oRunner>::iterator it=oe->Runners.begin();
|
|
it!=oe->Runners.end(); ++it) {
|
|
it->storeChangeStatus();
|
|
it->sqlChanged = false;
|
|
}
|
|
for (list<oTeam>::iterator it=oe->Teams.begin();
|
|
it!=oe->Teams.end(); ++it) {
|
|
it->storeChangeStatus();
|
|
it->sqlChanged = false;
|
|
}
|
|
}
|
|
if (cleanClasses) {
|
|
// This data is used to redraw lists/speaker etc.
|
|
for (list<oClass>::iterator it=oe->Classes.begin();
|
|
it!=oe->Classes.end(); ++it) {
|
|
it->storeChangeStatus();
|
|
it->sqlChangedControlLeg.clear();
|
|
it->sqlChangedLegControl.clear();
|
|
}
|
|
globalModification = false;
|
|
}
|
|
}
|
|
|
|
bool BaseIsRemoved(const oBase &ob){return ob.isRemoved();}
|
|
|
|
//Returns true if data is changed.
|
|
bool oEvent::autoSynchronizeLists(bool SyncPunches)
|
|
{
|
|
if (!HasDBConnection)
|
|
return false;
|
|
|
|
bool changed=false;
|
|
string ot;
|
|
|
|
int mask = getListMask(*this);
|
|
if (mask == 0)
|
|
return false;
|
|
|
|
// Reset change data and store update status on objects
|
|
// (which might be incorrectly changed during sql update)
|
|
resetSQLChanged(true, false);
|
|
|
|
//Synchronize ourself
|
|
if (mask & oLEventId) {
|
|
ot=sqlUpdated;
|
|
msSynchronize(this);
|
|
if (sqlUpdated!=ot) {
|
|
changed=true;
|
|
gdibase.setWindowTitle(getTitleName());
|
|
}
|
|
}
|
|
|
|
//Controls
|
|
if (mask & oLControlId) {
|
|
int oc = sqlCounterControls;
|
|
ot = sqlUpdateControls;
|
|
synchronizeList(oLControlId, false, false);
|
|
changed |= oc!=sqlCounterControls;
|
|
changed |= ot!=sqlUpdateControls;
|
|
}
|
|
|
|
//Courses
|
|
if (mask & oLCourseId) {
|
|
int oc = sqlCounterCourses;
|
|
ot = sqlUpdateCourses;
|
|
synchronizeList(oLCourseId, false, false);
|
|
changed |= oc!=sqlCounterCourses;
|
|
changed |= ot!=sqlUpdateCourses;
|
|
}
|
|
|
|
//Classes
|
|
if (mask & oLClassId) {
|
|
int oc = sqlCounterClasses;
|
|
ot = sqlUpdateClasses;
|
|
synchronizeList(oLClassId, false, false);
|
|
changed |= oc!=sqlCounterClasses;
|
|
changed |= ot!=sqlUpdateClasses;
|
|
}
|
|
|
|
//Clubs
|
|
if (mask & oLClubId) {
|
|
int oc = sqlCounterClubs;
|
|
ot = sqlUpdateClubs;
|
|
synchronizeList(oLClubId, false, false);
|
|
changed |= oc!=sqlCounterClubs;
|
|
changed |= ot!=sqlUpdateClubs;
|
|
}
|
|
|
|
//Cards
|
|
if (mask & oLCardId) {
|
|
int oc = sqlCounterCards;
|
|
ot = sqlUpdateCards;
|
|
synchronizeList(oLCardId, false, false);
|
|
changed |= oc!=sqlCounterCards;
|
|
changed |= ot!=sqlUpdateCards;
|
|
}
|
|
|
|
//Runners
|
|
if (mask & oLRunnerId) {
|
|
int oc = sqlCounterRunners;
|
|
ot = sqlUpdateRunners;
|
|
synchronizeList(oLRunnerId, false, false);
|
|
changed |= oc!=sqlCounterRunners;
|
|
changed |= ot!=sqlUpdateRunners;
|
|
}
|
|
|
|
//Teams
|
|
if (mask & oLTeamId) {
|
|
int oc = sqlCounterTeams;
|
|
ot = sqlUpdateTeams;
|
|
synchronizeList(oLTeamId, false, false);
|
|
changed |= oc!=sqlCounterTeams;
|
|
changed |= ot!=sqlUpdateTeams;
|
|
}
|
|
|
|
if (SyncPunches && (mask & oLPunchId)) {
|
|
//Punches
|
|
int oc = sqlCounterPunches;
|
|
ot = sqlUpdatePunches;
|
|
synchronizeList(oLPunchId, false, false);
|
|
changed |= oc!=sqlCounterPunches;
|
|
changed |= ot!=sqlUpdatePunches;
|
|
}
|
|
|
|
if (changed) {
|
|
if (needReEvaluate())
|
|
reEvaluateChanged();
|
|
|
|
reCalculateLeaderTimes(0);
|
|
//Restore changed staus on object that might have been changed
|
|
//during sql update, due to partial updates
|
|
resetChangeStatus();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
bool oEvent::connectToMySQL(const string &server, const string &user, const string &pwd, int port)
|
|
{
|
|
if (isThreadReconnecting())
|
|
return false;
|
|
|
|
if (!connectToServer())
|
|
return false;
|
|
|
|
MySQLServer=server;
|
|
MySQLPassword=pwd;
|
|
MySQLPort=port;
|
|
MySQLUser=user;
|
|
|
|
//Delete non-server competitions.
|
|
list<CompetitionInfo> saved;
|
|
list<CompetitionInfo>::iterator it;
|
|
for (it=cinfo.begin(); it!=cinfo.end(); ++it) {
|
|
if (it->Server.empty())
|
|
saved.push_back(*it);
|
|
}
|
|
cinfo = saved;
|
|
|
|
if (!msConnectToServer(this)) {
|
|
char bf[256];
|
|
msGetErrorState(bf);
|
|
MessageBox(NULL, lang.tl(bf).c_str(), "Error", MB_OK);
|
|
return false;
|
|
}
|
|
|
|
for (it=cinfo.begin(); it!=cinfo.end(); ++it) {
|
|
if (it->Name.size() > 1 && it->Name[0] == '%')
|
|
it->Name = lang.tl(it->Name.substr(1));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool oEvent::uploadSynchronize()
|
|
{
|
|
if (isThreadReconnecting())
|
|
throw std::exception("Internt fel i anslutningen. Starta om MeOS");
|
|
string newId = makeValidFileName(CurrentNameId, true);
|
|
strcpy_s(CurrentNameId, newId.c_str());
|
|
|
|
for (list<CompetitionInfo>::iterator it = cinfo.begin(); it != cinfo.end(); ++it) {
|
|
if (it->FullPath == CurrentNameId && it->Server.length()>0) {
|
|
gdioutput::AskAnswer ans = gdibase.askCancel("ask:overwrite_server");
|
|
|
|
if (ans == gdioutput::AnswerCancel)
|
|
return false;
|
|
else if (ans == gdioutput::AnswerNo) {
|
|
int len = strlen(CurrentNameId);
|
|
char ex[10];
|
|
sprintf_s(ex, "_%05XZ", (GetTickCount()/97) & 0xFFFFF);
|
|
if (len > 0) {
|
|
if (len< 7 || CurrentNameId[len-1] != 'Z')
|
|
strcat_s(CurrentNameId, ex);
|
|
else
|
|
strcpy_s(CurrentNameId + len - 7, 64 - len + 7, ex);
|
|
}
|
|
}
|
|
else {
|
|
if (!gdibase.ask("ask:overwriteconfirm"))
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
HasDBConnection=false;
|
|
|
|
#ifdef BUILD_DB_DLL
|
|
if (!msSynchronizeUpdate)
|
|
throw std::exception("Internt fel. Starta om MeOS");
|
|
#endif
|
|
|
|
if ( !msOpenDatabase(this) ){
|
|
char bf[256];
|
|
msGetErrorState(bf);
|
|
string error = string("Kunde inte öppna databasen (X).#") + bf;
|
|
throw std::exception(error.c_str());
|
|
}
|
|
|
|
if ( !msSynchronizeUpdate(this) ) {
|
|
char bf[256];
|
|
msGetErrorState(bf);
|
|
string error = string("Kunde inte ladda upp tävlingen (X).#") + bf;
|
|
throw std::exception(error.c_str());
|
|
}
|
|
|
|
OpFailStatus stat = (OpFailStatus)msUploadRunnerDB(this);
|
|
|
|
if (stat == opStatusFail) {
|
|
char bf[256];
|
|
msGetErrorState(bf);
|
|
string error = string("Kunde inte ladda upp löpardatabasen (X).#") + bf;
|
|
throw meosException(error);
|
|
}
|
|
else if (stat == opStatusWarning) {
|
|
char bf[256];
|
|
msGetErrorState(bf);
|
|
gdibase.addInfoBox("", string("Kunde inte ladda upp löpardatabasen (X).#") + bf, 5000);
|
|
}
|
|
|
|
HasDBConnection=true;
|
|
|
|
// Save local version of database
|
|
saveRunnerDatabase(CurrentNameId, false);
|
|
|
|
return true;
|
|
}
|
|
|
|
//Load a (new) competition from the server.
|
|
bool oEvent::readSynchronize(const CompetitionInfo &ci)
|
|
{
|
|
if (ci.Id<=0)
|
|
throw std::exception("help:12290");
|
|
|
|
if (isThreadReconnecting())
|
|
return false;
|
|
|
|
HasDBConnection=false;
|
|
|
|
#ifdef BUILD_DB_DLL
|
|
if (!msConnectToServer)
|
|
return false;
|
|
#endif
|
|
|
|
MySQLServer=ci.Server;
|
|
MySQLPassword=ci.ServerPassword;
|
|
MySQLPort=ci.ServerPort;
|
|
MySQLUser=ci.ServerUser;
|
|
|
|
//Delete non-server competitions.
|
|
list<CompetitionInfo> saved;
|
|
list<CompetitionInfo>::iterator it;
|
|
for (it=cinfo.begin(); it!=cinfo.end(); ++it) {
|
|
if (it->Server.empty())
|
|
saved.push_back(*it);
|
|
}
|
|
cinfo=saved;
|
|
|
|
if (!msConnectToServer(this)) {
|
|
char bf[256];
|
|
msGetErrorState(bf);
|
|
throw std::exception(bf);
|
|
return false;
|
|
}
|
|
|
|
for (it=cinfo.begin(); it!=cinfo.end(); ++it) {
|
|
if (it->Name.size() > 1 && it->Name[0] == '%')
|
|
it->Name = lang.tl(it->Name.substr(1));
|
|
}
|
|
|
|
newCompetition("");
|
|
Id=ci.Id;
|
|
strcpy_s(CurrentNameId, ci.FullPath.c_str());
|
|
|
|
char file[260];
|
|
sprintf_s(file, "%s.dbmeos", CurrentNameId);
|
|
getUserFile(CurrentFile, file);
|
|
if ( !msOpenDatabase(this) ) {
|
|
char bf[256];
|
|
msGetErrorState(bf);
|
|
throw std::exception(bf);
|
|
}
|
|
|
|
updateFreeId();
|
|
HasDBConnection=false;
|
|
|
|
openRunnerDatabase(CurrentNameId);
|
|
|
|
int ret = msSynchronizeRead(this);
|
|
if (ret == 0) {
|
|
char bf[256];
|
|
msGetErrorState(bf);
|
|
|
|
string err = string("Kunde inte öppna tävlingen (X)#") + bf;
|
|
throw std::exception(err.c_str());
|
|
}
|
|
else if (ret == 1) {
|
|
// Warning
|
|
char bf[256];
|
|
msGetErrorState(bf);
|
|
string info = "Databasvarning: X#" + lang.tl(bf);
|
|
gdibase.addInfoBox("sqlerror", info, 15000);
|
|
}
|
|
|
|
// Cache database locally
|
|
saveRunnerDatabase(CurrentNameId, false);
|
|
|
|
HasDBConnection=true;
|
|
|
|
// Setup multirunner links
|
|
for (oRunnerList::iterator it = Runners.begin(); it != Runners.end(); ++it)
|
|
it->createMultiRunner(false,false);
|
|
|
|
// Remove incorrect references
|
|
for (oRunnerList::iterator it = Runners.begin(); it != Runners.end(); ++it) {
|
|
if (it->multiRunner.size() > 0 ) {
|
|
vector<pRunner> &pr = it->multiRunner;
|
|
for (size_t k=0; k<pr.size(); k++) {
|
|
if (pr[k]==0 || pr[k]->tParentRunner != &*it) {
|
|
it->multiRunner.clear();
|
|
it->updateChanged();
|
|
it->synchronize();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check duplicates
|
|
vector<bool> usedInTeam(qFreeRunnerId+1);
|
|
bool teamCorrected = false;
|
|
|
|
for (oTeamList::iterator it = Teams.begin(); it != Teams.end(); ++it) {
|
|
if (it->correctionNeeded) {
|
|
it->updateChanged();
|
|
teamCorrected = true;
|
|
}
|
|
|
|
for (size_t i = 0; i < it->Runners.size(); i++) {
|
|
pRunner r = it->Runners[i];
|
|
|
|
if (r != 0) {
|
|
int expectedIndex = -1;
|
|
if (it->Class)
|
|
expectedIndex = it->Class->getLegRunnerIndex(i);
|
|
|
|
if (expectedIndex>=0 && expectedIndex != r->getMultiIndex()) {
|
|
int baseLeg = it->Class->getLegRunner(i);
|
|
it->setRunner(baseLeg, r->getMultiRunner(0), true);
|
|
teamCorrected = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (size_t i = 0; i < it->Runners.size(); i++) {
|
|
pRunner r = it->Runners[i];
|
|
if (r != 0) {
|
|
if (usedInTeam[r->Id]) {
|
|
it->Runners[i] = 0; // Reset duplicate runners
|
|
it->updateChanged();
|
|
teamCorrected = true;
|
|
if (r->tInTeam == &*it)
|
|
r->tInTeam = 0;
|
|
}
|
|
else
|
|
usedInTeam[r->Id] = true;
|
|
}
|
|
}
|
|
}
|
|
usedInTeam.clear();
|
|
|
|
if (teamCorrected) {
|
|
for (oTeamList::iterator it = Teams.begin(); it != Teams.end(); ++it) {
|
|
it->apply(true, 0, false);
|
|
}
|
|
}
|
|
|
|
reEvaluateAll(set<int>(), false);
|
|
vector<string> out;
|
|
checkChanged(out);
|
|
assert(out.empty());
|
|
|
|
for (oRunnerList::iterator it = Runners.begin(); it != Runners.end(); ++it) {
|
|
if (it->correctionNeeded) {
|
|
it->createMultiRunner(true, true);
|
|
if (it->tInTeam && it->Class) {
|
|
pTeam t = it->tInTeam;
|
|
int nr = min(int(it->Class->getNumStages())-1, int(it->multiRunner.size()));
|
|
t->setRunner(0, &*it, true);
|
|
for (int k=0; k<nr; k++)
|
|
t->setRunner(k+1, it->multiRunner[k], true);
|
|
t->updateChanged();
|
|
t->synchronize();
|
|
}
|
|
it->synchronizeAll();
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool oEvent::reConnect(char *errorMsg256)
|
|
{
|
|
if (HasDBConnection)
|
|
return true;
|
|
|
|
if (isThreadReconnecting()){
|
|
strcpy_s(errorMsg256, 256, "Synkroniseringsfel.");
|
|
return false;
|
|
}
|
|
|
|
#ifdef BUILD_DB_DLL
|
|
if (!msReConnect) {
|
|
strcpy_s(errorMsg256, 256, "Inte ansluten mot meosdb.dll");
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
if (msReConnect()) {
|
|
HasDBConnection = true;
|
|
HasPendingDBConnection = false;
|
|
//synchronize changed objects
|
|
for (list<oCard>::iterator it=oe->Cards.begin();
|
|
it!=oe->Cards.end(); ++it)
|
|
if (it->isChanged())
|
|
it->synchronize(false);
|
|
|
|
for (list<oClub>::iterator it=oe->Clubs.begin();
|
|
it!=oe->Clubs.end(); ++it)
|
|
if (it->isChanged())
|
|
it->synchronize(false);
|
|
|
|
for (list<oControl>::iterator it=oe->Controls.begin();
|
|
it!=oe->Controls.end(); ++it)
|
|
if (it->isChanged())
|
|
it->synchronize(false);
|
|
|
|
for (list<oCourse>::iterator it=oe->Courses.begin();
|
|
it!=oe->Courses.end(); ++it)
|
|
if (it->isChanged())
|
|
it->synchronize(false);
|
|
|
|
for (list<oClass>::iterator it=oe->Classes.begin();
|
|
it!=oe->Classes.end(); ++it)
|
|
if (it->isChanged())
|
|
it->synchronize(false);
|
|
|
|
for (list<oRunner>::iterator it=oe->Runners.begin();
|
|
it!=oe->Runners.end(); ++it)
|
|
if (it->isChanged())
|
|
it->synchronize(false);
|
|
|
|
for (list<oTeam>::iterator it=oe->Teams.begin();
|
|
it!=oe->Teams.end(); ++it)
|
|
if (it->isChanged())
|
|
it->synchronize(false);
|
|
|
|
for (list<oFreePunch>::iterator it=oe->punches.begin();
|
|
it!=oe->punches.end(); ++it)
|
|
if (it->isChanged())
|
|
it->synchronize(false);
|
|
|
|
autoSynchronizeLists(true);
|
|
|
|
return true;
|
|
}
|
|
|
|
msGetErrorState(errorMsg256);
|
|
return false;
|
|
}
|
|
|
|
//Returns number of changed, non-saved elements.
|
|
int oEvent::checkChanged(vector<string> &out) const
|
|
{
|
|
int changed=0;
|
|
char bf[256];
|
|
out.clear();
|
|
|
|
for (list<oCard>::iterator it=oe->Cards.begin();
|
|
it!=oe->Cards.end(); ++it)
|
|
if (it->isChanged()) {
|
|
changed++;
|
|
sprintf_s(bf, "Card %d", it->cardNo);
|
|
out.push_back(bf);
|
|
it->synchronize();
|
|
}
|
|
|
|
for (list<oClub>::iterator it=oe->Clubs.begin();
|
|
it!=oe->Clubs.end(); ++it)
|
|
if (it->isChanged()) {
|
|
changed++;
|
|
sprintf_s(bf, "Club %s", it->name.c_str());
|
|
out.push_back(bf);
|
|
it->synchronize();
|
|
}
|
|
|
|
for (list<oControl>::iterator it=oe->Controls.begin();
|
|
it!=oe->Controls.end(); ++it)
|
|
if (it->isChanged()) {
|
|
changed++;
|
|
sprintf_s(bf, "Control %d", it->Numbers[0]);
|
|
out.push_back(bf);
|
|
it->synchronize();
|
|
}
|
|
|
|
for (list<oCourse>::iterator it=oe->Courses.begin();
|
|
it!=oe->Courses.end(); ++it)
|
|
if (it->isChanged()) {
|
|
changed++;
|
|
sprintf_s(bf, "Course %s", it->Name.c_str());
|
|
out.push_back(bf);
|
|
it->synchronize();
|
|
}
|
|
|
|
for (list<oClass>::iterator it=oe->Classes.begin();
|
|
it!=oe->Classes.end(); ++it)
|
|
if (it->isChanged()) {
|
|
changed++;
|
|
sprintf_s(bf, "Class %s", it->Name.c_str());
|
|
out.push_back(bf);
|
|
it->synchronize();
|
|
}
|
|
|
|
for (list<oRunner>::iterator it=oe->Runners.begin();
|
|
it!=oe->Runners.end(); ++it)
|
|
if (it->isChanged()) {
|
|
changed++;
|
|
sprintf_s(bf, "Runner %s", it->getName().c_str());
|
|
out.push_back(bf);
|
|
it->synchronize();
|
|
}
|
|
for (list<oTeam>::iterator it=oe->Teams.begin();
|
|
it!=oe->Teams.end(); ++it)
|
|
if (it->isChanged()) {
|
|
changed++;
|
|
sprintf_s(bf, "Team %s", it->getName().c_str());
|
|
out.push_back(bf);
|
|
it->synchronize();
|
|
}
|
|
for (list<oFreePunch>::iterator it=oe->punches.begin();
|
|
it!=oe->punches.end(); ++it)
|
|
if (it->isChanged()) {
|
|
changed++;
|
|
sprintf_s(bf, "Punch SI=%d, %d", it->CardNo, it->Type);
|
|
out.push_back(bf);
|
|
it->synchronize();
|
|
}
|
|
|
|
return changed;
|
|
}
|
|
|
|
bool oEvent::verifyConnection()
|
|
{
|
|
if (!HasDBConnection)
|
|
return false;
|
|
|
|
if (isThreadReconnecting())
|
|
return false;
|
|
|
|
#ifdef BUILD_DB_DLL
|
|
if (!msMonitor)
|
|
return false;
|
|
#endif
|
|
|
|
if (!msMonitor(this)) {
|
|
startReconnectDaemon();
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
const string &oEvent::getServerName() const
|
|
{
|
|
return serverName;
|
|
}
|
|
|
|
void oEvent::closeDBConnection()
|
|
{
|
|
bool hadDB=HasDBConnection;
|
|
|
|
if (isThreadReconnecting()) {
|
|
//Don't know what to do?!
|
|
}
|
|
gdibase.setWaitCursor(true);
|
|
if (HasDBConnection) {
|
|
autoSynchronizeLists(true);
|
|
}
|
|
HasDBConnection=false;
|
|
|
|
#ifdef BUILD_DB_DLL
|
|
if (msResetConnection)
|
|
msResetConnection();
|
|
#else
|
|
msResetConnection();
|
|
#endif
|
|
Id=0;
|
|
|
|
if (!oe->empty() && hadDB) {
|
|
save();
|
|
Name+=" (Lokal kopia från: " + serverName + ")";
|
|
char cn[260];
|
|
sprintf_s(cn, "%s.%s.meos", CurrentNameId, serverName.c_str());
|
|
getUserFile(CurrentFile, cn);
|
|
serverName.clear();
|
|
save();
|
|
gdibase.setWindowTitle(Name);
|
|
}
|
|
else serverName.clear();
|
|
|
|
gdibase.setWaitCursor(false);
|
|
}
|
|
|
|
void oEvent::listConnectedClients(gdioutput &gdi)
|
|
{
|
|
gdi.addString("", 1, "Anslutna klienter:");
|
|
char bf[256];
|
|
gdi.fillRight();
|
|
gdi.pushX();
|
|
int x=gdi.getCX();
|
|
for (size_t k=0;k<connectedClients.size();k++) {
|
|
sprintf_s(bf, "%d.", k+1);
|
|
gdi.addStringUT(0, bf);
|
|
gdi.addStringUT(gdi.getCY(), x+30, 0, connectedClients[k]);
|
|
gdi.popX();
|
|
gdi.dropLine();
|
|
}
|
|
gdi.fillRight();
|
|
gdi.dropLine(2);
|
|
}
|
|
|
|
DWORD oEvent::clientCheckSum() const
|
|
{
|
|
DWORD cs=0;
|
|
for (size_t k=0;k<connectedClients.size();k++)
|
|
for (size_t i=0;i<connectedClients[k].length();i++)
|
|
cs += BYTE(connectedClients[k][i]) << ((i*5+k*7)%24);
|
|
|
|
return cs;
|
|
}
|
|
|
|
void oEvent::validateClients()
|
|
{
|
|
currentClientCS = clientCheckSum();
|
|
}
|
|
|
|
bool oEvent::hasClientChanged() const
|
|
{
|
|
return currentClientCS!=clientCheckSum();
|
|
}
|
|
|
|
void oEvent::dropDatabase()
|
|
{
|
|
bool dropped = false;
|
|
if (HasDBConnection) {
|
|
HasDBConnection=false;
|
|
|
|
dropped = msDropDatabase(this)!=0;
|
|
}
|
|
else throw std::exception("Inte ansluten");
|
|
|
|
if (!dropped) {
|
|
char bf[256];
|
|
msGetErrorState(bf);
|
|
if (strlen(bf)>0)
|
|
throw std::exception(bf);
|
|
|
|
throw std::exception("Operationen misslyckades. Orsak okänd.");
|
|
}
|
|
clear();
|
|
}
|
|
|