Browse code

csv to web function and specific timeout for first get location

louis.jonget authored on10/10/2022 10:33:01
Showing4 changed files
1 1
Binary files a/build/bike_companion.pbw and b/build/bike_companion.pbw differ
... ...
@@ -25,6 +25,7 @@
25 25
       "altitude",
26 26
       "altitude_accuracy",
27 27
       "speed",
28
+      "max_speed",
28 29
       "timestamp",
29 30
       "status",
30 31
       "JSReady"
... ...
@@ -9,7 +9,7 @@ const uint32_t inbox_size = 64;
9 9
 const uint32_t outbox_size = 64;
10 10
 
11 11
 static uint32_t size ;
12
-static char * msg;
12
+//static char * msg;
13 13
 
14 14
 static char s_status[3];
15 15
 
... ...
@@ -22,6 +22,7 @@ static char s_altitude[6];
22 22
 static char s_altitude_accuracy[4];
23 23
 static char s_timestamp[14];
24 24
 static char s_speed[6];
25
+static char s_max_speed[5];
25 26
 
26 27
 static char s_msg[50];
27 28
 
... ...
@@ -35,13 +36,12 @@ typedef enum {
35 36
   accuracy,
36 37
   altitude_accuracy,
37 38
   speed,
39
+  max_speed,
38 40
   timestamp
39 41
 } AppKey;
40 42
 
41 43
 
42
-
43
-static void prv_select_click_handler(ClickRecognizerRef recognizer, void *context) {
44
-  text_layer_set_text(s_other_text_layer, "Select - sending get geoloc");
44
+static void send_message(char * msg){
45 45
 
46 46
   // Declare the dictionary's iterator
47 47
   DictionaryIterator *out_iter;
... ...
@@ -50,8 +50,6 @@ static void prv_select_click_handler(ClickRecognizerRef recognizer, void *contex
50 50
   AppMessageResult result = app_message_outbox_begin(&out_iter);
51 51
 
52 52
   if(result == APP_MSG_OK) {
53
-    // Add an item to ask for geoloc
54
-    msg = "get";
55 53
     dict_write_cstring(out_iter, status, msg);
56 54
 
57 55
     // Send this message
... ...
@@ -63,8 +61,12 @@ static void prv_select_click_handler(ClickRecognizerRef recognizer, void *contex
63 61
     // The outbox cannot be used right now
64 62
     APP_LOG(APP_LOG_LEVEL_ERROR, "Error preparing the outbox: %d", (int)result);
65 63
   }
66
-  // Button click provider
67
-  // window_set_click_config_provider(s_window, click_config_provider);
64
+}
65
+
66
+
67
+static void prv_select_click_handler(ClickRecognizerRef recognizer, void *context) {
68
+  text_layer_set_text(s_other_text_layer, "Select - sending get geoloc");
69
+  send_message("get");
68 70
 }
69 71
 
70 72
 static void prv_up_click_handler(ClickRecognizerRef recognizer, void *context) {
... ...
@@ -223,14 +225,28 @@ static void inbox_received_callback(DictionaryIterator *iter, void *context) {
223 225
   if(speed_tuple) {
224 226
     memset(s_speed,'\0',sizeof(s_speed));
225 227
     strncpy(s_speed, speed_tuple->value->cstring, 5);
226
-
228
+    //uint i_speed=atoi(s_speed);
227 229
     // Display in the TextLayer
228 230
     //text_layer_set_text(s_speed_text_layer, lf_speed);
229
-    APP_LOG(APP_LOG_LEVEL_DEBUG, "speed message received : %s",s_speed);
231
+    APP_LOG(APP_LOG_LEVEL_DEBUG, "speed message received : %s ",s_speed);
230 232
   }else{
231 233
     //APP_LOG(APP_LOG_LEVEL_DEBUG, "not speed message... ");
232 234
   }
233 235
 
236
+  // Read max_speed returns
237
+  Tuple *max_speed_tuple = dict_find(iter, MESSAGE_KEY_max_speed);
238
+
239
+  if(max_speed_tuple) {
240
+    memset(s_max_speed,'\0',sizeof(s_max_speed));
241
+    strncpy(s_max_speed, max_speed_tuple->value->cstring, 5);
242
+    //uint i_max_speed=atoi(s_max_speed);
243
+    // Display in the TextLayer
244
+    //text_layer_set_text(s_max_speed_text_layer, lf_max_speed);
245
+    APP_LOG(APP_LOG_LEVEL_DEBUG, "max_speed message received : %s ",s_max_speed);
246
+  }else{
247
+    //APP_LOG(APP_LOG_LEVEL_DEBUG, "not max_speed message... ");
248
+  }
249
+
234 250
 
235 251
   // Read status returns
236 252
   Tuple *status_tuple = dict_find(iter, MESSAGE_KEY_status);
... ...
@@ -242,23 +258,22 @@ static void inbox_received_callback(DictionaryIterator *iter, void *context) {
242 258
 
243 259
     // DEBUG concatenate all data received
244 260
     memset(s_msg,'\0',sizeof(s_msg));
245
-    strncpy(s_msg, s_status, 2);
246
-    //strcat(s_msg, "Lat. ");
261
+    strncpy(s_msg, "Acc. ",5);
262
+    strcat(s_msg, s_accuracy);
263
+    strcat(s_msg, "    Alt. ");
264
+    strcat(s_msg, s_altitude);
265
+    strcat(s_msg, "\n Max. ");
266
+    strcat(s_msg, s_max_speed);
267
+    //strcat(s_msg, "\n Lat. ");
247 268
     //strcat(s_msg, s_latitude);
248 269
     //strcat(s_msg, "\n Long. ");
249 270
     //strcat(s_msg, s_longitude);
250
-    strcat(s_msg, "\n Acc. ");
251
-    strcat(s_msg, s_accuracy);
252
-    strcat(s_msg, "\n Alt. ");
253
-    strcat(s_msg, s_altitude);
254
-    //strcat(s_msg, "\n Speed. ");
255
-    //strcat(s_msg, s_speed);
256 271
     //strcat(s_msg, "\n Time. ");
257 272
     //strcat(s_msg, s_timestamp);
258 273
 
259 274
     APP_LOG(APP_LOG_LEVEL_DEBUG, "to display : %s ",s_msg);
260
-    // DEBUG Display in the TextLayer
261
-    text_layer_set_text(s_speed_text_layer, s_speed);
275
+
276
+    text_layer_set_text(s_speed_text_layer, strcat(s_speed," km/h"));
262 277
     text_layer_set_text(s_other_text_layer, s_msg);
263 278
   }else{
264 279
     //APP_LOG(APP_LOG_LEVEL_DEBUG, "not status message... ");
... ...
@@ -307,6 +322,7 @@ static void prv_init(void) {
307 322
 }
308 323
 
309 324
 static void prv_deinit(void) {
325
+  send_message("exit");
310 326
   window_destroy(s_window);
311 327
 }
312 328
 
... ...
@@ -5,12 +5,21 @@ var clay = new Clay(clayConfig);
5 5
 var messageKeys = require('message_keys');
6 6
 
7 7
 var message;
8
+var gpx_to_strava = false;
9
+var csv_to_web = true;
8 10
 
9
-//var locationInterval;
11
+var firstlocationInterval;
12
+var locationInterval;
13
+
14
+var firstlocationOptions = {
15
+  'enableHighAccuracy': true, // default = false (quick and dirty mode), can be true (more accurate but need more power and time)
16
+  'timeout': 60000, //60s timeout to get a good signal
17
+  'maximumAge': 5 // no cache
18
+};
10 19
 var locationOptions = {
11 20
   'enableHighAccuracy': true, // default = false (quick and dirty mode), can be true (more accurate but need more power and time)
12
-  'timeout': 5000, //2s timeout
13
-  'maximumAge': 0 // no cache
21
+  'timeout': 5000, //5s timeout to get a good signal
22
+  'maximumAge': 5 // no cache
14 23
 };
15 24
 var geoloc_id;
16 25
 
... ...
@@ -83,12 +92,28 @@ function speed_from_distance_and_time(p1, p2) {
83 92
   return speed_kph;
84 93
 }
85 94
 
95
+// Get max speed of the run
96
+//
97
+function getMaxSpeed(lastSpeed) {
98
+  oldmax = localStorage.getItem("maxSpeed") || -1;
99
+  if (oldmax < lastSpeed) {
100
+    maxSpeed = lastSpeed
101
+  } else if (oldmax > lastSpeed) {
102
+    maxSpeed = oldmax
103
+  } else {
104
+    maxSpeed = oldmax
105
+  }
106
+  localStorage.setItem("maxSpeed", maxSpeed);
107
+  return maxSpeed
108
+}
109
+
86 110
 // split float number into an array of int (null returned instead of 0 for decimal)
87 111
 //
88 112
 function splitFloatNumber(num) {
89 113
   const intStr = num.toString().split('.')[0];
90 114
   const decimalStr = num.toString().split('.')[1];
91 115
   return [Number(intStr), Number(decimalStr)];
116
+
92 117
 }
93 118
 
94 119
 // Build GPX headers
... ...
@@ -114,9 +139,99 @@ function GPXfooterBuilder() {
114 139
   var GPX = localStorage.getItem("GPX");
115 140
   var footer = '</trkseg></trk></gpx>';
116 141
   var ret = localStorage.setItem("GPX", GPX + footer);
117
-  return true;
142
+  console.log("GPX closed : " + GPX + footer);
143
+  return ret;
144
+}
145
+
146
+// Send GPX to Strava profile
147
+// TODO : get authentication creds from settings
148
+function SendToStrava() {
149
+  var GPX = localStorage.getItem("GPX");
150
+
151
+
152
+  /* -------------------- */
153
+  var array = [GPX]; // an array consisting of a single string
154
+  console.log('------array for blob: ' + array)
155
+  var blob = new Blob(array, { type: "application/gpx+xml" }); // the blob
156
+
157
+  // creating multipart/form-data to be sent
158
+  var data = new FormData();
159
+  data.append("file", blob, "blob.gpx");
160
+  data.append("name", "test");
161
+  data.append("description", "testdesc");
162
+  data.append("data_type", "gpx");
163
+
164
+  var url = "https://www.strava.com/api/v3/uploads";
165
+  var xhr = new XMLHttpRequest();
166
+  xhr.withCredentials = true;
167
+  xhr.timeout = 10000; // time in milliseconds
168
+
169
+  xhr.open("POST", url, false);
170
+  xhr.setRequestHeader("Authorization", "Bearer d8927033b3996efe1e5a4e62425bc2aff8f635b0");
171
+
172
+  console.log('------xhr opened with authorization')
173
+  xhr.onload = function () {
174
+    console.log('------xhr onload')
175
+    if (xhr.readyState === 4) {
176
+      console.log('------xhr request returned with ' + xhr.status);
177
+      console.log(this.responseText);
178
+      if (xhr.status == 200) {
179
+        //console.log('--> HTTP 200');
180
+        return true;
181
+      } else {
182
+        //console.log('--> HTTP ' + xhr.status);
183
+        return false;
184
+      }
185
+    }
186
+  };
187
+
188
+  xhr.send(data);
189
+  /* -------------------- */
190
+
118 191
 }
119 192
 
193
+
194
+// Build CSV
195
+function CSV(pos) {
196
+  var CSV = localStorage.getItem("CSV");
197
+  var datapoint = pos.timestamp + "," + pos.coords.lat + "," + pos.coords.long + "," + pos.coords.altitude + "," + pos.coords.accuracy + "," + pos.coords.altitudeAccuracy + "," + pos.coords.heading + "," + pos.coords.speed;
198
+
199
+  localStorage.setItem("CSV", CSV + datapoint);
200
+
201
+}
202
+// Send CSV to web server (need configuration on serverside)
203
+// TODO : secure it ?
204
+function PostToWeb() {
205
+  var CSV = localStorage.getItem("CSV");
206
+
207
+  var url = "https://192.168.170.33/strava/upload.php";
208
+  var xhr = new XMLHttpRequest();
209
+  xhr.timeout = 10000; // time in milliseconds
210
+
211
+  xhr.open("POST", url, false);
212
+
213
+  console.log('------xhr opened')
214
+  xhr.onload = function () {
215
+    console.log('------xhr onload')
216
+    if (xhr.readyState === 4) {
217
+      console.log('------xhr request returned with ' + xhr.status);
218
+      console.log(this.responseText);
219
+      if (xhr.status == 200) {
220
+        //console.log('--> HTTP 200');
221
+        return true;
222
+      } else {
223
+        //console.log('--> HTTP ' + xhr.status);
224
+        return false;
225
+      }
226
+    }
227
+  };
228
+
229
+  //send CSV in body
230
+  xhr.send(CSV);
231
+
232
+}
233
+
234
+
120 235
 // Adding leading characters to string for nice displays
121 236
 //
122 237
 function padStart(string, max_length, padding) {
... ...
@@ -141,8 +256,15 @@ function locationSuccess(new_pos) {
141 256
   storeLocation(new_pos);
142 257
   if (prev_pos === null) {
143 258
     console.log('--- start building gpx');
144
-    // Start the GPX file
145
-    GPXHeadersBuilder(new Date(new_pos.timestamp).toISOString(), "test", "18");
259
+
260
+    if (gpx_to_strava) {
261
+      // Start the GPX file
262
+      GPXHeadersBuilder(new Date(new_pos.timestamp).toISOString(), "test", "18");
263
+    }
264
+    if (csv_to_web) {
265
+      // Start the csv file
266
+      CSV(new_pos);
267
+    }
146 268
   } else {
147 269
     //clear watch of new position to avoid overlap
148 270
     //navigator.geolocation.clearWatch(geoloc_id);
... ...
@@ -162,16 +284,27 @@ function locationSuccess(new_pos) {
162 284
     latitudeString = new_pos.coords.latitude.toString().substring(0, 12);
163 285
     longitudeString = new_pos.coords.longitude.toString().substring(0, 12);
164 286
     accuracyString = new_pos.coords.accuracy.toString().substring(0, 4);
287
+    //console.log("split num : " + new_pos.coords.altitude);
165 288
     altitudeString = splitFloatNumber(new_pos.coords.altitude)[0].toString().substring(0, 5);
166 289
     timestampISO = new Date(new_pos.timestamp).toISOString();
167
-    speedString = speed.toString().substring(0, 5);
290
+    //console.log("split num : " + speed);
291
+    speedString = splitFloatNumber(speed)[0].toString().substring(0, 3); //+ "." + splitFloatNumber(speed)[1].toString().substring(0, 1);
292
+
293
+    //console.log("split num : " + getMaxSpeed(speed));
294
+    maxSpeedString = splitFloatNumber(getMaxSpeed(speed))[0].toString().substring(0, 4);
168 295
 
169 296
     if (speedString == "NaN") {
170 297
       speedString = "---";
171 298
     }
172 299
 
173
-    //add a new datapoint to GPX file
174
-    GPXtrkptBuilder(latitudeString, longitudeString, altitudeString, timestampISO);
300
+    if (gpx_to_strava) {
301
+      //add a new datapoint to GPX file
302
+      GPXtrkptBuilder(latitudeString, longitudeString, altitudeString, timestampISO);
303
+    }
304
+    if (csv_to_web) {
305
+      // Start the csv file
306
+      CSV(new_pos);
307
+    }
175 308
 
176 309
     console.log('GPX: ' + localStorage.getItem("GPX"));
177 310
 
... ...
@@ -181,6 +314,7 @@ function locationSuccess(new_pos) {
181 314
       'accuracy': accuracyString,
182 315
       'altitude': altitudeString,
183 316
       'speed': speedString,
317
+      'max_speed': maxSpeedString,
184 318
       'status': message
185 319
     };
186 320
 
... ...
@@ -201,29 +335,36 @@ function locationError(err) {
201 335
   //navigator.geolocation.clearWatch(geoloc_id);
202 336
 
203 337
   console.warn('location error (' + err.code + '): ' + err.message);
338
+  if (csv_to_web) {
339
+
340
+    var CSV = localStorage.getItem("CSV");
341
+    var datapoint = Date.now() + ",location error," + err.code + "," + err.message + ",,,,";
342
+
343
+    localStorage.setItem("CSV", CSV + datapoint);
344
+  }
204 345
 
205 346
   // restart the watch
206 347
   //geoloc_id = navigator.geolocation.watchPosition(locationSuccess, locationError, locationOptions);
207 348
 }
208 349
 
209
-function get_coordinate() {
210
-  /*console.log('---- start geoloc');
211
-  navigator.geolocation.getCurrentPosition(locationSuccess, locationError, locationOptions);
212
-  */
213
-}
214 350
 
215
-function init() {
216
-  // local storage init
217
-  localStorage.clear();
218
-  // clear any other var to do
219
-  //clearInterval(locationInterval);
351
+function get_coordinate() {
352
+  console.log('--- Starting regular getCurrentPosition loop using setInterval at 5 sec');
220 353
 
354
+  navigator.geolocation.getCurrentPosition(locationSuccess, locationError, firstlocationOptions);
221 355
 
222
-  console.log('--- Starting regular getCurrentPosition loop using setInterval at 5 sec');
223 356
   locationInterval = setInterval(function () {
224 357
     navigator.geolocation.getCurrentPosition(locationSuccess, locationError, locationOptions);
225 358
   }, 5000);
226 359
 
360
+}
361
+
362
+function init() {
363
+  // local storage init,
364
+  // todo : clear only temporary values, not clay config (and do it on exit ?)
365
+  localStorage.clear();
366
+  // clear any other var to do
367
+  clearInterval(locationInterval);
227 368
 
228 369
 }
229 370
 
... ...
@@ -245,6 +386,19 @@ Pebble.addEventListener('appmessage', function (e) {
245 386
     case 'get':
246 387
       get_coordinate();
247 388
       break;
389
+    case 'exit':
390
+      if (gpx_to_strava) {
391
+        //End GPX file
392
+        GPXfooterBuilder();
393
+        //send to strava through API
394
+        SendToStrava();
395
+      }
396
+      if (csv_to_web) {
397
+        //CSV(new_pos);
398
+        // send CSV to web server through API
399
+        PostToWeb();
400
+      }
401
+      break;
248 402
     default:
249 403
       console.log('Sorry. I don\'t understand your request :' + dict[0]);
250 404
   }