postrunner 0.11.0 → 1.0.4
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.
- 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]
|