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@1: rt300@4: #include "ofMain.h" rt300@4: #include "ofxiPhone.h" rt300@4: #include "2dvector.h" rt300@4: #include "ofxiPhoneExtras.h" rt300@4: #include rt300@4: #include rt300@4: #include rt300@22: #include rt300@4: #include rt300@4: #include "2dvector.h" rt300@8: #include "json.h" rt300@24: #include "testApp.h" rt300@32: #include "presetManager.h" rt300@29: #import "ServerComms.h" rt300@33: #include "grid.h" rt300@33: rt300@38: #define EVENT_THIN_FACTOR 12 rt300@25: #define EVENT_LOG_FILENAME "log.json" rt300@42: #define UPLOAD_CHUNK_SIZE 1000 rt300@33: #define APP_CREATION_TIME 381429000000 // milliseconds to the time i wrote this wee blighter. saves digits rt300@33: #define SCROLL_TRAIL_LENGTH 200 rt300@47: #define PROGRAM_VERSION 1.0 // NOW USES NEW HILBERT CURVE. ALL OLD PRESETS WILL BE INVALID :( rt300@29: rt300@33: #define SUPERVISED // this def will save files rt300@25: rt300@25: rt300@9: // can add but don't change ordering - this will invalidate logs rt300@25: enum leventType {SAVE_PRESET, // 0 rt300@25: SAVE_DESET, // 1 rt300@25: SCROLL, // 2 rt300@25: ZOOM, // 3 rt300@25: SCROLL_STOPPED, // 4 rt300@25: ZOOM_STOPPED, // 5 rt300@25: SNAPPED_TO_PRESET, // 6 rt300@25: CHANGE_SLIDER, // 7 rt300@25: SWAP_VIEW, // 8 rt300@25: SET_MIN_ZOOM, // 9 rt300@25: SET_MAX_ZOOM, // 10 rt300@25: RANDOMISE, // 11 rt300@25: APP_STARTED, // 12 rt300@25: APP_EXITED, // 13 rt300@25: CONSENT_DENIED, // 14 rt300@25: SEQ_LOCKED, // 15 rt300@25: SYNTH_LOCKED, // 16 rt300@25: PLAY_PRESSED, // 17 rt300@27: PAUSE_PRESSED, // 18 rt300@31: HELP_PRESSED, // 19 rt300@33: QUESTIONNAIRE_COMPLETED, // 20 rt300@33: EVALUATION_POINT, // 21 rt300@39: EMPTY_EVENT, // 22 rt300@39: SMOOTHING_ON, // 23 rt300@44: SMOOTHING_OFF, // 24 rt300@44: SNAPPED_TO_EVALPT, // 25 rt300@44: PRESET_DOUBLE_TAPPED}; rt300@1: rt300@8: //--------------------------------------------------------------------------- rt300@8: rt300@4: class lEvent{ rt300@5: public: rt300@1: // try and make this as compact as possible. rt300@4: leventType eventType; rt300@1: double val1; // x coord, scale if zoom rt300@1: double val2; // y coord, 0 if zoom rt300@25: int sliderID; // xtra int rt300@32: long long eventTime; // MILLISECONDS since app creation rt300@8: lEvent(leventType eType, double v1 = 0.0, double v2 = 0.0,int sID = 0){ rt300@5: eventType = eType; rt300@5: val1 = v1; rt300@5: val2 = v2; rt300@5: sliderID = sID; rt300@9: rt300@28: double timemsd = [NSDate timeIntervalSinceReferenceDate]; // MILLISECONDS rt300@25: eventTime = (unsigned long long)(timemsd*1000) - APP_CREATION_TIME; rt300@9: rt300@5: } rt300@25: rt300@8: lEvent(const Json::Value &jevt){ rt300@9: // constructor takes "jsonToEvent" readfile function role rt300@25: eventType = (leventType)jevt["eType"].asInt(); rt300@25: val1 = jevt["v1"].asDouble(); rt300@25: val2 = jevt["v2"].asDouble(); rt300@25: sliderID = jevt["sID"].asInt(); rt300@25: eventTime = jevt["eTime"].asLargestInt(); rt300@9: rt300@15: // TODO what happens if we try to read one that isn't there? rt300@15: rt300@8: } rt300@8: Json::Value eventToJson(){ rt300@8: Json::Value jevt; rt300@25: jevt["eType"] = eventType; rt300@25: // here: should give a certain number of sig figs? rt300@25: jevt["v1"] = val1; rt300@25: jevt["v2"] = val2; rt300@25: jevt["sID"] = sliderID; rt300@25: jevt["eTime"] = eventTime; rt300@8: return jevt; rt300@8: } rt300@33: void draw(); rt300@5: }; rt300@5: //--------------------------------------------------------------------------- rt300@8: // streams no longer used rt300@5: inline istream& operator>>(istream & is, lEvent& e){ rt300@5: is.setf(ios_base::fixed,ios_base::floatfield); rt300@5: is.precision(1); rt300@4: rt300@5: char delim; rt300@5: int eType; rt300@0: rt300@5: is >> eType >> delim >> e.val1 >> delim >> e.val2 >> delim >> e.sliderID; rt300@5: rt300@5: e.eventType = (leventType)eType; rt300@5: rt300@5: return is; rt300@5: } rt300@4: rt300@5: //--------------------------------------------------------------------------- rt300@5: inline ostream& operator<<(ostream & os, const lEvent& e){ rt300@5: os.setf(ios_base::fixed,ios_base::floatfield); rt300@5: os.precision(1); rt300@5: rt300@5: os << e.eventType << ',' << e.val1 << ',' << e.val2 << ',' << e.sliderID << '\n'; rt300@5: rt300@5: return os; rt300@7: } rt300@5: //--------------------------------------------------------------------------- rt300@32: rt300@29: rt300@0: class EventLogger{ rt300@0: public: rt300@25: int nextUploadNumber; rt300@14: bool loggingEnabled; rt300@31: rt300@27: bool logUploadInProgress; rt300@22: bool serverConnectionOK; rt300@25: bool consentGiven; rt300@29: bool questionnaireCompleted; rt300@29: bool questionnaireUploaded; rt300@31: rt300@29: unsigned int deviceID; rt300@25: unsigned int totalInteractionTime, savedInteractionTime, sessionTime, sessionStartTime; rt300@14: string userName; // not unique rt300@27: rt300@31: // constr rt300@14: EventLogger(); rt300@31: rt300@31: // public methods: rt300@42: void startLoadAll(); rt300@14: void exitAndSave(); rt300@14: void setUsername(const char *u); rt300@27: void newUser(); rt300@14: void logEvent(const leventType& evtType,const TwoVector& centre = TwoVector(), const double& scale = 1.0, const int& sliderID = -1, const double& sliderVal = 0.0); rt300@28: void questionnaireAnswersObtained(vector answers, const char* userComments); rt300@27: void urlResponse(ofHttpResponse & response); rt300@30: rt300@30: void questionnaireOK(); rt300@30: void eventlogOK(); rt300@30: void testConnectionOK(); rt300@30: void questionnaireNotOK(); rt300@30: void eventlogNotOK(); rt300@30: void testConnectionNotOK(); rt300@30: rt300@30: void printAll(); rt300@32: void saveSessionToFile(); rt300@33: void drawTrail(const TwoVector min, const TwoVector max); rt300@33: vector getDrawableEventsInRange(const TwoVector min, const TwoVector max); rt300@44: vector getEvaluationPointsInRange(const TwoVector min, const TwoVector max); rt300@33: vector getRecentPath(int numEvents); rt300@38: rt300@38: void clearTrail(); rt300@14: private: rt300@31: rt300@38: vector theEvents; // all logged but not uploaded events rt300@38: deque eventsToDraw; // 200 or so drawable events, maybe uploaded maybe not rt300@29: rt300@39: void thinnedSliderEvent(lEvent nextEvent); rt300@39: void thinnedZoomEvent(lEvent nextEvent); rt300@39: void thinnedSnapEvent(lEvent nextEvent); rt300@33: void thinnedScrollEvent(lEvent nextEvent); rt300@14: unsigned int nextUploadQty; rt300@14: rt300@28: string questionnaireComments; rt300@8: rt300@22: ofxiPhoneDeviceType iOSdeviceType; rt300@14: rt300@42: rt300@22: vector questionnaireAnswers; rt300@28: int interfaceOrder; rt300@9: rt300@31: // private methods rt300@42: void testConnection(); rt300@7: void checkLogFile(); rt300@29: void deleteLogs(); // new user rt300@27: bool uploadEventLog(bool async); rt300@7: void firstEverAppOpen(); rt300@42: void loadExistingLogFile(const string &jsonFile); rt300@27: void uploadQuestionnaire(); rt300@27: bool sendToServer(string functionName, Json::Value jsonData, bool async); rt300@30: rt300@43: rt300@8: Json::Value logsToJson(); rt300@22: Json::Value questionnaireToJson(); rt300@30: rt300@29: rt300@29: // rt300@29: ServerComms *serverComms; rt300@29: rt300@0: }; rt300@0: rt300@44: lEvent * e; rt300@44: rt300@8: //--------------------------------------------------------------------------- rt300@8: rt300@5: rt300@0: #endif /* defined(__oscSenderExample__eventLogger__) */