408 lines
No EOL
12 KiB
C++
408 lines
No EOL
12 KiB
C++
#include <windows.h>
|
|
#include "buffered_request.h"
|
|
#include <sstream>
|
|
#include <time.h>
|
|
|
|
#ifdef ONLINE_MODE
|
|
|
|
void logStr(const char *format, ...);
|
|
|
|
/**
|
|
* Records an argument line with non-timestamps events (objectId,propertyValue)
|
|
*/
|
|
class SimpleRequestBuffer : public IRequestBuffer
|
|
{
|
|
public:
|
|
bool addRequest(IRequestBuffer::ObjectId objectId, const char *propertyValue);
|
|
std::string getRequestLine(bool consume);
|
|
|
|
private:
|
|
std::string _ArgLine;
|
|
};
|
|
|
|
|
|
//**********************************************************
|
|
bool SimpleRequestBuffer::addRequest(IRequestBuffer::ObjectId objectId, const char *propertyValue)
|
|
{
|
|
// TODO: size limit
|
|
std::ostringstream s;
|
|
s << objectId << "," << propertyValue << ";";
|
|
_ArgLine += s.str();
|
|
return true;
|
|
}
|
|
|
|
//**********************************************************
|
|
std::string SimpleRequestBuffer::getRequestLine(bool consume)
|
|
{
|
|
std::string s = _ArgLine;
|
|
if (consume)
|
|
_ArgLine.clear();
|
|
return s;
|
|
}
|
|
|
|
#define DISABLED_TIMESTAMP ~0
|
|
|
|
/**
|
|
* Records an argument line with timestamps events (objectId only, propertyValue is ignored)
|
|
*/
|
|
class TimestampedProplessRequestBuffer : public IRequestBuffer
|
|
{
|
|
public:
|
|
bool addRequest(IRequestBuffer::ObjectId objectId, const char *propertyValue="");
|
|
std::string getRequestLine(bool consume);
|
|
|
|
private:
|
|
|
|
class ObjectStat
|
|
{
|
|
public:
|
|
ObjectStat() : /*_ObjectId(0),*/ _LastUseTimestamp(0), _FirstUseTimestamp(0), _UseCount(0) {}
|
|
bool addEvent();
|
|
std::string getStatStr() const;
|
|
std::string popStatStr();
|
|
private:
|
|
//IRequestBuffer::ObjectId _ObjectId;
|
|
int _UseCount;
|
|
time_t _FirstUseTimestamp;
|
|
time_t _LastUseTimestamp;
|
|
};
|
|
|
|
typedef std::map<IRequestBuffer::ObjectId, ObjectStat> ObjectStatMap;
|
|
ObjectStatMap _Stat;
|
|
};
|
|
|
|
//**********************************************************
|
|
bool TimestampedProplessRequestBuffer::ObjectStat::addEvent()
|
|
{
|
|
++_UseCount;
|
|
if (_FirstUseTimestamp == 0)
|
|
time(&_FirstUseTimestamp);
|
|
time(&_LastUseTimestamp);
|
|
return true;
|
|
}
|
|
|
|
//**********************************************************
|
|
std::string TimestampedProplessRequestBuffer::ObjectStat::getStatStr() const
|
|
{
|
|
if (_UseCount == 0)
|
|
return std::string();
|
|
|
|
std::ostringstream s;
|
|
time_t now;
|
|
s << _UseCount << ","
|
|
<< ((_FirstUseTimestamp != DISABLED_TIMESTAMP) ? (time(&now) - _FirstUseTimestamp) : -1) << ","// time diff from the first event
|
|
<< (time(&now) - _LastUseTimestamp); // time diff from the last event
|
|
return s.str();
|
|
}
|
|
|
|
//**********************************************************
|
|
std::string TimestampedProplessRequestBuffer::ObjectStat::popStatStr()
|
|
{
|
|
std::string s = getStatStr();
|
|
_UseCount = 0;
|
|
// _FirstUseTimestamp is for the whole session, disable in subsequent sendings
|
|
if (_FirstUseTimestamp != 0)
|
|
_FirstUseTimestamp = DISABLED_TIMESTAMP;
|
|
_LastUseTimestamp = 0;
|
|
return s;
|
|
}
|
|
|
|
//**********************************************************
|
|
bool TimestampedProplessRequestBuffer::addRequest(IRequestBuffer::ObjectId objectId, const char *propertyValue)
|
|
{
|
|
return _Stat[objectId].addEvent();
|
|
}
|
|
|
|
//**********************************************************
|
|
std::string TimestampedProplessRequestBuffer::getRequestLine(bool consume)
|
|
{
|
|
std::ostringstream s;
|
|
std::string statStr;
|
|
for (ObjectStatMap::iterator ist=_Stat.begin(); ist!=_Stat.end(); ++ist)
|
|
{
|
|
const IRequestBuffer::ObjectId& objectId = ist->first;
|
|
ObjectStat& stat = ist->second;
|
|
statStr = (consume ? stat.popStatStr() : stat.getStatStr());
|
|
if (!statStr.empty())
|
|
s << objectId << "," << statStr << ";";
|
|
}
|
|
return s.str();
|
|
}
|
|
|
|
|
|
/**
|
|
* Records the usage count, time and duration of objects.
|
|
* Usage: set propertyValue to "1" for On, "0" for Off.
|
|
*/
|
|
class ActiveTimeRequestBuffer : public IRequestBuffer
|
|
{
|
|
public:
|
|
bool addRequest(IRequestBuffer::ObjectId objectId, const char *propertyValue);
|
|
std::string getRequestLine(bool consume);
|
|
|
|
private:
|
|
|
|
class ObjectStat
|
|
{
|
|
public:
|
|
ObjectStat() : /*_ObjectId(0),*/ _LastBeginTimestamp(0), _LastBeginTimestampInterm(0), _ElapsedTime(0), _UseCount(0), _InUse(false), _Anomaly(false) {}
|
|
bool addEvent(const char *eventValue);
|
|
std::string getStatStr() const;
|
|
std::string popStatStr();
|
|
private:
|
|
//IRequestBuffer::ObjectId _ObjectId;
|
|
bool _InUse;
|
|
bool _Anomaly;
|
|
int _UseCount;
|
|
time_t _LastBeginTimestamp; // sent for DateUpd
|
|
time_t _LastBeginTimestampInterm; // used to calculate _ElapsedTime
|
|
time_t _ElapsedTime;
|
|
};
|
|
|
|
typedef std::map<IRequestBuffer::ObjectId, ObjectStat> ObjectStatMap;
|
|
ObjectStatMap _Stat;
|
|
};
|
|
|
|
//**********************************************************
|
|
bool ActiveTimeRequestBuffer::ObjectStat::addEvent(const char *eventValue)
|
|
{
|
|
if (strncmp(eventValue, "1", 2) == 0) // object enabled
|
|
{
|
|
if (_InUse)
|
|
return !(_Anomaly = true);
|
|
_InUse = true;
|
|
time(&_LastBeginTimestamp);
|
|
_LastBeginTimestampInterm = _LastBeginTimestamp;
|
|
}
|
|
else // object disabled
|
|
{
|
|
if (!_InUse)
|
|
return !(_Anomaly = true);
|
|
_InUse = false;
|
|
++_UseCount;
|
|
time_t now;
|
|
_ElapsedTime += time(&now) - _LastBeginTimestampInterm;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//**********************************************************
|
|
std::string ActiveTimeRequestBuffer::ObjectStat::getStatStr() const
|
|
{
|
|
if ((_UseCount == 0) && (_ElapsedTime == 0))
|
|
return std::string();
|
|
|
|
std::ostringstream s;
|
|
time_t now;
|
|
s << _UseCount << "," << _ElapsedTime << ","
|
|
<< (time(&now) - _LastBeginTimestamp) // time diff from the last begin event
|
|
<< (_InUse ? ",1" : ",0") << (_Anomaly ? ",1" : ",0");
|
|
return s.str();
|
|
}
|
|
|
|
//**********************************************************
|
|
std::string ActiveTimeRequestBuffer::ObjectStat::popStatStr()
|
|
{
|
|
if (_InUse)
|
|
{
|
|
// We will return the current state
|
|
++_UseCount; // count this partial use as one (we'll not count the remaining)
|
|
time_t now;
|
|
_ElapsedTime += time(&now) - _LastBeginTimestampInterm; // count partial elapsed time
|
|
}
|
|
std::string s = getStatStr();
|
|
if (_InUse)
|
|
{
|
|
// We reset the state so that the remaining of the current use will consistenly be taken into account
|
|
_UseCount = -1; // on end of use, or on next pop if during the same use, will return 0
|
|
time(&_LastBeginTimestampInterm);
|
|
}
|
|
else
|
|
{
|
|
_UseCount = 0;
|
|
}
|
|
_ElapsedTime = 0;
|
|
return s;
|
|
}
|
|
|
|
//**********************************************************
|
|
bool ActiveTimeRequestBuffer::addRequest(IRequestBuffer::ObjectId objectId, const char *propertyValue)
|
|
{
|
|
return _Stat[objectId].addEvent(propertyValue);
|
|
}
|
|
|
|
//**********************************************************
|
|
std::string ActiveTimeRequestBuffer::getRequestLine(bool consume)
|
|
{
|
|
std::ostringstream s;
|
|
std::string statStr;
|
|
for (ObjectStatMap::iterator ist=_Stat.begin(); ist!=_Stat.end(); ++ist)
|
|
{
|
|
const IRequestBuffer::ObjectId& objectId = ist->first;
|
|
ObjectStat& stat = ist->second;
|
|
statStr = (consume ? stat.popStatStr() : stat.getStatStr());
|
|
if (!statStr.empty())
|
|
s << objectId << "," << statStr << ";";
|
|
}
|
|
return s.str();
|
|
}
|
|
|
|
|
|
//**********************************************************
|
|
IRequestBuffer *IRequestBuffer::factory(const std::string& destServerScript)
|
|
{
|
|
if (destServerScript == "trk-text")
|
|
return new SimpleRequestBuffer();
|
|
else if (destServerScript == "trk-action")
|
|
return new TimestampedProplessRequestBuffer();
|
|
else
|
|
return new ActiveTimeRequestBuffer();
|
|
}
|
|
|
|
//**********************************************************
|
|
bool BufferedRequester::addRequest(const char *destServerScript, IRequestBuffer::ObjectId objectId, const char *propertyValue)
|
|
{
|
|
std::string destS = destServerScript;
|
|
IRequestBuffer* &rb = _BRMap[destS];
|
|
if (!rb)
|
|
rb = IRequestBuffer::factory(destS);
|
|
|
|
return rb && rb->addRequest(objectId, propertyValue);
|
|
}
|
|
|
|
//**********************************************************
|
|
std::string BufferedRequester::getLineToSend(bool consume)
|
|
{
|
|
std::string argLine, s;
|
|
for (BufferedRequesterMap::iterator im=_BRMap.begin(); im!=_BRMap.end(); ++im)
|
|
{
|
|
s = im->second->getRequestLine(consume);
|
|
if (!s.empty())
|
|
argLine += im->first + ":" + s + "|";
|
|
}
|
|
return argLine;
|
|
}
|
|
|
|
//**********************************************************
|
|
// Can be called from 1 thread only (actually the background one)
|
|
bool BufferedRequester::sendBatch(const std::string& lineToSend)
|
|
{
|
|
// TODO: if the request fails, try to send next time (but don't overstress the server...)
|
|
//logStr("Sending batch: %s", lineToSend.c_str());
|
|
|
|
static time_t dbPingS = 0; // not multithread safe
|
|
time_t now;
|
|
time(&now);
|
|
std::ostringstream args;
|
|
args << "batch=" << lineToSend << "&dbp=" << dbPingS;
|
|
if (!doRequest("batch-req", args.str().c_str(), USE_MORE_INFO, __FUNCTION__, "Could not send batch request"))
|
|
return false;
|
|
|
|
dbPingS = time(&dbPingS) - now; // not quite precise (in secs), only significant if >1 (says at least 1 sec)
|
|
const HttpPage& buffer = _HttpClient.getResponsePage();
|
|
const char *bufPos = buffer.c_str() + 2; // skip "0|"
|
|
std::string nextPeriodStr = getNextResultField(&bufPos);
|
|
if (!nextPeriodStr.empty())
|
|
_BufferedSendingPeriodS = atoi(nextPeriodStr.c_str());
|
|
return true;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
void BufferedRequester::clear()
|
|
{
|
|
for (BufferedRequesterMap::iterator im=_BRMap.begin(); im!=_BRMap.end(); ++im)
|
|
{
|
|
if (im->second)
|
|
delete im->second;
|
|
}
|
|
_BRMap.clear();
|
|
}
|
|
|
|
// ***************************************************************************
|
|
bool BckgndBufferedRequester::init(const SimpleString<16> *pIdAccount)
|
|
{
|
|
CBackgroundTask::init();
|
|
MUTEX_SETUP(_MutexToRequest);
|
|
_ExpediteNextSending = false;
|
|
_WantExit = false;
|
|
return CamCamHttpClient::init(pIdAccount);
|
|
}
|
|
|
|
//**********************************************************
|
|
BckgndBufferedRequester::~BckgndBufferedRequester()
|
|
{
|
|
MUTEX_CLEANUP(_MutexToRequest);
|
|
}
|
|
|
|
|
|
//**********************************************************
|
|
bool BckgndBufferedRequester::addRequest(const char *destServerScript, IRequestBuffer::ObjectId objectId, const char *propertyValue, bool expediteSending)
|
|
{
|
|
MUTEX_LOCK(_MutexToRequest);
|
|
BufferedRequester::addRequest(destServerScript, objectId, propertyValue);
|
|
_ExpediteNextSending = expediteSending;
|
|
MUTEX_UNLOCK(_MutexToRequest);
|
|
return true;
|
|
}
|
|
|
|
//**********************************************************
|
|
bool BckgndBufferedRequester::flushAndExit()
|
|
{
|
|
MUTEX_LOCK(_MutexToRequest);
|
|
_ExpediteNextSending = true;
|
|
_WantExit = true;
|
|
MUTEX_UNLOCK(_MutexToRequest);
|
|
return true;
|
|
}
|
|
|
|
//**********************************************************
|
|
// Called from background thread
|
|
void BckgndBufferedRequester::run()
|
|
{
|
|
time_t lastSendingTime;
|
|
time(&lastSendingTime);
|
|
std::string lineToSend;
|
|
while (true)
|
|
{
|
|
Sleep(500);
|
|
time_t now;
|
|
bool wantExit = _WantExit; // check flag from other thread, only here
|
|
|
|
// Is sending completely disabled/-1 (including on log-out)?
|
|
if (_BufferedSendingPeriodS == -1)
|
|
{
|
|
if (wantExit)
|
|
break;
|
|
continue;
|
|
}
|
|
|
|
// Is it time to send an update (except if disabled/0 or if there is nothing to send) or to flush (last update)?
|
|
time(&now);
|
|
if (_ExpediteNextSending ||
|
|
((_BufferedSendingPeriodS > 0) && (now > lastSendingTime + _BufferedSendingPeriodS)))
|
|
{
|
|
lastSendingTime = now;
|
|
|
|
// Get stat from all requests that have been added
|
|
MUTEX_LOCK(_MutexToRequest);
|
|
_ExpediteNextSending = false;
|
|
lineToSend = getLineToSend(true);
|
|
MUTEX_UNLOCK(_MutexToRequest);
|
|
|
|
// Send to server (can take some time)
|
|
sendBatch(lineToSend); // send even if (!lineToSend.empty()) to check dual-log-in case
|
|
|
|
if (wantExit) // not checking _WantExit as it could have changed during sending
|
|
break;
|
|
}
|
|
if (_HttpClient.isAborting())
|
|
break;
|
|
}
|
|
MUTEX_LOCK(_MutexToRequest);
|
|
clear(); // TODO: replace this by a global account release() on log-out
|
|
MUTEX_UNLOCK(_MutexToRequest);
|
|
_IsRunning = false;
|
|
_WantExit = false;
|
|
}
|
|
|
|
#endif |