postrunner 0.11.0 → 1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +36 -12
- data/lib/postrunner/Activity.rb +1 -1
- data/lib/postrunner/ActivitySummary.rb +11 -1
- data/lib/postrunner/ActivityView.rb +2 -1
- data/lib/postrunner/ChartView.rb +55 -4
- data/lib/postrunner/DailyMonitoringView.rb +1 -1
- data/lib/postrunner/DeviceList.rb +21 -0
- data/lib/postrunner/EventList.rb +12 -2
- data/lib/postrunner/FFS_Activity.rb +1 -1
- data/lib/postrunner/FFS_Device.rb +3 -2
- data/lib/postrunner/FitFileStore.rb +76 -40
- data/lib/postrunner/Main.rb +10 -4
- data/lib/postrunner/MonitoringStatistics.rb +8 -4
- data/lib/postrunner/UserProfileView.rb +20 -8
- data/lib/postrunner/version.rb +2 -2
- data/misc/jquery/jquery-3.5.1.min.js +2 -0
- data/misc/openlayers/ol.css +2 -1
- data/misc/openlayers/ol.js +9 -948
- data/postrunner.gemspec +5 -5
- data/spec/FitFileStore_spec.rb +7 -7
- metadata +16 -19
- data/misc/flot/jquery.js +0 -9472
- data/misc/flot/jquery.min.js +0 -5
- data/misc/jquery/jquery-2.1.1.min.js +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 49a5f5f39dfa3c4aa542ad58f086e6c6a193420274ef291c4a1f5b009665734b
|
4
|
+
data.tar.gz: cccd8e865768fbbd7d7266e4579753ac8ee261f0a35b4551b291e03f4e4684ee
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 05b6cb51addbb4f366e9779e3d69c3307078b64a0256b82833a8016dcdee64ee4b2347038ba7f8921cd0a7fe530913d6d3eee1b48258935adf71f9ff06c7ec35
|
7
|
+
data.tar.gz: 075c12e07e651813fa59fefb15d4702510679dba7cbe23aaed7f27400655c72e68f913343820b631b514e6a03e3dfde4b9269441fabfe75bdcd5d51f567907db
|
data/README.md
CHANGED
@@ -2,26 +2,50 @@
|
|
2
2
|
|
3
3
|
PostRunner is an application to manage FIT files such as those
|
4
4
|
produced by Garmin products like the Forerunner 620 (FR620),
|
5
|
-
Forerunner 25 (FR25), Fenix 3, Fenix 3HR
|
6
|
-
import the files from the device and analyze the
|
7
|
-
the common features like plotting pace, heart
|
8
|
-
other captured values it also provides a heart
|
9
|
-
analysis. It can also update satellite orbit
|
10
|
-
the device to speed-up GPS fix times.
|
11
|
-
|
12
|
-
|
5
|
+
Forerunner 25 (FR25), Fenix 3, Fenix 3HR, Fenix 5, Fenix 5+ or Fenix6.
|
6
|
+
It allows you to import the files from the device and analyze the
|
7
|
+
data. In addition to the common features like plotting pace, heart
|
8
|
+
rates, elevation and other captured values it also provides a heart
|
9
|
+
rate variability (HRV) analysis. It can also update satellite orbit
|
10
|
+
prediction (EPO) data on the device to speed-up GPS fix times.
|
11
|
+
Unfortunately, the download mechanism for CPE files used by the
|
12
|
+
devices with GPS chipsets from Sony is still unknown and hence
|
13
|
+
unsupported. Postrunner is an offline alternative to Garmin Connect.
|
14
|
+
The software has been developed and tested on Linux but should work on
|
15
|
+
other operating systems as well.
|
13
16
|
|
14
17
|
## Installation
|
15
18
|
|
16
19
|
PostRunner is a [http://www.ruby-lang.org](Ruby) application. You need
|
17
|
-
to have a Ruby 2.
|
20
|
+
to have a Ruby 2.4 or later runtime environment installed. This
|
18
21
|
application was developed and tested on Linux but may work on other
|
19
|
-
operating systems as well.
|
22
|
+
operating systems as well. You can either install it as root for all
|
23
|
+
users on the computer or as a particular user for just this user.
|
24
|
+
|
25
|
+
### System-wide installation as root user
|
20
26
|
|
21
27
|
```
|
22
28
|
$ gem install postrunner
|
23
29
|
```
|
24
30
|
|
31
|
+
On some Linux distributions using sudo might resolve in permission
|
32
|
+
problems as the installed packages are not readable for normal users.
|
33
|
+
This typically results in 'cannot load such file' type error messages.
|
34
|
+
|
35
|
+
### Installation as non-privileged user
|
36
|
+
|
37
|
+
```
|
38
|
+
gem install --user-install postrunner
|
39
|
+
```
|
40
|
+
|
41
|
+
This will install PostRunner and all dependency packages in your .gem
|
42
|
+
directory. You then need to add the binary path to your PATH variable
|
43
|
+
in your .profile or .bashrc or .whatever file. The path is typically
|
44
|
+
.gem/ruby/<version>/bin. Watch out, on some Linux distributions the
|
45
|
+
version number of ruby gets added to the binary name, e. g.
|
46
|
+
postrunner.ruby2.7. You can use a symbolic link or alias to safe some
|
47
|
+
typing.
|
48
|
+
|
25
49
|
## Usage
|
26
50
|
|
27
51
|
### Importing FIT files
|
@@ -29,7 +53,7 @@ $ gem install postrunner
|
|
29
53
|
To get started you need to connect your device to your computer and
|
30
54
|
mount it as a disk drive. Only devices that expose their data as FAT file
|
31
55
|
system are supported. Older devices use proprietary drivers and are
|
32
|
-
not supported by
|
56
|
+
not supported by PostRunner. Once the device is mounted find out the
|
33
57
|
full path to the directory that contains your FIT files. You can then
|
34
58
|
import all files on the device.
|
35
59
|
|
@@ -128,7 +152,7 @@ $ postrunner show :1
|
|
128
152
|
## Contributing
|
129
153
|
|
130
154
|
PostRunner is currently work in progress. It does some things I want
|
131
|
-
with files from my Garmin
|
155
|
+
with files from my Garmin devices. It's certainly possible to do more
|
132
156
|
things and support more devices. Patches are welcome!
|
133
157
|
|
134
158
|
1. Fork it ( https://github.com/scrapper/postrunner/fork )
|
data/lib/postrunner/Activity.rb
CHANGED
@@ -316,7 +316,7 @@ module PostRunner
|
|
316
316
|
if zone.type == 18
|
317
317
|
total_time = 0.0
|
318
318
|
if zone.time_in_hr_zone
|
319
|
-
zone.time_in_hr_zone.each { |tiz| total_time += tiz }
|
319
|
+
zone.time_in_hr_zone.each { |tiz| total_time += tiz if tiz }
|
320
320
|
end
|
321
321
|
break if total_time <= 0.0
|
322
322
|
if zone.heart_rate_zones
|
@@ -354,12 +354,22 @@ module PostRunner
|
|
354
354
|
def local_value(fdr, field, format, units)
|
355
355
|
unit = units[@unit_system]
|
356
356
|
value = fdr.get_as(field, unit)
|
357
|
+
if value.nil? && field == 'avg_speed'
|
358
|
+
# New fit files used 'enhanced_avg_speed' instead of the older
|
359
|
+
# 'avg_speed'.
|
360
|
+
value = fdr.get_as('enhanced_avg_speed', unit)
|
361
|
+
end
|
357
362
|
return '-' unless value
|
358
363
|
"#{format % [value, unit]}"
|
359
364
|
end
|
360
365
|
|
361
366
|
def pace(fdr, field, show_unit = true)
|
362
367
|
speed = fdr.get(field)
|
368
|
+
if speed.nil? && field == 'avg_speed'
|
369
|
+
# New fit files used 'enhanced_avg_speed' instead of the older
|
370
|
+
# 'avg_speed'.
|
371
|
+
speed = fdr.get('enhanced_avg_speed')
|
372
|
+
end
|
363
373
|
case @unit_system
|
364
374
|
when :metric
|
365
375
|
"#{speedToPace(speed)}#{show_unit ? ' min/km' : ''}"
|
@@ -53,7 +53,8 @@ module PostRunner
|
|
53
53
|
def generate_html(doc)
|
54
54
|
doc.unique(:activityview_style) {
|
55
55
|
doc.head {
|
56
|
-
[ 'jquery/jquery-
|
56
|
+
[ 'jquery/jquery-3.5.1.min.js', 'flot/jquery.flot.js',
|
57
|
+
#'flot/jquery.flot.time.js' ].each do |js|
|
57
58
|
'flot/jquery.flot.time.js' ].each do |js|
|
58
59
|
doc.script({ 'language' => 'javascript',
|
59
60
|
'type' => 'text/javascript', 'src' => js })
|
data/lib/postrunner/ChartView.rb
CHANGED
@@ -30,7 +30,7 @@ module PostRunner
|
|
30
30
|
:unit => select_unit('min/km'),
|
31
31
|
:graph => :line_graph,
|
32
32
|
:colors => '#0A7BEE',
|
33
|
-
:show => @sport == 'running' || @sport == 'multisport'
|
33
|
+
:show => @sport == 'running' || @sport == 'multisport'
|
34
34
|
},
|
35
35
|
{
|
36
36
|
:id => 'speed',
|
@@ -40,9 +40,17 @@ module PostRunner
|
|
40
40
|
:colors => '#0A7BEE',
|
41
41
|
:show => @sport != 'running'
|
42
42
|
},
|
43
|
+
{
|
44
|
+
:id => 'Power_18FB2CF01A4B430DAD66988C847421F4',
|
45
|
+
:label => 'Power',
|
46
|
+
:unit => select_unit('Watts'),
|
47
|
+
:graph => :line_graph,
|
48
|
+
:colors => '#FFAC2E',
|
49
|
+
:show => @sport == 'running' || @sport == 'multisport'
|
50
|
+
},
|
43
51
|
{
|
44
52
|
:id => 'altitude',
|
45
|
-
:label => '
|
53
|
+
:label => 'Altitude',
|
46
54
|
:unit => select_unit('m'),
|
47
55
|
:graph => :line_graph,
|
48
56
|
:colors => '#5AAA44',
|
@@ -73,6 +81,14 @@ module PostRunner
|
|
73
81
|
:colors => '#900000',
|
74
82
|
:show => false
|
75
83
|
},
|
84
|
+
{
|
85
|
+
:id => 'respiration_rate',
|
86
|
+
:label => 'Respiration Rate',
|
87
|
+
:unit => 'brpm',
|
88
|
+
:graph => :line_graph,
|
89
|
+
:colors => '#9cd6ef',
|
90
|
+
:show => true
|
91
|
+
},
|
76
92
|
{
|
77
93
|
:id => 'performance_condition',
|
78
94
|
:label => 'Performance Condition',
|
@@ -119,6 +135,22 @@ module PostRunner
|
|
119
135
|
[ '#FF5558', nil ] ],
|
120
136
|
:show => @sport == 'running' || @sport == 'multisport'
|
121
137
|
},
|
138
|
+
{
|
139
|
+
:id => 'Form_Power_18FB2CF01A4B430DAD66988C847421F4',
|
140
|
+
:label => 'Form Power',
|
141
|
+
:unit => select_unit('Watts'),
|
142
|
+
:graph => :line_graph,
|
143
|
+
:colors => '#CBBB58',
|
144
|
+
:show => @sport == 'running' || @sport == 'multisport'
|
145
|
+
},
|
146
|
+
{
|
147
|
+
:id => 'Leg_Spring_Stiffness_18FB2CF01A4B430DAD66988C847421F4',
|
148
|
+
:label => 'Leg Spring Stiffness',
|
149
|
+
:unit => select_unit('kN/m'),
|
150
|
+
:graph => :line_graph,
|
151
|
+
:colors => '#358C88',
|
152
|
+
:show => @sport == 'running' || @sport == 'multisport'
|
153
|
+
},
|
122
154
|
{
|
123
155
|
:id => 'stance_time',
|
124
156
|
:label => 'Ground Contact Time',
|
@@ -149,6 +181,14 @@ module PostRunner
|
|
149
181
|
:colors => '#A88BBB',
|
150
182
|
:show => @sport == 'cycling'
|
151
183
|
},
|
184
|
+
{
|
185
|
+
:id => "Air_Power_18FB2CF01A4B430DAD66988C847421F4",
|
186
|
+
:label => 'Air Power',
|
187
|
+
:unit => select_unit('Watts'),
|
188
|
+
:graph => :line_graph,
|
189
|
+
:colors => '#919498',
|
190
|
+
:show => @sport == 'running' || @sport == 'multisport'
|
191
|
+
},
|
152
192
|
{
|
153
193
|
:id => 'temperature',
|
154
194
|
:label => 'Temperature',
|
@@ -221,7 +261,7 @@ EOT
|
|
221
261
|
end
|
222
262
|
|
223
263
|
def java_script
|
224
|
-
s = "$(function() {\n"
|
264
|
+
s = "$(document).ready(function() {\n"
|
225
265
|
|
226
266
|
s << tooltip_div
|
227
267
|
@charts.each do |chart|
|
@@ -295,7 +335,18 @@ EOT
|
|
295
335
|
# finish the line and start a new one later.
|
296
336
|
data_set << [ (last_timestamp - start_time + 1).to_i * 1000, nil ]
|
297
337
|
end
|
298
|
-
|
338
|
+
value = r.get_as(chart[:id], chart[:unit] || '')
|
339
|
+
if value.nil? && chart[:id] == 'speed'
|
340
|
+
# If speed field doesn't exist the value might be in the
|
341
|
+
# enhanced_speed field.
|
342
|
+
value = r.get_as('enhanced_speed', chart[:unit] || '')
|
343
|
+
end
|
344
|
+
if value.nil? && chart[:id] == 'altitude'
|
345
|
+
# If altitude field doesn't exist the value might be in the
|
346
|
+
# enhanced_elevation field.
|
347
|
+
value = r.get_as('enhanced_elevation', chart[:unit] || '')
|
348
|
+
end
|
349
|
+
if value
|
299
350
|
if chart[:id] == 'pace'
|
300
351
|
# Slow speeds lead to very large pace values that make the graph
|
301
352
|
# hard to read. We cap the pace at 20.0 min/km to keep it
|
@@ -44,7 +44,7 @@ module PostRunner
|
|
44
44
|
def generate_html(doc)
|
45
45
|
doc.unique(:dailymonitoringview_style) {
|
46
46
|
doc.head {
|
47
|
-
[ 'jquery/jquery-
|
47
|
+
[ 'jquery/jquery-3.5.1.min.js', 'flot/jquery.flot.js',
|
48
48
|
'flot/jquery.flot.time.js' ].each do |js|
|
49
49
|
doc.script({ 'language' => 'javascript',
|
50
50
|
'type' => 'text/javascript', 'src' => js })
|
@@ -126,6 +126,19 @@ module PostRunner
|
|
126
126
|
t.new_row
|
127
127
|
end
|
128
128
|
|
129
|
+
if (ant_id = device.ant_id)
|
130
|
+
t.cell('ANT ID:')
|
131
|
+
t.cell(ant_id)
|
132
|
+
t.new_row
|
133
|
+
end
|
134
|
+
|
135
|
+
if ant_id && (sensor_settings = find_settings_by_ant_id(ant_id)) &&
|
136
|
+
(calibration_factor = sensor_settings.calibration_factor)
|
137
|
+
t.cell('Calibration Factor')
|
138
|
+
t.cell('%.1f' % calibration_factor)
|
139
|
+
t.new_row
|
140
|
+
end
|
141
|
+
|
129
142
|
if (rx_ok = device.rx_packets_ok) && (rx_err = device.rx_packets_err)
|
130
143
|
t.cell('Packet Errors:')
|
131
144
|
t.cell('%d%%' % ((rx_err.to_f / (rx_ok + rx_err)) * 100).to_i)
|
@@ -148,6 +161,14 @@ module PostRunner
|
|
148
161
|
tables
|
149
162
|
end
|
150
163
|
|
164
|
+
private
|
165
|
+
|
166
|
+
def find_settings_by_ant_id(ant_id)
|
167
|
+
@fit_activity.sensor_settings.find do |sensor|
|
168
|
+
sensor.ant_id == ant_id
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
151
172
|
end
|
152
173
|
|
153
174
|
end
|
data/lib/postrunner/EventList.rb
CHANGED
@@ -75,7 +75,7 @@ module PostRunner
|
|
75
75
|
case event.event
|
76
76
|
when 'timer'
|
77
77
|
name = "Timer (#{event.event_type.gsub(/_/, ' ')})"
|
78
|
-
value =
|
78
|
+
value = '-'
|
79
79
|
when 'course_point'
|
80
80
|
name = 'Course Point'
|
81
81
|
value = event.message_index
|
@@ -121,12 +121,19 @@ module PostRunner
|
|
121
121
|
when 'comm_timeout'
|
122
122
|
name 'Communication timeout'
|
123
123
|
value = event.comm_timeout
|
124
|
+
when 'off_course'
|
125
|
+
name = 'Off Course'
|
126
|
+
value = '-'
|
124
127
|
when 'recovery_hr'
|
125
128
|
name = 'Recovery heart rate'
|
126
129
|
value = "#{event.recovery_hr} bpm"
|
127
130
|
when 'recovery_time'
|
128
131
|
name = 'Recovery time'
|
129
|
-
|
132
|
+
if event.recovery_time
|
133
|
+
value = "#{secsToDHMS(event.recovery_time * 60)}"
|
134
|
+
else
|
135
|
+
value = '-'
|
136
|
+
end
|
130
137
|
when 'recovery_info'
|
131
138
|
name = 'Recovery info'
|
132
139
|
mins = event.recovery_info
|
@@ -140,6 +147,9 @@ module PostRunner
|
|
140
147
|
when 'lactate_threshold_speed'
|
141
148
|
name = 'Lactate Threshold Pace'
|
142
149
|
value = pace(event, 'lactate_threshold_speed')
|
150
|
+
when 'functional_threshold_power'
|
151
|
+
name = 'Functional Threshold Power'
|
152
|
+
value = "#{event.functional_threshold_power} W"
|
143
153
|
else
|
144
154
|
name = event.event
|
145
155
|
value = event.data
|
@@ -3,7 +3,7 @@
|
|
3
3
|
#
|
4
4
|
# = FFS_Device.rb -- PostRunner - Manage the data from your Garmin sport devices.
|
5
5
|
#
|
6
|
-
# Copyright (c) 2015, 2016, 2018 by Chris Schlaeger <cs@taskjuggler.org>
|
6
|
+
# Copyright (c) 2015, 2016, 2018, 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
|
@@ -83,7 +83,8 @@ module PostRunner
|
|
83
83
|
return nil
|
84
84
|
end
|
85
85
|
else
|
86
|
-
# Don't add the entity if has deleted before and overwrite isn't
|
86
|
+
# Don't add the entity if it has deleted before and overwrite isn't
|
87
|
+
# true.
|
87
88
|
path = @store['file_store'].fit_file_dir(File.basename(fit_file_name),
|
88
89
|
long_uid, type)
|
89
90
|
fq_fit_file_name = File.join(path, File.basename(fit_file_name))
|
@@ -44,7 +44,7 @@ module PostRunner
|
|
44
44
|
# Setup non-persistent variables.
|
45
45
|
def restore
|
46
46
|
@data_dir = @store['config']['data_dir']
|
47
|
-
# Ensure that we have
|
47
|
+
# Ensure that we have a Hash in the store to hold all known devices.
|
48
48
|
@store['devices'] = @store.new(PEROBS::Hash) unless @store['devices']
|
49
49
|
|
50
50
|
@devices_dir = File.join(@data_dir, 'devices')
|
@@ -69,8 +69,58 @@ module PostRunner
|
|
69
69
|
end
|
70
70
|
|
71
71
|
# Version upgrade logic.
|
72
|
-
def handle_version_update
|
73
|
-
|
72
|
+
def handle_version_update(from_version, to_version)
|
73
|
+
if from_version <= Gem::Version.new('0.12.0')
|
74
|
+
# PostRunner up until version 0.12.0 was using a long_uid with
|
75
|
+
# manufacturer name and product name. This was a bad idea since unknown
|
76
|
+
# devices were resolved to their numerical ID. In case the unknown ID
|
77
|
+
# was later added to the dictionary in fit4ruby version update, it
|
78
|
+
# resolved to its name and the device was recognized as a new device.
|
79
|
+
# Versions after 0.12.0 only use the numerical versions for the device
|
80
|
+
# long_uid and directory names.
|
81
|
+
uid_remap = {}
|
82
|
+
@store['devices'].each do |uid, device|
|
83
|
+
old_uid = uid
|
84
|
+
|
85
|
+
if (first_activity = device.activities.first)
|
86
|
+
first_activity.load_fit_file
|
87
|
+
if (fit_activity = first_activity.fit_activity)
|
88
|
+
if (device_info = fit_activity.device_infos.first)
|
89
|
+
new_uid = "#{device_info.numeric_manufacturer}-" +
|
90
|
+
"#{device_info.numeric_product}-#{device_info.serial_number}"
|
91
|
+
|
92
|
+
uid_remap[old_uid] = new_uid
|
93
|
+
puts first_activity.fit_file_name
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
@store.transaction do
|
100
|
+
pwd = Dir.pwd
|
101
|
+
base_dir_name = @store['config']['devices_dir']
|
102
|
+
Dir.chdir(base_dir_name)
|
103
|
+
|
104
|
+
uid_remap.each do |old_uid, new_uid|
|
105
|
+
if Dir.exist?(old_uid) && !Dir.exist?(new_uid) &&
|
106
|
+
!File.symlink?(old_uid)
|
107
|
+
# Rename the directory from the old (string) scheme to the
|
108
|
+
# new numeric scheme.
|
109
|
+
FileUtils.mv(old_uid, new_uid)
|
110
|
+
# Create a symbolic link with that points the old name to
|
111
|
+
# the new name.
|
112
|
+
File.symlink(new_uid, old_uid)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Now update the long_uid in the FFS_Device object
|
116
|
+
@store['devices'][new_uid] = device = @store['devices'][old_uid]
|
117
|
+
device.long_uid = new_uid
|
118
|
+
@store['devices'].delete(old_uid)
|
119
|
+
end
|
120
|
+
|
121
|
+
Dir.chdir(pwd)
|
122
|
+
end
|
123
|
+
end
|
74
124
|
end
|
75
125
|
|
76
126
|
# Add a file to the store.
|
@@ -94,8 +144,11 @@ module PostRunner
|
|
94
144
|
|
95
145
|
# Generate a String that uniquely identifies the device that generated
|
96
146
|
# the FIT file.
|
97
|
-
id = extract_fit_file_id(fit_entity)
|
98
|
-
|
147
|
+
unless (id = extract_fit_file_id(fit_entity))
|
148
|
+
return nil
|
149
|
+
end
|
150
|
+
long_uid = "#{id[:numeric_manufacturer]}-" +
|
151
|
+
"#{id[:numeric_product]}-#{id[:serial_number]}"
|
99
152
|
|
100
153
|
# Make sure the device that created the FIT file is properly registered.
|
101
154
|
device = register_device(long_uid)
|
@@ -187,6 +240,7 @@ module PostRunner
|
|
187
240
|
@store['records'].generate_html_reports
|
188
241
|
generate_html_index_pages
|
189
242
|
end
|
243
|
+
|
190
244
|
# Determine the right directory for the given FIT file. The resulting path
|
191
245
|
# looks something like /home/user/.postrunner/devices/garmin-fenix3-1234/
|
192
246
|
# activity/5A.
|
@@ -450,42 +504,23 @@ module PostRunner
|
|
450
504
|
return nil
|
451
505
|
end
|
452
506
|
|
453
|
-
|
454
|
-
|
455
|
-
#
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
:serial_number => di.serial_number
|
466
|
-
}
|
467
|
-
end
|
468
|
-
end
|
469
|
-
Log.error "Fit entity has no device info for 0"
|
470
|
-
return nil
|
471
|
-
else
|
472
|
-
# And for all properly developed devices we can just look at the
|
473
|
-
# file_id section.
|
474
|
-
if fid.manufacturer.nil? ||
|
475
|
-
fid.manufacturer[0..'Undocumented value'.length - 1] ==
|
476
|
-
'Undocumented value'
|
477
|
-
Log.error "Cannot store FIT files for unknown manufacturer " +
|
478
|
-
fid.manufacturer
|
479
|
-
return nil
|
507
|
+
fit_entity.device_infos.each do |di|
|
508
|
+
# Not all FIT file have indexed device sections. In case the device
|
509
|
+
# index is nil we'll take the first entry.
|
510
|
+
if (di.device_index.nil? || di.device_index == 0) &&
|
511
|
+
di.numeric_manufacturer && di.numeric_product
|
512
|
+
return {
|
513
|
+
:manufacturer => di.manufacturer,
|
514
|
+
:product => di.garmin_product || di.product,
|
515
|
+
:numeric_manufacturer => di.numeric_manufacturer,
|
516
|
+
:numeric_product => di.numeric_product,
|
517
|
+
:serial_number => di.serial_number || 0
|
518
|
+
}
|
480
519
|
end
|
481
|
-
fid.serial_number ||= 0
|
482
|
-
|
483
|
-
return {
|
484
|
-
:manufacturer => fid.manufacturer,
|
485
|
-
:product => fid.garmin_product || fid.product,
|
486
|
-
:serial_number => fid.serial_number
|
487
|
-
}
|
488
520
|
end
|
521
|
+
|
522
|
+
Log.error "Fit entity has no device info section"
|
523
|
+
return nil
|
489
524
|
end
|
490
525
|
|
491
526
|
def register_device(long_uid)
|
@@ -502,7 +537,8 @@ module PostRunner
|
|
502
537
|
@store.new(FFS_Device, short_uid, long_uid)
|
503
538
|
|
504
539
|
# Create the directory to store the FIT files of this device.
|
505
|
-
create_directory(File.join(@devices_dir, long_uid),
|
540
|
+
create_directory(File.join(@devices_dir, long_uid),
|
541
|
+
long_uid)
|
506
542
|
end
|
507
543
|
|
508
544
|
@store['devices'][long_uid]
|