fit4ruby 3.5.0 → 3.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -23,7 +23,50 @@ module Fit4Ruby
23
23
  entry 3, 'transition'
24
24
  entry 4, 'fitness_equipment'
25
25
  entry 5, 'swimming'
26
- entry 6, 'walking'
26
+ entry 6, 'basketball'
27
+ entry 7, 'soccer'
28
+ entry 8, 'tennis'
29
+ entry 9, 'american_football'
30
+ entry 10, 'training'
31
+ entry 11, 'walking'
32
+ entry 12, 'cross_country_skiing'
33
+ entry 13, 'alpine_skiing'
34
+ entry 14, 'snowboarding'
35
+ entry 15, 'rowing'
36
+ entry 16, 'mountaineering'
37
+ entry 17, 'hiking'
38
+ entry 18, 'multisport'
39
+ entry 19, 'paddling'
40
+ entry 20, 'flying'
41
+ entry 21, 'e_biking'
42
+ entry 22, 'motorcycling'
43
+ entry 23, 'boating'
44
+ entry 24, 'driving'
45
+ entry 25, 'golf'
46
+ entry 26, 'hang_gliding'
47
+ entry 27, 'horseback_riding'
48
+ entry 28, 'hunting'
49
+ entry 29, 'fishing'
50
+ entry 30, 'inline_skating'
51
+ entry 31, 'rock_climbing'
52
+ entry 32, 'sailing'
53
+ entry 33, 'ice_skating'
54
+ entry 34, 'sky_diving'
55
+ entry 35, 'snowshoeing'
56
+ entry 36, 'snowmobiling'
57
+ entry 37, 'stand_up_paddleboarding'
58
+ entry 38, 'surfing'
59
+ entry 39, 'wakeboarding'
60
+ entry 40, 'water_skiing'
61
+ entry 41, 'kayaking'
62
+ entry 42, 'rafting'
63
+ entry 43, 'windsurfing'
64
+ entry 44, 'kitesurfing'
65
+ entry 45, 'tactical'
66
+ entry 46, 'jumpmaster'
67
+ entry 47, 'boxing'
68
+ entry 48, 'floor_climbing'
69
+ entry 53, 'diving'
27
70
  entry 254, 'all'
28
71
 
29
72
  dict 'activity_level'
@@ -51,6 +94,52 @@ module Fit4Ruby
51
94
  entry 16, 'stair_climbing'
52
95
  entry 17, 'lap_swimming'
53
96
  entry 18, 'open_water'
97
+ entry 19, 'flexibility_training'
98
+ entry 20, 'strength_training'
99
+ entry 21, 'warm_up'
100
+ entry 22, 'match'
101
+ entry 23, 'exercise'
102
+ entry 24, 'challenge'
103
+ entry 25, 'indoor_skiing'
104
+ entry 26, 'cardio_training'
105
+ entry 27, 'indoor_walking'
106
+ entry 28, 'e_bike_fitness'
107
+ entry 29, 'bmx'
108
+ entry 30, 'casual_walking'
109
+ entry 31, 'speed_walking'
110
+ entry 32, 'bike_to_run_transition'
111
+ entry 33, 'run_to_bike_transition'
112
+ entry 34, 'swim_to_bike_transition'
113
+ entry 35, 'atv'
114
+ entry 36, 'motocross'
115
+ entry 37, 'backcountry'
116
+ entry 38, 'resort'
117
+ entry 39, 'rc_drone'
118
+ entry 40, 'wingsuit'
119
+ entry 41, 'whitewater'
120
+ entry 42, 'skate_skiing'
121
+ entry 43, 'yoga'
122
+ entry 44, 'pilates'
123
+ entry 45, 'indoor_running'
124
+ entry 46, 'gravel_cycling'
125
+ entry 47, 'e_bike_mountain'
126
+ entry 48, 'commuting'
127
+ entry 49, 'mixed_surface'
128
+ entry 50, 'navigate'
129
+ entry 51, 'track_me'
130
+ entry 52, 'map'
131
+ entry 53, 'single_gas_diving'
132
+ entry 54, 'multi_gas_diving'
133
+ entry 55, 'gauge_diving'
134
+ entry 56, 'apnea_diving'
135
+ entry 57, 'apnea_hunting'
136
+ entry 58, 'virtual_activity'
137
+ entry 59, 'obstacle'
138
+ entry 62, 'breathing'
139
+ entry 65, 'sail_race'
140
+ entry 67, 'ultra'
141
+ entry 68, 'indoor_climbing'
142
+ entry 69, 'bouldering'
54
143
  entry 254, 'all'
55
144
 
56
145
  dict 'ant_network'
@@ -312,52 +401,306 @@ module Fit4Ruby
312
401
  entry 144, 'uint64z'
313
402
 
314
403
  dict 'garmin_product'
404
+ entry 1, 'hrm1'
405
+ entry 2, 'axh01'
406
+ entry 3, 'axb01'
407
+ entry 4, 'axb02'
408
+ entry 5, 'hrm2ss'
409
+ entry 6, 'dsi_alf02'
410
+ entry 7, 'hrm3ss'
315
411
  entry 8, 'hrm_run_single_byte_product_id'
316
412
  entry 9, 'bsm'
317
413
  entry 10, 'bcm'
414
+ entry 11, 'axs01'
415
+ entry 12, 'hrm_tri_single_byte_product_id'
416
+ entry 13, 'hrm4_run_single_byte_product_id'
417
+ entry 14, 'fr225_single_byte_product_id'
418
+ entry 473, 'fr301_china'
419
+ entry 474, 'fr301_japan'
420
+ entry 475, 'fr301_korea'
421
+ entry 494, 'fr301_taiwan'
422
+ entry 717, 'fr405'
423
+ entry 782, 'fr50'
424
+ entry 987, 'fr405_japan'
425
+ entry 988, 'fr60'
426
+ entry 1011, 'dsi_alf01'
427
+ entry 1018, 'fr310xt'
428
+ entry 1036, 'edge500'
429
+ entry 1124, 'fr110'
430
+ entry 1169, 'edge800'
431
+ entry 1199, 'edge500_taiwan'
432
+ entry 1213, 'edge500_japan'
433
+ entry 1253, 'chirp'
434
+ entry 1274, 'fr110_japan'
435
+ entry 1325, 'edge200'
436
+ entry 1328, 'fr910xt'
437
+ entry 1333, 'edge800_taiwan'
438
+ entry 1334, 'edge800_japan'
439
+ entry 1341, 'alf04'
440
+ entry 1345, 'fr610'
441
+ entry 1360, 'fr210_japan'
442
+ entry 1380, 'vector_ss'
443
+ entry 1381, 'vector_cp'
444
+ entry 1386, 'edge800_china'
445
+ entry 1387, 'edge500_china'
446
+ entry 1405, 'approach_g10'
447
+ entry 1410, 'fr610_japan'
448
+ entry 1422, 'edge500_korea'
449
+ entry 1436, 'fr70'
450
+ entry 1446, 'fr310xt_4t'
451
+ entry 1461, 'amx'
452
+ entry 1482, 'fr10'
453
+ entry 1497, 'edge800_korea'
454
+ entry 1499, 'swim'
455
+ entry 1537, 'fr910xt_china'
318
456
  entry 1551, 'fenix'
457
+ entry 1555, 'edge200_taiwan'
458
+ entry 1561, 'edge510'
459
+ entry 1567, 'edge810'
460
+ entry 1570, 'tempe'
461
+ entry 1600, 'fr910xt_japan'
319
462
  # The Fenix3 is rumored to have a Mediatek MT3333 GPS chipset. Not sure if
320
463
  # that would be a beter name.
321
464
  entry 1620, 'fenix3_gps' # Just a guess
322
465
  entry 1621, 'fenix5_gps' # Just a guess
323
466
  entry 1623, 'fr620'
324
467
  entry 1632, 'fr220'
468
+ entry 1664, 'fr910xt_korea'
469
+ entry 1688, 'fr10_japan'
325
470
  # The FR620 is rumored to have a MediaTek MT3339 GPS chipset while the
326
471
  # FR920XT is rumored to have a MT3333. Not sure why they have the same ID
327
472
  # in the FIT file for the GPS device.
328
473
  entry 1689, 'fr620_fr920xt_gps' # Just a guess
474
+ entry 1721, 'edge810_japan'
475
+ entry 1735, 'virb_elite'
476
+ entry 1736, 'edge_touring'
477
+ entry 1742, 'edge510_japan'
329
478
  entry 1743, 'hrm_tri'
330
479
  entry 1752, 'hrm_run'
331
480
  entry 1765, 'fr920xt'
481
+ entry 1821, 'edge510_asia'
482
+ entry 1822, 'edge810_china'
483
+ entry 1823, 'edge810_taiwan'
484
+ entry 1836, 'edge1000'
485
+ entry 1837, 'vivo_fit'
486
+ entry 1853, 'virb_remote'
487
+ entry 1885, 'vivo_ki'
488
+ entry 1903, 'fr15'
489
+ entry 1907, 'vivo_active'
490
+ entry 1918, 'edge510_korea'
332
491
  entry 1928, 'fr620_japan'
333
492
  entry 1929, 'fr620_china'
334
493
  entry 1930, 'fr220_japan'
335
494
  entry 1931, 'fr220_china'
495
+ entry 1936, 'approach_s6'
496
+ entry 1956, 'vivo_smart'
336
497
  entry 1967, 'fenix2'
337
498
  entry 1988, 'epix'
338
499
  entry 2050, 'fenix3'
500
+ entry 2052, 'edge1000_taiwan'
501
+ entry 2053, 'edge1000_japan'
502
+ entry 2061, 'fr15_japan'
503
+ entry 2067, 'edge520'
504
+ entry 2070, 'edge1000_china'
339
505
  entry 2072, 'fr620_russia'
340
506
  entry 2073, 'fr220_russia'
507
+ entry 2079, 'vector_s'
508
+ entry 2100, 'edge1000_korea'
341
509
  entry 2130, 'fr920xt_taiwan'
342
510
  entry 2131, 'fr920xt_china'
343
511
  entry 2132, 'fr920xt_japan'
512
+ entry 2134, 'virbx'
513
+ entry 2135, 'vivo_smart_apac'
514
+ entry 2140, 'etrex_touch'
515
+ entry 2147, 'edge25'
516
+ entry 2148, 'fr25'
517
+ entry 2150, 'vivo_fit2'
518
+ entry 2153, 'fr225'
519
+ entry 2156, 'fr630'
520
+ entry 2157, 'fr230'
521
+ entry 2158, 'fr735xt'
522
+ entry 2160, 'vivo_active_apac'
523
+ entry 2161, 'vector_2'
524
+ entry 2162, 'vector_2s'
525
+ entry 2172, 'virbxe'
344
526
  entry 2173, 'fr620_taiwan'
527
+ entry 2174, 'fr220_taiwan'
528
+ entry 2175, 'truswing'
345
529
  entry 2188, 'fenix3_china'
346
530
  entry 2189, 'fenix3_twn'
531
+ entry 2192, 'varia_headlight'
532
+ entry 2193, 'varia_taillight_old'
533
+ entry 2204, 'edge_explore_1000'
534
+ entry 2219, 'fr225_asia'
535
+ entry 2225, 'varia_radar_taillight'
536
+ entry 2226, 'varia_radar_display'
537
+ entry 2238, 'edge20'
538
+ entry 2260, 'edge520_asia'
539
+ entry 2261, 'edge520_japan'
540
+ entry 2262, 'd2_bravo'
541
+ entry 2266, 'approach_s20'
542
+ entry 2271, 'vivo_smart2'
543
+ entry 2274, 'edge1000_thai'
544
+ entry 2276, 'varia_remote'
545
+ entry 2288, 'edge25_asia'
546
+ entry 2289, 'edge25_jpn'
547
+ entry 2290, 'edge20_asia'
548
+ entry 2292, 'approach_x40'
549
+ entry 2293, 'fenix3_japan'
550
+ entry 2294, 'vivo_smart_emea'
551
+ entry 2310, 'fr630_asia'
552
+ entry 2311, 'fr630_jpn'
553
+ entry 2313, 'fr230_jpn'
347
554
  entry 2327, 'hrm4_run'
555
+ entry 2332, 'epix_japan'
556
+ entry 2337, 'vivo_active_hr'
557
+ entry 2347, 'vivo_smart_gps_hr'
558
+ entry 2348, 'vivo_smart_hr'
559
+ entry 2361, 'vivo_smart_hr_asia'
560
+ entry 2362, 'vivo_smart_gps_hr_asia'
561
+ entry 2368, 'vivo_move'
562
+ entry 2379, 'varia_taillight'
563
+ entry 2397, 'fr235_japan'
564
+ entry 2398, 'varia_vision'
565
+ entry 2406, 'vivo_fit3'
566
+ entry 2407, 'fenix3_korea'
567
+ entry 2408, 'fenix3_sea'
348
568
  entry 2413, 'fenix3_hr'
569
+ entry 2417, 'virb_ultra_30'
570
+ entry 2429, 'index_smart_scale'
349
571
  entry 2431, 'fr235'
350
572
  entry 2432, 'fenix3_chronos'
573
+ entry 2441, 'oregon7xx'
574
+ entry 2444, 'rino7xx'
575
+ entry 2457, 'epix_korea'
576
+ entry 2473, 'fenix3_hr_chn'
577
+ entry 2474, 'fenix3_hr_twn'
578
+ entry 2475, 'fenix3_hr_jpn'
579
+ entry 2476, 'fenix3_hr_sea'
580
+ entry 2477, 'fenix3_hr_kor'
581
+ entry 2496, 'nautix'
582
+ entry 2497, 'vivo_active_hr_apac'
583
+ entry 2512, 'oregon7xx_ww'
584
+ entry 2530, 'edge_820'
585
+ entry 2531, 'edge_explore_820'
586
+ entry 2533, 'fr735xt_apac'
587
+ entry 2534, 'fr735xt_japan'
351
588
  entry 2544, 'fenix5s'
352
- entry 2604, 'fenix5x'
589
+ entry 2547, 'd2_bravo_titanium'
590
+ entry 2567, 'varia_ut800'
353
591
  entry 2593, 'running_dynamics_pod'
592
+ entry 2599, 'edge_820_china'
593
+ entry 2600, 'edge_820_japan'
594
+ entry 2604, 'fenix5x'
595
+ entry 2606, 'vivo_fit_jr'
596
+ entry 2622, 'vivo_smart3'
597
+ entry 2623, 'vivo_sport'
598
+ entry 2628, 'edge_820_taiwan'
599
+ entry 2629, 'edge_820_korea'
600
+ entry 2630, 'edge_820_sea'
601
+ entry 2650, 'fr35_hebrew'
602
+ entry 2656, 'approach_s60'
603
+ entry 2667, 'fr35_apac'
604
+ entry 2668, 'fr35_japan'
605
+ entry 2675, 'fenix3_chronos_asia'
606
+ entry 2687, 'virb_360'
354
607
  entry 2691, 'fr935'
355
608
  entry 2697, 'fenix5'
609
+ entry 2700, 'vivoactive3'
610
+ entry 2733, 'fr235_china_nfc'
611
+ entry 2769, 'foretrex_601_701'
612
+ entry 2772, 'vivo_move_hr'
613
+ entry 2713, 'edge_1030'
614
+ entry 2796, 'fenix5_asia'
615
+ entry 2797, 'fenix5s_asia'
616
+ entry 2798, 'fenix5x_asia'
617
+ entry 2806, 'approach_z80'
618
+ entry 2814, 'fr35_korea'
619
+ entry 2819, 'd2charlie'
620
+ entry 2831, 'vivo_smart3_apac'
621
+ entry 2832, 'vivo_sport_apac'
622
+ entry 2833, 'fr935_asia'
623
+ entry 2859, 'descent'
624
+ entry 2886, 'fr645'
625
+ entry 2888, 'fr645m'
626
+ entry 2891, 'fr30'
627
+ entry 2900, 'fenix5s_plus'
628
+ entry 2909, 'Edge_130'
629
+ entry 2924, 'edge_1030_asia'
630
+ entry 2927, 'vivosmart_4'
631
+ entry 2945, 'vivo_move_hr_asia'
356
632
  entry 2957, 'fenix5_gps_galileo' # Just a guess
633
+ entry 2962, 'approach_x10'
634
+ entry 2977, 'fr30_asia'
635
+ entry 2988, 'vivoactive3m_w'
636
+ entry 3003, 'fr645_asia'
637
+ entry 3004, 'fr645m_asia'
638
+ entry 3011, 'edge_explore'
639
+ entry 3028, 'gpsmap66'
640
+ entry 3049, 'approach_s10'
641
+ entry 3066, 'vivoactive3m_l'
642
+ entry 3085, 'approach_g80'
643
+ entry 3092, 'edge_130_asia'
644
+ entry 3095, 'edge_1030_bontrager'
645
+ entry 3110, 'fenix5_plus'
357
646
  entry 3111, 'fenix5x_plus'
358
- entry 3126, 'instinct'
647
+ entry 3112, 'edge_520_plus'
648
+ entry 3121, 'edge_530'
649
+ entry 3122, 'edge_830'
650
+ entry 3134, 'fenix5s_plus_apac'
651
+ entry 3135, 'fenix5x_plus_apac'
652
+ entry 3142, 'edge_520_plus_apac'
653
+ entry 3144, 'fr235l_asia'
654
+ entry 3145, 'fr245_asia'
655
+ entry 3163, 'vivo_active3m_apac'
656
+ entry 3218, 'vivo_smart4_asia'
657
+ entry 3224, 'vivoactive4_small'
658
+ entry 3225, 'vivoactive4_large'
659
+ entry 3226, 'venu'
660
+ entry 3246, 'marq_driver'
661
+ entry 3247, 'marq_aviator'
662
+ entry 3248, 'marq_captain'
663
+ entry 3249, 'marq_commander'
664
+ entry 3250, 'marq_expedition'
665
+ entry 3251, 'marq_athlete'
666
+ entry 3287, 'fenix6S_sport'
667
+ entry 3288, 'fenix6S'
668
+ entry 3289, 'fenix6_sport'
669
+ entry 3290, 'fenix6'
359
670
  entry 3291, 'fenix6x'
671
+ entry 3299, 'hrm_dual'
672
+ entry 3308, 'vivo_move3_premium'
673
+ entry 3314, 'approach_s40'
674
+ entry 3321, 'fr245m_asia'
675
+ entry 3349, 'edge_530_apac'
676
+ entry 3350, 'edge_830_apac'
677
+ entry 3378, 'vivo_move3'
678
+ entry 3387, 'vivo_active4_small_asia'
679
+ entry 3388, 'vivo_active4_large_asia'
680
+ entry 3389, 'vivo_active4_oled_asia'
681
+ entry 3405, 'swim2'
682
+ entry 3420, 'marq_driver_asia'
683
+ entry 3421, 'marq_aviator_asia'
684
+ entry 3422, 'vivo_move3_asia'
685
+ entry 3446, 'vivo_active3t_chn'
686
+ entry 3448, 'marq_captain_asia'
687
+ entry 3449, 'marq_commander_asia'
688
+ entry 3450, 'marq_expedition_asia'
689
+ entry 3451, 'marq_athlete_asia'
690
+ entry 3469, 'fr45_asia'
691
+ entry 3473, 'vivoactive3_daimler'
692
+ entry 3512, 'fenix6s_sport_asia'
693
+ entry 3513, 'fenix6s_asia'
694
+ entry 3514, 'fenix6_sport_asia'
695
+ entry 3515, 'fenix6_asia'
696
+ entry 3516, 'fenix6x_asia'
697
+ entry 3624, 'marq_adventurer'
698
+ entry 3648, 'marq_adventurer_asia'
699
+ entry 3639, 'swim2_apac'
700
+ entry 3737, 'venu_daimler_asia'
701
+ entry 3740, 'venu_daimler'
360
702
  entry 10007, 'sdm4'
703
+ entry 10014, 'edge_remote'
361
704
  entry 20119, 'training_center'
362
705
  entry 65532, 'android_antplus_plugin'
363
706
  entry 65534, 'connect'
@@ -629,39 +972,39 @@ module Fit4Ruby
629
972
  entry 24, 'challenge'
630
973
  entry 25, 'indoor_skiing'
631
974
  entry 26, 'cardio_training'
632
- entry 27, 'indoor_walking '
633
- entry 28, 'e_bike_fitness '
975
+ entry 27, 'indoor_walking'
976
+ entry 28, 'e_bike_fitness'
634
977
  entry 29, 'bmx'
635
- entry 30, 'casual_walking '
978
+ entry 30, 'casual_walking'
636
979
  entry 31, 'speed_walking'
637
- entry 32, 'bike_to_run_transition '
638
- entry 33, 'run_to_bike_transition '
980
+ entry 32, 'bike_to_run_transition'
981
+ entry 33, 'run_to_bike_transition'
639
982
  entry 34, 'swim_to_bike_transition'
640
983
  entry 35, 'atv'
641
984
  entry 36, 'motocross'
642
985
  entry 37, 'backcountry'
643
- entry 38, 'resort '
644
- entry 39, 'rc_drone '
645
- entry 40, 'wingsuit '
646
- entry 41, 'whitewater '
647
- entry 42, 'skate_skiing '
648
- entry 43, 'yoga '
986
+ entry 38, 'resort'
987
+ entry 39, 'rc_drone'
988
+ entry 40, 'wingsuit'
989
+ entry 41, 'whitewater'
990
+ entry 42, 'skate_skiing'
991
+ entry 43, 'yoga'
649
992
  entry 44, 'pilates'
650
- entry 45, 'indoor_running '
651
- entry 46, 'gravel_cycling '
993
+ entry 45, 'indoor_running'
994
+ entry 46, 'gravel_cycling'
652
995
  entry 47, 'e_bike_mountain'
653
996
  entry 48, 'commuting'
654
997
  entry 49, 'mixed_surface'
655
- entry 50, 'navigate '
656
- entry 51, 'track_me '
998
+ entry 50, 'navigate'
999
+ entry 51, 'track_me'
657
1000
  entry 52, 'map'
658
1001
  entry 53, 'single_gas_diving'
659
- entry 54, 'multi_gas_diving '
660
- entry 55, 'gauge_diving '
661
- entry 56, 'apnea_diving '
1002
+ entry 54, 'multi_gas_diving'
1003
+ entry 55, 'gauge_diving'
1004
+ entry 56, 'apnea_diving'
662
1005
  entry 57, 'apnea_hunting'
663
- entry 58, 'virtual_activity '
664
- entry 59, 'obstacle '
1006
+ entry 58, 'virtual_activity'
1007
+ entry 59, 'obstacle'
665
1008
  entry 254, 'all'
666
1009
 
667
1010
  dict 'training_status'
@@ -3,7 +3,7 @@
3
3
  #
4
4
  # = GlobalFitMessage.rb -- Fit4Ruby - FIT file processing library for Ruby
5
5
  #
6
- # Copyright (c) 2014, 2015 by Chris Schlaeger <cs@taskjuggler.org>
6
+ # Copyright (c) 2014, 2015, 2020 by Chris Schlaeger <cs@taskjuggler.org>
7
7
  #
8
8
  # This program is free software; you can redistribute it and/or modify
9
9
  # it under the terms of version 2 of the GNU General Public License as
@@ -21,7 +21,8 @@ module Fit4Ruby
21
21
  # the data fields of the message.
22
22
  class GlobalFitMessage
23
23
 
24
- attr_reader :name, :number, :fields_by_name, :fields_by_number
24
+ attr_reader :name, :number, :fields_by_name, :fields_by_number,
25
+ :field_values_by_name
25
26
 
26
27
  # The Field objects describe the name, type and optional attributes of a
27
28
  # FitMessage definition field. It also provides methods to convert field
@@ -38,8 +39,21 @@ module Fit4Ruby
38
39
  @opts = opts
39
40
  end
40
41
 
42
+ def is_undefined?(value)
43
+ value == FitDefinitionFieldBase.undefined_value(@type)
44
+ end
45
+
46
+ def is_string?
47
+ @type == 'string'
48
+ end
49
+
50
+ def is_array?
51
+ @opts[:array] == true
52
+ end
53
+
41
54
  def to_machine(value)
42
- return nil if value.nil?
55
+ return nil if value.nil? ||
56
+ value == FitDefinitionFieldBase.undefined_value(@type)
43
57
 
44
58
  if @opts.include?(:dict) &&
45
59
  (dict = GlobalFitDictionaries[@opts[:dict]])
@@ -51,6 +65,13 @@ module Fit4Ruby
51
65
  value *= 180.0 / 2147483648
52
66
  when 'date_time'
53
67
  value = fit_time_to_time(value)
68
+ when 'float'
69
+ if value >= 4294967295.0
70
+ return nil
71
+ end
72
+ end
73
+ if value.is_a?(Float) && value >= 4294967295.0
74
+ return nil
54
75
  end
55
76
  value /= @opts[:scale].to_f if @opts[:scale]
56
77
  value -= @opts[:offset] if @opts[:offset]
@@ -133,7 +154,7 @@ module Fit4Ruby
133
154
  value
134
155
  end
135
156
 
136
- def to_s(value)
157
+ def to_s(value = nil)
137
158
  return "[no value]" if value.nil?
138
159
 
139
160
  human_readable = to_human(value)
@@ -167,7 +188,7 @@ module Fit4Ruby
167
188
 
168
189
  def field(ref_value, type, name, opts = {})
169
190
  field = Field.new(type, name, opts)
170
- if ref_value.respond_to?('each')
191
+ if ref_value.respond_to?(:each)
171
192
  ref_value.each do |rv|
172
193
  @fields[rv] = field
173
194
  end
@@ -180,13 +201,13 @@ module Fit4Ruby
180
201
  # Select the alternative field based on the actual field values of the
181
202
  # FitMessageRecord.
182
203
  def select(field_values_by_name)
183
- unless (value = field_values_by_name[@ref_field])
204
+ unless (value_of_referenced_field = field_values_by_name[@ref_field])
184
205
  Log.fatal "The selection field #{@ref_field} for the alternative " +
185
206
  "field is undefined in global message #{@message.name}: " +
186
207
  field_values_by_name.inspect
187
208
  end
188
209
  @fields.each do |ref_value, field|
189
- return field if ref_value == value
210
+ return field if ref_value == value_of_referenced_field
190
211
  end
191
212
  return @fields[:default] if @fields[:default]
192
213
 
@@ -199,7 +220,7 @@ module Fit4Ruby
199
220
  # Create a new GlobalFitMessage definition.
200
221
  # @param name [String] name of the FIT message
201
222
  # @param number [Fixnum] global message number
202
- def initialize(name, number)
223
+ def initialize(name, number, field_values_by_name = {})
203
224
  @name = name
204
225
  @number = number
205
226
  # Field names must be unique. A name always matches a single Field.
@@ -207,13 +228,57 @@ module Fit4Ruby
207
228
  # Field numbers are not unique. A group of alternative fields shares the
208
229
  # same number and is stored as an AltField. Otherwise as Field.
209
230
  @fields_by_number = {}
231
+ # To generate the proper definition message we need to know the length
232
+ # of String and Array fields. This is only needed when writing FIT
233
+ # files.
234
+ @field_values_by_name = field_values_by_name
210
235
  end
211
236
 
212
237
  # Two GlobalFitMessage objects are considered equal if they have the same
213
- # number, name and list of named fields.
238
+ # number, name and list of named fields. In case they have String or Array
239
+ # values, they must have identical size.
214
240
  def ==(m)
215
- @number == m.number && @name == m.name &&
216
- @fields_by_name.keys.sort == m.fields_by_name.keys.sort
241
+ unless @number == m.number && @name == m.name &&
242
+ @fields_by_name.keys.sort == m.fields_by_name.keys.sort
243
+ return false
244
+ end
245
+
246
+ unless @field_values_by_name.size == m.field_values_by_name.size
247
+ return false
248
+ end
249
+
250
+ unless @field_values_by_name.empty?
251
+ @field_values_by_name.keys.each do |name|
252
+ a = @field_values_by_name[name]
253
+ b = m.field_values_by_name[name]
254
+ if a.class != b.class
255
+ return false
256
+ end
257
+ if a.is_a?(String)
258
+ if a.bytes.length != b.bytes.length
259
+ return false
260
+ end
261
+ elsif a.is_a?(Array)
262
+ if a.length != b.length
263
+ return false
264
+ end
265
+ end
266
+ end
267
+ end
268
+
269
+ true
270
+ end
271
+
272
+ def each_field(field_values)
273
+ @fields_by_number.each do |number, field|
274
+ if field.is_a?(AltField)
275
+ # For alternative fields, we need to look at the value of the
276
+ # selector field and pick the corresponding Field.
277
+ field = field.select(field_values)
278
+ end
279
+
280
+ yield(number, field)
281
+ end
217
282
  end
218
283
 
219
284
  # Define a new Field for this message definition.
@@ -223,6 +288,11 @@ module Fit4Ruby
223
288
  register_field_by_number(field, number)
224
289
  end
225
290
 
291
+ # Check if a field with the given name already exists?
292
+ def has_field?(name)
293
+ return @fields_by_name.include?(name)
294
+ end
295
+
226
296
  # Define a new set of Field alternatives for this message definition.
227
297
  def alt_field(number, ref_field, &block)
228
298
  unless @fields_by_name.include?(ref_field)
@@ -250,7 +320,7 @@ module Fit4Ruby
250
320
  end
251
321
 
252
322
  def construct(field_values_by_name)
253
- gfm = GlobalFitMessage.new(@name, @number)
323
+ gfm = GlobalFitMessage.new(@name, @number, field_values_by_name)
254
324
 
255
325
  @fields_by_number.each do |number, field|
256
326
  if field.is_a?(AltField)
@@ -264,7 +334,7 @@ module Fit4Ruby
264
334
  gfm
265
335
  end
266
336
 
267
- def write(io, local_message_type, global_fit_message = self)
337
+ def write(io, local_message_type)
268
338
  header = FitRecordHeader.new
269
339
  header.normal = 0
270
340
  header.message_type = 1
@@ -273,7 +343,7 @@ module Fit4Ruby
273
343
 
274
344
  definition = FitDefinition.new
275
345
  definition.global_message_number = @number
276
- definition.setup(global_fit_message)
346
+ definition.setup(self, @field_values_by_name)
277
347
  definition.write(io)
278
348
  end
279
349