rt300@0
|
1 //
|
rt300@0
|
2 // eventLogger.h
|
rt300@0
|
3 // oscSenderExample
|
rt300@0
|
4 //
|
rt300@0
|
5 // Created by Robert Tubb on 05/11/2012.
|
rt300@0
|
6 //
|
rt300@0
|
7 //
|
rt300@0
|
8 // This class handle everything to do with loggin user actions,
|
rt300@0
|
9 // uploading logs to server, and storing locally if not uploaded
|
rt300@0
|
10
|
rt300@0
|
11 #ifndef __oscSenderExample__eventLogger__
|
rt300@0
|
12 #define __oscSenderExample__eventLogger__
|
rt300@0
|
13
|
rt300@0
|
14
|
rt300@0
|
15 #include "ofMain.h"
|
rt300@0
|
16 #include "ofxiPhone.h"
|
rt300@0
|
17 #include "2dvector.h"
|
rt300@0
|
18 #include "ofxiPhoneExtras.h"
|
rt300@0
|
19 #include <sys/time.h>
|
rt300@0
|
20 #include <iostream>
|
rt300@0
|
21 #include <string>
|
rt300@0
|
22 #include <cstring>
|
rt300@0
|
23 #include <map>
|
rt300@0
|
24 #include "2dvector.h"
|
rt300@0
|
25 #include "json.h"
|
rt300@0
|
26 #import "ServerComms.h"
|
rt300@0
|
27 #include "boost/bind.hpp"
|
rt300@0
|
28 #include "boost/function.hpp"
|
rt300@0
|
29 #include "Poco/Mutex.h"
|
rt300@32
|
30
|
rt300@0
|
31 #define EVENT_THIN_FACTOR 30
|
rt300@0
|
32 #define EVENT_LOG_FILENAME "log.json"
|
rt300@0
|
33 #define UPLOAD_CHUNK_SIZE 1000
|
rt300@34
|
34 #define SAVE_CHUNK_SIZE 30000 // this is "too big" and must be saved, usually run end will save
|
rt300@0
|
35 #define APP_CREATION_TIME 0 // ignore this, pointless
|
rt300@18
|
36 #define PROGRAM_NAME "RIFTATHON"
|
rt300@18
|
37 #define PROGRAM_VERSION 0.1
|
rt300@0
|
38
|
rt300@0
|
39 #define SUPERVISED // this def will save files
|
rt300@0
|
40
|
rt300@0
|
41
|
rt300@0
|
42 // can add but don't change ordering - this will invalidate logs
|
rt300@0
|
43 enum leventType {APP_LOADED, // 0
|
rt300@0
|
44 INTRO_CONSENTED, // 1
|
rt300@0
|
45 APP_EXITED, // 2
|
rt300@0
|
46 HELP_PRESSED, // 3
|
rt300@0
|
47 QUESTIONNAIRE_COMPLETED, // 4
|
rt300@0
|
48
|
rt300@0
|
49 NEW_TEST, // 5
|
rt300@0
|
50 // al the set up of the new test
|
rt300@0
|
51 TARGET_PARAM_SET, // 6 setting up the target sound, param id and value
|
rt300@0
|
52 CANDIDATE_PARAM_SET, // 7 setting up the cand sound starting pos, subset of param id and value
|
rt300@0
|
53 CANDIDATE_CHANGEABLE_IDX, // 8 list of changeable indexes
|
rt300@0
|
54 CANDIDATE_MAPPING_IDS, // 9 which kind of control was mapped to which param
|
rt300@0
|
55 CONTROL_LIST, // 10 list of control types
|
rt300@0
|
56
|
rt300@0
|
57 TEST_TIMER_STARTED, // 11 we're off!!
|
rt300@0
|
58
|
rt300@0
|
59 CANDIDATE_PARAM_ADJUSTED, // 12 interactions changing params
|
rt300@0
|
60 SUBMIT_PRESSED, // 13 this is the suer saying they've found it, along with the actual answer
|
rt300@0
|
61 DISTANCE_TIME_SCORE, // 14 list of the results, now we should go back to NEW_TEST
|
rt300@0
|
62 COUNTDOWN_INITIATED, // 15 just in case
|
rt300@0
|
63 ALL_TESTS_COMPLETED, // 16 ?
|
rt300@0
|
64 TARGET_PLAYED, // 17 however this happens it might be good to know when this happens
|
rt300@0
|
65 CANDIDATE_PLAYED, // 18
|
rt300@9
|
66 START_THE_SEARCH_TESTS, // 19 probably superfluous
|
rt300@0
|
67 CRAP_TEST, // eliminate these coords somehow ???
|
rt300@0
|
68 EMPTY_EVENT,
|
rt300@0
|
69 SPEED_CHANGED, // 22 ms between sounds
|
rt300@9
|
70 SAVE_PRESET, // 23 save a preset
|
rt300@16
|
71 START_THE_TRAINING_TESTS, // 24 start traning
|
rt300@32
|
72 START_THE_EXP_TESTS, // 25 start explore/express
|
rt300@32
|
73 START_THE_PERFORMANCE_TESTS, // 26 starting perf
|
rt300@32
|
74
|
rt300@32
|
75 // new stuff for TRaining stage
|
rt300@43
|
76 START_NEW_RUN, // 27
|
rt300@43
|
77 TRAINING_RESULT, // 28
|
rt300@43
|
78 NEW_STEP, // 29
|
rt300@43
|
79 NEW_TARGET, // 30
|
rt300@43
|
80 NEW_START_POS, //31
|
rt300@43
|
81 FINISH_POS, //32
|
rt300@43
|
82 FINISHED_RUN, //33
|
rt300@43
|
83 CANDIDATE_PARAM_ADJUSTED_ALL, //34
|
rt300@43
|
84 TARGET_AND_MATCH, //35
|
rt300@43
|
85 FORGOT_SEQ, //36
|
rt300@45
|
86 RUN_SKIPPED, //37
|
rt300@45
|
87 START_THE_TRAINING_DEMO
|
rt300@0
|
88
|
rt300@0
|
89 };
|
rt300@0
|
90
|
rt300@0
|
91 //---------------------------------------------------------------------------
|
rt300@0
|
92
|
rt300@0
|
93 class lEvent{
|
rt300@0
|
94 public:
|
rt300@0
|
95 // try and make this as compact as possible.
|
rt300@0
|
96 leventType eventType;
|
rt300@0
|
97 double val1; // x coord, scale if zoom
|
rt300@0
|
98 double val2; // y coord, 0 if zoom
|
rt300@0
|
99 int sliderID; // xtra int
|
rt300@0
|
100 long long eventTime; // MILLISECONDS since ref date
|
rt300@0
|
101 vector<int> eventData; // variable length data int
|
rt300@0
|
102
|
rt300@0
|
103 // old sonic zoom style event
|
rt300@0
|
104 lEvent(leventType eType, double v1 = 0.0, double v2 = 0.0,int sID = 0){
|
rt300@0
|
105 eventType = eType;
|
rt300@0
|
106 val1 = v1;
|
rt300@0
|
107 val2 = v2;
|
rt300@0
|
108 sliderID = sID;
|
rt300@0
|
109
|
rt300@0
|
110 double timemsd = [NSDate timeIntervalSinceReferenceDate]; // MILLISECONDS, but with fractional part (
|
rt300@0
|
111 eventTime = (unsigned long long)(timemsd*1000) - APP_CREATION_TIME;
|
rt300@0
|
112
|
rt300@0
|
113 }
|
rt300@0
|
114
|
rt300@0
|
115 // new tweakathlon event
|
rt300@0
|
116 lEvent(leventType eType, vector<int> theData){
|
rt300@0
|
117 eventType = eType;
|
rt300@0
|
118 eventData = theData;
|
rt300@0
|
119
|
rt300@0
|
120 double timemsd = [NSDate timeIntervalSinceReferenceDate]; // MILLISECONDS, but with fractional part (
|
rt300@0
|
121 eventTime = (unsigned long long)(timemsd*1000) - APP_CREATION_TIME;
|
rt300@0
|
122
|
rt300@0
|
123 }
|
rt300@0
|
124
|
rt300@0
|
125 lEvent(const Json::Value &jevt){
|
rt300@0
|
126 // constructor takes "jsonToEvent" readfile function role
|
rt300@0
|
127 eventType = (leventType)jevt["eType"].asInt();
|
rt300@0
|
128 val1 = jevt["v1"].asDouble();
|
rt300@0
|
129 val2 = jevt["v2"].asDouble();
|
rt300@0
|
130 sliderID = jevt["sID"].asInt();
|
rt300@0
|
131 eventTime = jevt["eTime"].asLargestInt();
|
rt300@0
|
132
|
rt300@0
|
133 // TODO what happens if we try to read one that isn't there?
|
rt300@0
|
134
|
rt300@0
|
135 }
|
rt300@0
|
136 // new tweak event to json
|
rt300@0
|
137 Json::Value eventToJson(){
|
rt300@0
|
138 Json::Value jevt;
|
rt300@0
|
139 jevt["eType"] = eventType;
|
rt300@0
|
140 // convert vector to json array
|
rt300@0
|
141 for(auto it = eventData.begin(); it < eventData.end(); it++){
|
rt300@0
|
142 jevt["eData"].append(*it);
|
rt300@0
|
143 }
|
rt300@0
|
144 jevt["eTime"] = eventTime;
|
rt300@0
|
145 return jevt;
|
rt300@0
|
146 }
|
rt300@0
|
147 Json::Value eventToJsonOld(){
|
rt300@0
|
148 Json::Value jevt;
|
rt300@0
|
149 jevt["eType"] = eventType;
|
rt300@0
|
150 // here: should give a certain number of sig figs?
|
rt300@0
|
151 jevt["v1"] = val1;
|
rt300@0
|
152 jevt["v2"] = val2;
|
rt300@0
|
153 jevt["sID"] = sliderID;
|
rt300@0
|
154 jevt["eTime"] = eventTime;
|
rt300@0
|
155 return jevt;
|
rt300@0
|
156 }
|
rt300@0
|
157 };
|
rt300@0
|
158
|
rt300@0
|
159
|
rt300@0
|
160 //------------------------------------------
|
rt300@0
|
161
|
rt300@0
|
162 class EventLogger{
|
rt300@0
|
163 public:
|
rt300@0
|
164 Poco::Mutex _mutex;
|
rt300@0
|
165
|
rt300@0
|
166 int nextUploadNumber;
|
rt300@0
|
167 int nextUploadQty;
|
rt300@0
|
168 bool loggingEnabled;
|
rt300@0
|
169
|
rt300@0
|
170 bool logUploadInProgress;
|
rt300@0
|
171 bool serverConnectionOK;
|
rt300@0
|
172 bool consentGiven;
|
rt300@0
|
173 bool questionnaireCompleted;
|
rt300@0
|
174 bool questionnaireUploaded;
|
rt300@0
|
175
|
rt300@34
|
176 unsigned long long deviceID;
|
rt300@34
|
177 unsigned long long totalInteractionTime, savedInteractionTime, sessionTime, sessionStartTime;
|
rt300@0
|
178 string userName; // not unique
|
rt300@0
|
179 string questionnaireComments;
|
rt300@0
|
180 // constr
|
rt300@0
|
181 EventLogger();
|
rt300@0
|
182
|
rt300@0
|
183 // public methods:
|
rt300@18
|
184 void onUsernameEntered();
|
rt300@0
|
185 void startLoadAll();
|
rt300@0
|
186 void exitAndSave();
|
rt300@0
|
187 void setUsername(const char *u);
|
rt300@0
|
188 string getUsername(){
|
rt300@0
|
189 return userName;
|
rt300@0
|
190 }
|
rt300@0
|
191 void newUser();
|
rt300@0
|
192 void logEvent(const leventType& evtType);
|
rt300@0
|
193 void logEvent(const leventType& evtType, const vector<int> eventData);
|
rt300@0
|
194
|
rt300@0
|
195 void questionnaireAnswersObtained(vector<int> answers, const char* userComments);
|
rt300@0
|
196 void urlResponse(ofHttpResponse & response);
|
rt300@0
|
197
|
rt300@0
|
198 void questionnaireOK();
|
rt300@0
|
199 void eventlogOK();
|
rt300@0
|
200 void testConnectionOK();
|
rt300@0
|
201 void questionnaireNotOK();
|
rt300@0
|
202 void eventlogNotOK();
|
rt300@0
|
203 void testConnectionNotOK();
|
rt300@0
|
204
|
rt300@0
|
205 void printAll();
|
rt300@0
|
206 void saveSessionToFile();
|
rt300@0
|
207
|
rt300@0
|
208 private:
|
rt300@0
|
209 void thinnedSliderEvent(lEvent newEvent);
|
rt300@0
|
210 vector<lEvent> theEvents; // all logged but not uploaded events
|
rt300@0
|
211
|
rt300@0
|
212 vector<int> questionnaireAnswers;
|
rt300@0
|
213
|
rt300@18
|
214 string nextLogFileIndexString();
|
rt300@18
|
215 void loadUserDetailsFromLastLogFile(int numExistingLogs);
|
rt300@0
|
216 // private methods
|
rt300@0
|
217 void testConnection();
|
rt300@0
|
218 void checkLogFile();
|
rt300@0
|
219 void deleteLogs(); // new user
|
rt300@0
|
220 bool uploadEventLog(bool async);
|
rt300@0
|
221 void firstEverAppOpen();
|
rt300@0
|
222 void loadExistingLogFile(const string &jsonFile);
|
rt300@0
|
223 void uploadQuestionnaire();
|
rt300@0
|
224 bool sendToServer(string functionName, Json::Value jsonData, bool async);
|
rt300@0
|
225
|
rt300@0
|
226 Json::Value logsToJson();
|
rt300@0
|
227 Json::Value questionnaireToJson();
|
rt300@0
|
228
|
rt300@0
|
229
|
rt300@0
|
230 template<class T> bool matchID(T thing);
|
rt300@0
|
231 bool matchID2();
|
rt300@0
|
232 //
|
rt300@0
|
233 ServerComms *serverComms;
|
rt300@18
|
234 int nextLogFileIndex;
|
rt300@0
|
235 };
|
rt300@0
|
236
|
rt300@0
|
237
|
rt300@0
|
238 //---------------------------------------------------------------------------
|
rt300@0
|
239
|
rt300@0
|
240
|
rt300@0
|
241 #endif /* defined(__oscSenderExample__eventLogger__) */
|