rt300@0: // rt300@0: // eventLogger.h rt300@0: // oscSenderExample rt300@0: // rt300@0: // Created by Robert Tubb on 05/11/2012. rt300@0: // rt300@0: // rt300@0: // This class handle everything to do with loggin user actions, rt300@0: // uploading logs to server, and storing locally if not uploaded rt300@0: rt300@0: #ifndef __oscSenderExample__eventLogger__ rt300@0: #define __oscSenderExample__eventLogger__ rt300@0: rt300@0: rt300@0: #include "ofMain.h" rt300@0: #include "ofxiPhone.h" rt300@0: #include "2dvector.h" rt300@0: #include "ofxiPhoneExtras.h" rt300@0: #include rt300@0: #include rt300@0: #include rt300@0: #include rt300@0: #include rt300@0: #include "2dvector.h" rt300@0: #include "json.h" rt300@0: #import "ServerComms.h" rt300@0: #include "boost/bind.hpp" rt300@0: #include "boost/function.hpp" rt300@0: #include "Poco/Mutex.h" rt300@32: rt300@0: #define EVENT_THIN_FACTOR 30 rt300@0: #define EVENT_LOG_FILENAME "log.json" rt300@0: #define UPLOAD_CHUNK_SIZE 1000 rt300@34: #define SAVE_CHUNK_SIZE 30000 // this is "too big" and must be saved, usually run end will save rt300@0: #define APP_CREATION_TIME 0 // ignore this, pointless rt300@18: #define PROGRAM_NAME "RIFTATHON" rt300@18: #define PROGRAM_VERSION 0.1 rt300@0: rt300@0: #define SUPERVISED // this def will save files rt300@0: rt300@0: rt300@0: // can add but don't change ordering - this will invalidate logs rt300@0: enum leventType {APP_LOADED, // 0 rt300@0: INTRO_CONSENTED, // 1 rt300@0: APP_EXITED, // 2 rt300@0: HELP_PRESSED, // 3 rt300@0: QUESTIONNAIRE_COMPLETED, // 4 rt300@0: rt300@0: NEW_TEST, // 5 rt300@0: // al the set up of the new test rt300@0: TARGET_PARAM_SET, // 6 setting up the target sound, param id and value rt300@0: CANDIDATE_PARAM_SET, // 7 setting up the cand sound starting pos, subset of param id and value rt300@0: CANDIDATE_CHANGEABLE_IDX, // 8 list of changeable indexes rt300@0: CANDIDATE_MAPPING_IDS, // 9 which kind of control was mapped to which param rt300@0: CONTROL_LIST, // 10 list of control types rt300@0: rt300@0: TEST_TIMER_STARTED, // 11 we're off!! rt300@0: rt300@0: CANDIDATE_PARAM_ADJUSTED, // 12 interactions changing params rt300@0: SUBMIT_PRESSED, // 13 this is the suer saying they've found it, along with the actual answer rt300@0: DISTANCE_TIME_SCORE, // 14 list of the results, now we should go back to NEW_TEST rt300@0: COUNTDOWN_INITIATED, // 15 just in case rt300@0: ALL_TESTS_COMPLETED, // 16 ? rt300@0: TARGET_PLAYED, // 17 however this happens it might be good to know when this happens rt300@0: CANDIDATE_PLAYED, // 18 rt300@9: START_THE_SEARCH_TESTS, // 19 probably superfluous rt300@0: CRAP_TEST, // eliminate these coords somehow ??? rt300@0: EMPTY_EVENT, rt300@0: SPEED_CHANGED, // 22 ms between sounds rt300@9: SAVE_PRESET, // 23 save a preset rt300@16: START_THE_TRAINING_TESTS, // 24 start traning rt300@32: START_THE_EXP_TESTS, // 25 start explore/express rt300@32: START_THE_PERFORMANCE_TESTS, // 26 starting perf rt300@32: rt300@32: // new stuff for TRaining stage rt300@43: START_NEW_RUN, // 27 rt300@43: TRAINING_RESULT, // 28 rt300@43: NEW_STEP, // 29 rt300@43: NEW_TARGET, // 30 rt300@43: NEW_START_POS, //31 rt300@43: FINISH_POS, //32 rt300@43: FINISHED_RUN, //33 rt300@43: CANDIDATE_PARAM_ADJUSTED_ALL, //34 rt300@43: TARGET_AND_MATCH, //35 rt300@43: FORGOT_SEQ, //36 rt300@45: RUN_SKIPPED, //37 rt300@45: START_THE_TRAINING_DEMO rt300@0: rt300@0: }; rt300@0: rt300@0: //--------------------------------------------------------------------------- rt300@0: rt300@0: class lEvent{ rt300@0: public: rt300@0: // try and make this as compact as possible. rt300@0: leventType eventType; rt300@0: double val1; // x coord, scale if zoom rt300@0: double val2; // y coord, 0 if zoom rt300@0: int sliderID; // xtra int rt300@0: long long eventTime; // MILLISECONDS since ref date rt300@0: vector eventData; // variable length data int rt300@0: rt300@0: // old sonic zoom style event rt300@0: lEvent(leventType eType, double v1 = 0.0, double v2 = 0.0,int sID = 0){ rt300@0: eventType = eType; rt300@0: val1 = v1; rt300@0: val2 = v2; rt300@0: sliderID = sID; rt300@0: rt300@0: double timemsd = [NSDate timeIntervalSinceReferenceDate]; // MILLISECONDS, but with fractional part ( rt300@0: eventTime = (unsigned long long)(timemsd*1000) - APP_CREATION_TIME; rt300@0: rt300@0: } rt300@0: rt300@0: // new tweakathlon event rt300@0: lEvent(leventType eType, vector theData){ rt300@0: eventType = eType; rt300@0: eventData = theData; rt300@0: rt300@0: double timemsd = [NSDate timeIntervalSinceReferenceDate]; // MILLISECONDS, but with fractional part ( rt300@0: eventTime = (unsigned long long)(timemsd*1000) - APP_CREATION_TIME; rt300@0: rt300@0: } rt300@0: rt300@0: lEvent(const Json::Value &jevt){ rt300@0: // constructor takes "jsonToEvent" readfile function role rt300@0: eventType = (leventType)jevt["eType"].asInt(); rt300@0: val1 = jevt["v1"].asDouble(); rt300@0: val2 = jevt["v2"].asDouble(); rt300@0: sliderID = jevt["sID"].asInt(); rt300@0: eventTime = jevt["eTime"].asLargestInt(); rt300@0: rt300@0: // TODO what happens if we try to read one that isn't there? rt300@0: rt300@0: } rt300@0: // new tweak event to json rt300@0: Json::Value eventToJson(){ rt300@0: Json::Value jevt; rt300@0: jevt["eType"] = eventType; rt300@0: // convert vector to json array rt300@0: for(auto it = eventData.begin(); it < eventData.end(); it++){ rt300@0: jevt["eData"].append(*it); rt300@0: } rt300@0: jevt["eTime"] = eventTime; rt300@0: return jevt; rt300@0: } rt300@0: Json::Value eventToJsonOld(){ rt300@0: Json::Value jevt; rt300@0: jevt["eType"] = eventType; rt300@0: // here: should give a certain number of sig figs? rt300@0: jevt["v1"] = val1; rt300@0: jevt["v2"] = val2; rt300@0: jevt["sID"] = sliderID; rt300@0: jevt["eTime"] = eventTime; rt300@0: return jevt; rt300@0: } rt300@0: }; rt300@0: rt300@0: rt300@0: //------------------------------------------ rt300@0: rt300@0: class EventLogger{ rt300@0: public: rt300@0: Poco::Mutex _mutex; rt300@0: rt300@0: int nextUploadNumber; rt300@0: int nextUploadQty; rt300@0: bool loggingEnabled; rt300@0: rt300@0: bool logUploadInProgress; rt300@0: bool serverConnectionOK; rt300@0: bool consentGiven; rt300@0: bool questionnaireCompleted; rt300@0: bool questionnaireUploaded; rt300@0: rt300@34: unsigned long long deviceID; rt300@34: unsigned long long totalInteractionTime, savedInteractionTime, sessionTime, sessionStartTime; rt300@0: string userName; // not unique rt300@0: string questionnaireComments; rt300@0: // constr rt300@0: EventLogger(); rt300@0: rt300@0: // public methods: rt300@18: void onUsernameEntered(); rt300@0: void startLoadAll(); rt300@0: void exitAndSave(); rt300@0: void setUsername(const char *u); rt300@0: string getUsername(){ rt300@0: return userName; rt300@0: } rt300@0: void newUser(); rt300@0: void logEvent(const leventType& evtType); rt300@0: void logEvent(const leventType& evtType, const vector eventData); rt300@0: rt300@0: void questionnaireAnswersObtained(vector answers, const char* userComments); rt300@0: void urlResponse(ofHttpResponse & response); rt300@0: rt300@0: void questionnaireOK(); rt300@0: void eventlogOK(); rt300@0: void testConnectionOK(); rt300@0: void questionnaireNotOK(); rt300@0: void eventlogNotOK(); rt300@0: void testConnectionNotOK(); rt300@0: rt300@0: void printAll(); rt300@0: void saveSessionToFile(); rt300@0: rt300@0: private: rt300@0: void thinnedSliderEvent(lEvent newEvent); rt300@0: vector theEvents; // all logged but not uploaded events rt300@0: rt300@0: vector questionnaireAnswers; rt300@0: rt300@18: string nextLogFileIndexString(); rt300@18: void loadUserDetailsFromLastLogFile(int numExistingLogs); rt300@0: // private methods rt300@0: void testConnection(); rt300@0: void checkLogFile(); rt300@0: void deleteLogs(); // new user rt300@0: bool uploadEventLog(bool async); rt300@0: void firstEverAppOpen(); rt300@0: void loadExistingLogFile(const string &jsonFile); rt300@0: void uploadQuestionnaire(); rt300@0: bool sendToServer(string functionName, Json::Value jsonData, bool async); rt300@0: rt300@0: Json::Value logsToJson(); rt300@0: Json::Value questionnaireToJson(); rt300@0: rt300@0: rt300@0: template bool matchID(T thing); rt300@0: bool matchID2(); rt300@0: // rt300@0: ServerComms *serverComms; rt300@18: int nextLogFileIndex; rt300@0: }; rt300@0: rt300@0: rt300@0: //--------------------------------------------------------------------------- rt300@0: rt300@0: rt300@0: #endif /* defined(__oscSenderExample__eventLogger__) */