Subversion Repositories public

Rev

Rev 225 | Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
224 andreas 1
/***************************************************************************
2
 *   Copyright (C) 2007, 2008 by Andreas Theofilu                          *
3
 *   andreas@theosys.at                                                    *
4
 *                                                                         *
5
 *   This program is free software; you can redistribute it and/or modify  *
6
 *   it under the terms of the GNU General Public License as published by  *
7
 *   the Free Software Foundation version 3 of the License.                *
8
 *                                                                         *
9
 *   This program is distributed in the hope that it will be useful,       *
10
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12
 *   GNU General Public License for more details.                          *
13
 *                                                                         *
14
 *   You should have received a copy of the GNU General Public License     *
15
 *   along with this program; if not, write to the                         *
16
 *   Free Software Foundation, Inc.,                                       *
17
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
18
 ***************************************************************************/
19
#include <time.h>
20
#include <string.h>
21
#include <math.h>
22
 
23
#include <qxml.h>
24
#include <qlistview.h>
25
#include <qgrid.h>
26
#include <qdatetime.h>
27
#include <qxml.h>
28
 
29
#include <klocale.h>
30
 
31
#include "import.h"
32
 
33
#define FLD_NAME		1
34
#define FLD_TOTALTIMESECONDS	2
35
#define FLD_DISTANCEMETERS	3
36
#define FLD_LATITUDEDEGREES	4
37
#define FLD_LONGITUDEDEGREES	5
38
#define FLD_VALUE		6
39
#define FLD_INTENSITY		7
40
#define FLD_TIME		8
41
#define FLD_ALTITUDEMETERS	9
42
#define FLD_SENSORSTATE		10
43
#define FLD_STARTTIME		11
44
#define FLD_AVERAGECADENCE	12
45
#define FLD_SPORTTYPE		13
46
#define FLD_VERSIONMAJOR	14
47
#define FLD_VERSIONMINOR	15
48
#define FLD_BUILDMAJOR		16
49
#define FLD_BUILDMINOR		17
50
#define FLD_TYPE		18
51
#define FLD_BUILDER		19
52
#define FLD_LANGID		20
53
#define FLD_PARTNUMBER		21
54
#define FLD_CADENCE		22
55
#define FLD_MAXSPEED		23
56
#define FLD_CALORIES		24
57
 
58
#define CON_COURSES		100
59
#define CON_COURSE		101
60
#define CON_LAP			102
61
#define CON_BEGINPOSITION	103
62
#define CON_ENDPOSITION		104
63
#define CON_AVERAGEHEARTRATEBPM	105
64
#define CON_MAXIMUMHEARTRATEBPM	106
65
#define CON_TRACK		107
66
#define CON_TRACKPOINT		108
67
#define CON_POSITION		109
68
#define CON_HEARTRATEBPM	110
69
#define CON_AUTHOR		111
70
#define CON_VERSION		112
71
#define CON_BUILD		113
72
 
73
#define STOPSTOP		0
74
 
75
KEYS keys[] = {
76
	/* Fields */
77
	{ FLD_NAME,			QString("name") },
78
	{ FLD_TOTALTIMESECONDS,		QString("TotalTimeSeconds") },
79
	{ FLD_DISTANCEMETERS,		QString("DistanceMeters") },
80
	{ FLD_LATITUDEDEGREES,		QString("LatitudeDegrees") },
81
	{ FLD_LONGITUDEDEGREES,		QString("LongitudeDegrees") },
82
	{ FLD_VALUE,			QString("Value") },
83
	{ FLD_INTENSITY,		QString("Intensity") },
84
	{ FLD_TIME,			QString("Time") },
85
	{ FLD_ALTITUDEMETERS,		QString("AltitudeMeters") },
86
	{ FLD_SENSORSTATE,		QString("SensorState") },
87
	{ FLD_STARTTIME,		QString("StartTime") },
88
	{ FLD_AVERAGECADENCE,		QString("AverageCadence") },
89
	{ FLD_SPORTTYPE,		QString("SportType") },
90
	{ FLD_VERSIONMAJOR,		QString("VersionMajor") },
91
	{ FLD_VERSIONMINOR,		QString("VersionMinor") },
92
	{ FLD_BUILDMAJOR,		QString("BuildMajor") },
93
	{ FLD_BUILDMINOR,		QString("BuildMinor") },
94
	{ FLD_TYPE,			QString("Type") },
95
	{ FLD_BUILDER,			QString("Builder") },
96
	{ FLD_LANGID,			QString("LangID") },
97
	{ FLD_PARTNUMBER,		QString("PartNumber") },
98
	{ FLD_CADENCE,			QString("Cadence") },
99
	{ FLD_CALORIES,			QString("Calories") },
100
	{ FLD_MAXSPEED,			QString("MaximumSpeed") },
101
	/* Container */
102
	{ CON_COURSES,			QString("Courses") },
103
	{ CON_COURSE,			QString("Course") },
104
	{ CON_LAP,			QString("Lap") },
105
	{ CON_BEGINPOSITION,		QString("BeginPosition") },
106
	{ CON_ENDPOSITION,		QString("EndPosition") },
107
	{ CON_AVERAGEHEARTRATEBPM,	QString("AverageHeartRateBpm") },
108
	{ CON_MAXIMUMHEARTRATEBPM,	QString("MaximumHeartRateBpm") },
109
	{ CON_TRACK,			QString("Track") },
110
	{ CON_TRACKPOINT,		QString("Trackpoint") },
111
	{ CON_POSITION,			QString("Position") },
112
	{ CON_HEARTRATEBPM,		QString("HeartRateBpm") },
113
	{ CON_AUTHOR,			QString("Author") },
114
	{ CON_VERSION,			QString("Version") },
115
	{ CON_BUILD,			QString("Build") },
116
	/* Errors */
117
	{ ERR_OK,			QString("OK") },	// this is no error
118
	{ ERR_NOFILE,			i18n("IMPORT: No file name to parse XML!") },	// but this
119
	{ ERR_TAGS,			i18n("IMPORT: More end tags than open tags! Invalid XML file.") },
120
	{ ERR_ALLOCGMN,			i18n("IMPORT: Error allocating memory for base Garmin structure.") },
121
	{ ERR_ALLOCLAP,			i18n("IMPORT: Error allocating memory for a lap.") },
122
	{ ERR_ALLOCPOINT,		i18n("IMPORT: Error allocating memory for a track point.") },
123
	{ ERR_ALLOCRUN,			i18n("IMPORT: Error allocating memory for running information.") },
124
	{ ERR_ALLOCLIST,		i18n("IMPORT: Error allocating memory for a list node.") },
125
	{ STOPSTOP,			QString::null }
126
};
127
 
128
void gmn_import::Initialize()
129
{
130
	qfstat = false;
131
	qfopen = false;
132
	__error = 0;
133
	gmn = 0;
134
	list_lap = list_track = 0;
135
	ds = 0;
136
}
137
 
138
gmn_import::gmn_import (const QFile &qfile)
139
{
140
	Initialize ();
141
	file.setName (qfile.name ());
142
 
143
	if (file.exists ())
144
	   qfstat = true;
145
}
146
 
147
void gmn_import::setFile (const QFile &qfile)
148
{
149
	if (qfopen)
150
	{
151
	   file.close ();
152
	   qfopen = false;
153
	}
154
 
155
	qfstat = false;
156
	file.setName (qfile.name ());
157
 
158
	if (file.exists ())
159
	   qfstat = true;
160
}
161
 
162
void gmn_import::setFile (const QString &sfile)
163
{
164
	if (qfopen)
165
	{
166
	   file.close ();
167
	   qfopen = false;
168
	   __error = 1;
169
	}
170
 
171
	qfstat = false;
172
	file.setName (sfile);
173
 
174
	if (file.exists ())
175
	   qfstat = true;
176
}
177
 
178
/*
179
 * Convert an ISO8601 formated date into garmin epoch.
180
 */
181
unsigned int gmn_import::garmin_time (const QString& tstamp)
182
{
183
QDateTime dt;
184
time_t tval;
185
 
186
	/*
187
	 * tstamp should contain a valid ISO8601 formated date and time stamp.
188
	 * We will convert it to a kind of epoch, but with a garmin specific
189
	 * offset.
190
	 */
191
	dt = dt.fromString(tstamp, Qt::ISODate);
192
 
193
	if (!dt.isValid())
194
	   return 0;
195
 
196
	tval = dt.toTime_t() - TIME_OFFSET;
197
	return (unsigned int)tval;
198
}
199
 
200
/*
201
 * This function initializes the XML parser.
202
 */
203
bool gmn_import::startDocument()
204
{
205
	indent = 0;
206
	con.setAscii("");
207
	subCon.setAscii("");
208
	lpos = tpos = oldLPos = 0;
209
	return TRUE;
210
}
211
 
212
/*
213
 * This is called every time a new start element was parsed.
214
 */
215
bool gmn_import::startElement( const QString&, const QString&,
216
                                    const QString& qName,
217
                                    const QXmlAttributes&)
218
{
219
int i = CON_FIRST;
220
 
221
	while (i <= CON_LAST)
222
	{
223
	   if (qName.lower() == getKey(i).lower())
224
	   {
225
	      if (qName.lower() == QString("course"))
226
	      {
227
		 memset (&run, 0, sizeof (D1009));
228
		 con.setAscii("course");
229
		 run.track_index = 0;
230
		 run.first_lap_index = 0;
231
		 run.sport_type = 0;		// Running
232
		 tk = i;
233
	      }
234
 
235
	      if (qName.lower() == QString("lap"))
236
	      {
237
		 memset (&lap, 0, sizeof (D1015));
238
		 lap.index = lpos;
239
		 lap.begin.lat = 0x7fffffff;
240
		 lap.begin.lon = 0x7fffffff;
241
		 lap.end.lat = 0x7fffffff;
242
		 lap.end.lon = 0x7fffffff;
243
		 con.setAscii("lap");
244
		 lpos++;
245
		 tk = i;
246
	      }
247
 
248
	      if (con == QString("lap") && qName.lower() == QString("BeginPosition").lower())
249
		 subCon = qName.lower();
250
	      else if (con == QString("lap") && qName.lower() == QString("EndPosition").lower())
251
		 subCon = qName.lower();
252
	      else if (con == QString("lap") && qName.lower() == QString("AverageHeartRateBpm").lower())
253
		 subCon = qName.lower();
254
	      else if (con == QString("lap") && qName.lower() == QString("MaximumHeartRateBpm").lower())
255
		 subCon = qName.lower();
256
 
257
	      if (qName.lower() == QString("trackpoint"))
258
	      {
259
		 memset (&point, 0, sizeof (D304));
260
		 point.alt = 1.0e24;
261
		 point.posn.lat = 0x7fffffff;
262
		 point.posn.lon = 0x7fffffff;
263
		 con.setAscii("trackpoint");
264
		 tpos++;
265
		 tk = i;
266
	      }
267
 
268
	      if (con == QString("trackpoint") && qName.lower() == QString("position"))
269
		 subCon = qName.lower();
270
	      else if (con == QString("trackpoint") && qName.lower() == QString("heartratebpm"))
271
		 subCon = qName.lower();
272
	   }
273
 
274
	   i++;
275
	}
276
 
277
	i = FLD_FIRST;
278
 
279
	while (i <= FLD_LAST)
280
	{
281
	   if (qName.lower() == getKey(i).lower())
282
	      tk = i;
283
 
284
	   i++;
285
	}
286
 
287
	indent++;
288
	return TRUE;
289
}
290
 
291
/*
292
 * This is called every time an element is closed.
293
 */
294
bool gmn_import::endElement( const QString&, const QString&, const QString& qName)
295
{
296
	indent--;
297
 
298
	if (qName.lower() == QString("lap"))
299
	{
300
	   garmin_data *gdt;
301
	   garmin_list *l;
302
 
303
	   con.setAscii("");
304
 
305
	   if (!gmn)		/* allocating space for first structure */
306
	   {
307
	      if ((gmn = garmin_alloc_data (data_Dlist)) == NULL)
308
	      {
309
		 __error = 3;
310
		 return FALSE;
311
	      }
312
 
313
	      list = (garmin_list *)gmn->data;
314
	      /*
315
	       * This is the first data structure. It contains the total
316
	       * number of laps and the name of the course, if it was
317
	       * named.
318
	       */
319
	      if ((gdt = garmin_alloc_data (data_D1009)) == NULL)
320
	      {
321
		 __error = 6;
322
		 return FALSE;
323
	      }
324
 
325
	      memmove (gdt->data, &run, sizeof (D1009));
326
	      prun = (D1009 *)gdt->data;
327
 
328
	      if (ds)
329
		 ds->garmin_print_data (gdt);
330
 
331
	      if ((l = garmin_list_append (list, gdt)) == NULL)
332
	      {
333
		 __error = 7;
334
		 return FALSE;
335
	      }
336
 
337
	      list = l;
338
	   }
339
 
340
	   if (!list_lap)
341
	   {
342
	      if ((gmn_lap = garmin_alloc_data (data_Dlist)) == NULL)
343
	      {
344
		 __error = 3;
345
		 return FALSE;
346
	      }
347
 
348
	      list_lap = (garmin_list *)gmn_lap->data;
349
 
350
	      if (garmin_list_append (list, gmn_lap) == NULL)
351
	      {
352
		 __error = 7;
353
		 return FALSE;
354
	      }
355
 
356
	      list = list_lap;
357
	   }
358
	   else
359
	      list = list_lap;
360
 
361
	   if ((gdt = garmin_alloc_data (data_D1015)) == NULL)
362
	   {
363
	      __error = 4;
364
	      return FALSE;
365
	   }
366
 
367
	   memmove (gdt->data, &lap, sizeof (D1015));
368
 
369
	   if (ds)
370
	      ds->garmin_print_data (gdt);
371
 
372
	   if ((l = garmin_list_append (list, gdt)) == NULL)
373
	   {
374
	      __error = 7;
375
	      return FALSE;
376
	   }
377
 
378
	   list = l;
379
	}
380
 
381
	if (qName.lower() == QString("trackpoint"))
382
	{
383
	   garmin_data *gdt;
384
	   garmin_list *l;
385
 
386
	   con.setAscii("");
387
 
388
	   if (!gmn)		/* allocating space for first structure */
389
	   {
390
	      if ((gmn = garmin_alloc_data (data_Dlist)) == NULL)
391
	      {
392
		 __error = 3;
393
		 return FALSE;
394
	      }
395
 
396
	      list = (garmin_list *)gmn->data;
397
	      /*
398
	       * This is the first data structure. It contains the total
399
	       * number of laps and the name of the course, if it was
400
	       * named.
401
	       */
402
	      if ((gdt = garmin_alloc_data (data_D1009)) == NULL)
403
	      {
404
		 __error = 6;
405
		 return FALSE;
406
	      }
407
 
408
	      memmove (gdt->data, &run, sizeof (D1009));
409
	      prun = (D1009 *)gdt->data;
410
 
411
	      if (ds)
412
		 ds->garmin_print_data (gdt);
413
 
414
	      if ((l = garmin_list_append (list, gdt)) == NULL)
415
	      {
416
		 __error = 7;
417
		 return FALSE;
418
	      }
419
 
420
	      list = l;
421
	   }
422
 
423
	   if (!list_track)
424
	   {
425
	      if ((gmn_track = garmin_alloc_data (data_Dlist)) == NULL)
426
	      {
427
		 __error = 3;
428
		 return FALSE;
429
	      }
430
 
431
	      list_track = (garmin_list *)gmn_track->data;
432
 
433
	      if (garmin_list_append (list, gmn_track) == NULL)
434
	      {
435
		 __error = 7;
436
		 return FALSE;
437
	      }
438
 
439
	      list = list_track;
440
	   }
441
	   else
442
	      list = list_track;
443
 
444
	   if ((gdt = garmin_alloc_data (data_D304)) == NULL)
445
	   {
446
	      __error = 5;
447
	      return FALSE;
448
	   }
449
 
450
	   memmove (gdt->data, &point, sizeof (D304));
451
 
452
	   if (ds)
453
	      ds->garmin_print_data (gdt);
454
 
455
	   if ((l = garmin_list_append (list, gdt)) == NULL)
456
	   {
457
	      __error = 7;
458
	      return FALSE;
459
	   }
460
 
461
	   list = l;
462
	}
463
 
464
	if (qName.lower() == QString("course"))
465
	{
466
	   con.setAscii("");
467
	   run.track_index = tpos - 1;
468
	   run.last_lap_index = lpos - 1;
469
	   memmove (prun, &run, sizeof (D1009));
470
	}
471
 
472
	if (qName.lower() == QString("beginposition") || qName.lower() == QString("endposition") ||
473
	    qName.lower() == QString("averageheartratebpm") || qName.lower() == QString("maximumheartratebpm"))
474
	   subCon.setAscii("");
475
 
476
	if (qName.lower() == QString("heartratebpm") || qName.lower() == QString("position"))
477
	   subCon.setAscii("");
478
 
479
	if (indent < 0)
480
	{
481
	   __error = 2;
482
	   return FALSE;
483
	}
484
 
485
	return TRUE;
486
}
487
 
488
/*
489
 * The reader calls this function when it has parsed a chunk of character data
490
 * - either normal character data or character data inside a CDATA section.
491
 */
492
bool gmn_import::characters (const QString& ch)
493
{
494
int j = FLD_FIRST;
495
 
496
	if (con == QString("course"))
497
	{
498
	   if (tk == FLD_NAME)
499
	   {
500
	      strncpy (run.workout.name, ch.ascii(), 15);
501
	      run.workout.name[15] = 0;
502
	      tk = 0;
503
	   }
504
	}
505
 
506
	if (con == QString("lap"))
507
	{
508
	   if (tk == FLD_DISTANCEMETERS)
509
	   {
510
	      lap.total_dist = (float32)ch.toFloat();
511
	      tk = 0;
512
	   }
513
 
514
	   if (tk == FLD_INTENSITY)
515
	   {
516
	      lap.intensity = (ch.lower() == QString("activ")) ? 0 : 1;
517
	      tk = 0;
518
	   }
519
 
520
	   if (tk == FLD_STARTTIME)
521
	   {
522
	      lap.start_time = garmin_time (ch);
523
	      tk = 0;
524
	   }
525
 
526
	   if (tk == FLD_TOTALTIMESECONDS)
527
	   {
528
	      lap.total_time = (uint32)(ch.toDouble() * 100.0);
529
	      tk = 0;
530
	   }
531
 
532
	   if (tk == FLD_CADENCE)
533
	   {
534
	      lap.avg_cadence = (uint8)ch.toUInt();
535
	      tk = 0;
536
	   }
537
 
538
	   if (tk == FLD_CALORIES)
539
	   {
540
	      lap.calories = (uint16)ch.toUInt();
541
	      tk = 0;
542
	   }
543
 
544
	   if (tk == FLD_MAXSPEED)
545
	   {
546
	      lap.max_speed = (float32)ch.toFloat();
547
	      tk = 0;
548
	   }
549
 
550
	   if (subCon.lower() == QString("BeginPosition").lower())
551
	   {
552
	      if (tk == FLD_LATITUDEDEGREES)
553
	      {
554
		 lap.begin.lat = (ch.toDouble() == 180.0) ? 0x7fffffff : (sint32)DEG2SEMI(ch.toDouble());
555
		 tk = 0;
556
	      }
557
 
558
	      if (tk == FLD_LONGITUDEDEGREES)
559
	      {
560
		 lap.begin.lon = (ch.toDouble() == 180.0) ? 0x7fffffff : (sint32)DEG2SEMI(ch.toDouble());
561
		 tk = 0;
562
	      }
563
	   }
564
	   else if (subCon.lower() == QString("EndPosition").lower())
565
	   {
566
	      if (tk == FLD_LATITUDEDEGREES)
567
	      {
568
		 lap.end.lat = (ch.toDouble() == 180.0) ? 0x7fffffff : (sint32)DEG2SEMI(ch.toDouble());
569
		 tk = 0;
570
	      }
571
 
572
	      if (tk == FLD_LONGITUDEDEGREES)
573
	      {
574
		 lap.end.lon = (ch.toDouble() == 180.0) ? 0x7fffffff : (sint32)DEG2SEMI(ch.toDouble());
575
		 tk = 0;
576
	      }
577
	   }
578
	   else if (subCon.lower() == QString("AverageHeartRateBpm").lower())
579
	   {
580
	      if (tk == FLD_VALUE)
581
	      {
582
		 lap.avg_heart_rate = (uint8)ch.toInt();
583
		 tk = 0;
584
	      }
585
	   }
586
	   else if (subCon.lower() == QString("MaximumHeartRateBpm").lower())
587
	   {
588
	      if (tk == FLD_VALUE)
589
	      {
590
		 lap.max_heart_rate = (uint8)ch.toInt();
591
		 tk = 0;
592
	      }
593
	   }
594
	}
595
 
596
	if (con == QString("trackpoint"))
597
	{
598
	   if (tk == FLD_TIME)
599
	   {
600
	      point.time = garmin_time (ch);
601
	      tk = 0;
602
	   }
603
 
604
	   if (tk == FLD_ALTITUDEMETERS)
605
	   {
606
	      point.alt = (ch.toFloat() >= 1.0e24) ? 1.0e24 : (float32)ch.toFloat();
607
	      tk = 0;
608
	   }
609
 
610
	   if (tk == FLD_DISTANCEMETERS)
611
	   {
612
	      point.distance = (ch.toFloat() >= 1.0e24) ? 1.0e24 : (float32)ch.toFloat();
613
	      tk = 0;
614
	   }
615
 
616
	   if (tk == FLD_SENSORSTATE)
617
	   {
618
	      point.sensor = (ch.lower() == QString("absent")) ? false : true;
619
	      tk = 0;
620
	   }
621
 
622
	   if (subCon.lower() == QString("position"))
623
	   {
624
	      if (tk == FLD_LATITUDEDEGREES)
625
	      {
626
		 point.posn.lat = (ch.toDouble() == 180.0) ? 0x7fffffff : (sint32)DEG2SEMI(ch.toDouble());
627
		 tk = 0;
628
	      }
629
 
630
	      if (tk == FLD_LONGITUDEDEGREES)
631
	      {
632
		 point.posn.lon = (ch.toDouble() == 180.0) ? 0x7fffffff : (sint32)DEG2SEMI(ch.toDouble());
633
		 tk = 0;
634
	      }
635
	   }
636
	   else if (subCon.lower() == QString("heartratebpm"))
637
	   {
638
	      if (tk == FLD_VALUE)
639
	      {
640
		 point.heart_rate = (unsigned char)ch.toInt();
641
		 tk = 0;
642
	      }
643
	   }
644
	}
645
 
646
	return TRUE;
647
}
648
 
649
/*
650
 * The following function reads a Garmin HST file, parses the XML
651
 * content and creates the gmn files. If there already exists a file,
652
 * it's not overwritten.
653
 */
654
int gmn_import::import ()
655
{
656
QXmlSimpleReader reader;
657
 
658
	if (!qfstat)
659
	   return 1;
660
 
661
	QXmlInputSource source (&file);
662
	reader.setContentHandler (this);
663
	reader.parse (source);
664
	return 0;
665
}
666
 
667
QString gmn_import::getKey (int pos)
668
{
669
int i = 0;
670
 
671
	while (keys[i].id > 0)
672
	{
673
	   if (keys[i].id == pos)
674
	      return keys[i].name;
675
 
676
	   i++;
677
	}
678
 
679
	return QString::null;
680
}
681
 
682
QString gmn_import::getError (int err)
683
{
684
	if (err > eMax || err < 1)
685
	   return 0;
686
 
687
	return getKey (ERR_FIRST + err);
688
}
689
 
690
QString gmn_import::getError ()
691
{
692
	return getKey (ERR_FIRST + __error);
693
}
694