Skip to content

Commit f69ea17

Browse files
authored
Merge 'dev' into 'master' (#119)
1 parent 801454e commit f69ea17

File tree

9 files changed

+111
-48
lines changed

9 files changed

+111
-48
lines changed

src/components/OpenActivity.vue

+18-11
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ import SplitPaceGraph from './SplitPaceGraph.vue'
5757
<h5 class="title">
5858
<img src="@/assets/openivity-header.svg" />
5959
</h5>
60-
<div style="font-size: 0.9em">
60+
<div style="font-size: 1.05em">
6161
Your data stays in your computer: 100% client-side power.
6262
</div>
6363
</div>
@@ -67,7 +67,7 @@ import SplitPaceGraph from './SplitPaceGraph.vue'
6767
>
6868
</TheNavigatorInput>
6969
<Transition>
70-
<div style="font-size: 0.8em" class="pt-1">
70+
<div style="font-size: 0.9em" class="pt-1">
7171
<span v-if="isActivityServiceReady"> Supported files: *.fit, *.gpx, *.tcx </span>
7272
<span v-else>
7373
Instantiating WebAssembly <i class="fas fa-spinner fa-spin"></i>
@@ -281,16 +281,23 @@ import SplitPaceGraph from './SplitPaceGraph.vue'
281281
<!-- Tab Content Ends -->
282282
</div>
283283
<!-- Tab Ends -->
284-
<span class="footer pt-3">
285-
<span class="mx-1">
286-
<i class="fa-solid fa-copyright fa-rotate-180"></i> {{ new Date().getFullYear() }}
287-
</span>
288-
<span class="mx-1">
284+
<span class="footer pt-3 mb-4">
285+
<span class="mx-1 fw-bold">
289286
<a href="http://github.com/openivity/openivity.github.io" target="_blank">
290-
<i class="fa-brands fa-github"></i> Code
287+
<i class="fa-brands fa-github"></i> Star on GitHub
288+
</a>
289+
</span>
290+
<span class="mx-1">|</span>
291+
<span class="mx-1 fw-bold">
292+
<a href="http://github.com/sponsors/muktihari" target="_blank">
293+
Buy me a coffee&nbsp;&nbsp;☕
291294
</a>
292295
</span>
293-
<div class="mx-1 pt-1">Openivity's Open Source Project</div>
296+
<div class="mx-1 pt-1">
297+
Openivity's Open Source Project
298+
<i class="fa-solid fa-copyright fa-rotate-180"></i> 2023 -
299+
{{ new Date().getFullYear() }}
300+
</div>
294301
</span>
295302
</div>
296303
</div>
@@ -933,7 +940,7 @@ export default {
933940

934941
<style scoped>
935942
.title img {
936-
height: 1.5em;
943+
height: 2em;
937944
}
938945

939946
.v-enter-active,
@@ -985,7 +992,7 @@ export default {
985992
.footer {
986993
display: inline-block;
987994
height: 70px;
988-
font-size: 0.8em;
995+
font-size: 1em;
989996
color: var(--green-text);
990997
}
991998

src/components/TheNavigatorInput.vue

+2-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. -->
1919
<div class="navigator-input mx-auto">
2020
<input
2121
class="form-control form-control-sm"
22+
style="font-size: 1em"
2223
type="file"
2324
:id="id"
2425
multiple
@@ -53,6 +54,6 @@ export default {
5354
<style>
5455
.navigator-input {
5556
text-align: center;
56-
max-width: 320px;
57+
max-width: 360px;
5758
}
5859
</style>

src/wasm/activity-service/activity/activity.go

+17-17
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@ package activity
1818
import (
1919
"errors"
2020
"strconv"
21-
"time"
2221

22+
"github.com/muktihari/fit/factory"
23+
"github.com/muktihari/fit/kit/datetime"
2324
"github.com/muktihari/fit/profile/filedef"
2425
"github.com/muktihari/fit/profile/mesgdef"
25-
"github.com/muktihari/fit/profile/typedef"
26+
"github.com/muktihari/fit/profile/untyped/fieldnum"
27+
"github.com/muktihari/fit/profile/untyped/mesgnum"
2628
"github.com/muktihari/fit/proto"
2729
)
2830

@@ -34,6 +36,9 @@ type Activity struct {
3436
Timezone int8
3537
Sessions []Session
3638

39+
SplitSummaries []*mesgdef.SplitSummary // required for FIT file; entries must be unique within each split_type
40+
Activity *mesgdef.Activity // required for FIT file.
41+
3742
// UnrelatedMessages contains all messages not used by our service
3843
// such as DeveloperDataIds, FieldDescriptions, Events, etc.
3944
// We will restore these messages as it is when we recreate the FIT files.
@@ -49,7 +54,7 @@ func CreateActivity() Activity {
4954

5055
// ToFIT converts Activity into proto.FIT.
5156
func (a *Activity) ToFIT(options *mesgdef.Options) proto.FIT {
52-
size := 1 + len(a.Sessions) + len(a.UnrelatedMessages)
57+
size := 2 + len(a.Sessions) + len(a.SplitSummaries) + len(a.UnrelatedMessages)
5358
for i := range a.Sessions {
5459
ses := &a.Sessions[i]
5560
size += len(ses.Records) + len(ses.Laps)
@@ -59,14 +64,16 @@ func (a *Activity) ToFIT(options *mesgdef.Options) proto.FIT {
5964
fit.Messages = append(fit.Messages, a.Creator.FileId.ToMesg(options))
6065
fit.Messages = append(fit.Messages, a.UnrelatedMessages...)
6166

62-
var totalTimerTime uint32
63-
var lastTimestamp time.Time
67+
for i := range a.SplitSummaries {
68+
mesg := a.SplitSummaries[i].ToMesg(nil)
69+
mesg.Fields = append([]proto.Field{
70+
factory.CreateField(mesgnum.Session, fieldnum.SessionTimestamp).WithValue(datetime.ToUint32(a.Activity.Timestamp)),
71+
}, mesg.Fields...)
72+
fit.Messages = append(fit.Messages, mesg)
73+
}
74+
6475
for i := range a.Sessions {
6576
ses := &a.Sessions[i]
66-
if ses.Timestamp.After(lastTimestamp) {
67-
lastTimestamp = ses.Timestamp
68-
}
69-
totalTimerTime += ses.TotalTimerTime
7077

7178
for j := range ses.Records {
7279
rec := &ses.Records[j]
@@ -79,14 +86,7 @@ func (a *Activity) ToFIT(options *mesgdef.Options) proto.FIT {
7986
fit.Messages = append(fit.Messages, ses.Session.ToMesg(options))
8087
}
8188

82-
activityMesg := mesgdef.NewActivity(nil).
83-
SetType(typedef.ActivityAutoMultiSport).
84-
SetTimestamp(lastTimestamp).
85-
SetLocalTimestamp(lastTimestamp.Add(time.Duration(a.Timezone) * time.Hour)).
86-
SetTotalTimerTime(totalTimerTime).
87-
SetNumSessions(uint16(len(a.Sessions)))
88-
89-
fit.Messages = append(fit.Messages, activityMesg.ToMesg(options))
89+
fit.Messages = append(fit.Messages, a.Activity.ToMesg(nil))
9090

9191
filedef.SortMessagesByTimestamp(fit.Messages[1:]) // Exclude FileId
9292

src/wasm/activity-service/activity/fit/fit.go

+35-4
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"github.com/muktihari/fit/encoder"
2828
"github.com/muktihari/fit/kit/datetime"
2929
"github.com/muktihari/fit/profile/filedef"
30+
"github.com/muktihari/fit/profile/mesgdef"
3031
"github.com/muktihari/fit/profile/typedef"
3132
"github.com/muktihari/fit/proto"
3233
"github.com/openivity/activity-service/activity"
@@ -69,19 +70,19 @@ func (s *DecodeEncoder) Decode(ctx context.Context, r io.Reader) ([]activity.Act
6970
for dec.Next() {
7071
fileId, err := dec.PeekFileId()
7172
if err != nil {
72-
return nil, err
73+
return nil, fmt.Errorf("could not peek: %w", err)
7374
}
7475

7576
if fileId.Type != typedef.FileActivity {
7677
if err = dec.Discard(); err != nil {
77-
return nil, err
78+
return nil, fmt.Errorf("could not discard: %w", err)
7879
}
7980
continue
8081
}
8182

8283
_, err = dec.DecodeWithContext(ctx)
8384
if err != nil {
84-
return nil, err
85+
return nil, fmt.Errorf("could not decode: %w", err)
8586
}
8687

8788
activityFile := lis.File().(*filedef.Activity)
@@ -130,6 +131,8 @@ func (s *DecodeEncoder) convertToActivity(activityFile *filedef.Activity) activi
130131
act := activity.Activity{
131132
Creator: activity.CreateCreator(&fileId),
132133
Timezone: timezone,
134+
SplitSummaries: activityFile.SplitSummaries,
135+
Activity: activityFile.Activity,
133136
UnrelatedMessages: s.handleUnrelatedMessages(activityFile),
134137
}
135138

@@ -264,6 +267,10 @@ func (s *DecodeEncoder) handleUnrelatedMessages(activityFile *filedef.Activity)
264267
len(activityFile.WorkoutSteps) +
265268
len(activityFile.HRs) +
266269
len(activityFile.HRVs) +
270+
len(activityFile.GpsMetadatas) +
271+
len(activityFile.TimeInZones) +
272+
len(activityFile.Splits) +
273+
len(activityFile.Sports) +
267274
len(activityFile.UnrelatedMessages)
268275

269276
if activityFile.UserProfile != nil {
@@ -308,6 +315,18 @@ func (s *DecodeEncoder) handleUnrelatedMessages(activityFile *filedef.Activity)
308315
for i := range activityFile.HRVs {
309316
unrelatedMessages = append(unrelatedMessages, activityFile.HRVs[i].ToMesg(nil))
310317
}
318+
for i := range activityFile.GpsMetadatas {
319+
unrelatedMessages = append(unrelatedMessages, activityFile.GpsMetadatas[i].ToMesg(nil))
320+
}
321+
for i := range activityFile.TimeInZones {
322+
unrelatedMessages = append(unrelatedMessages, activityFile.TimeInZones[i].ToMesg(nil))
323+
}
324+
for i := range activityFile.Splits {
325+
unrelatedMessages = append(unrelatedMessages, activityFile.Splits[i].ToMesg(nil))
326+
}
327+
for i := range activityFile.Sports {
328+
unrelatedMessages = append(unrelatedMessages, activityFile.Sports[i].ToMesg(nil))
329+
}
311330

312331
unrelatedMessages = append(unrelatedMessages, activityFile.UnrelatedMessages...)
313332

@@ -330,7 +349,7 @@ func (s *DecodeEncoder) Encode(ctx context.Context, activities []activity.Activi
330349

331350
enc.Reset(bufAt,
332351
encoder.WithProtocolVersion(proto.V2),
333-
encoder.WithNormalHeader(15),
352+
encoder.WithHeaderOption(encoder.HeaderOptionNormal, 15),
334353
)
335354
if err := enc.EncodeWithContext(ctx, &fit); err != nil {
336355
return nil, fmt.Errorf("could not encode: %w", err)
@@ -344,8 +363,10 @@ func (s *DecodeEncoder) Encode(ctx context.Context, activities []activity.Activi
344363

345364
func (s *DecodeEncoder) makeLastSummary(a *activity.Activity) {
346365
var lastTimestamp time.Time
366+
var totalTimerTime uint32
347367
for i := len(a.Sessions) - 1; i >= 0; i-- {
348368
ses := a.Sessions[i]
369+
totalTimerTime += ses.TotalTimerTime
349370

350371
for j := len(ses.Records) - 1; j >= 0; j-- {
351372
rec := ses.Records[j]
@@ -371,6 +392,16 @@ func (s *DecodeEncoder) makeLastSummary(a *activity.Activity) {
371392
for i := range a.Sessions {
372393
a.Sessions[i].Timestamp = lastTimestamp
373394
}
395+
396+
if a.Activity == nil {
397+
a.Activity = mesgdef.NewActivity(nil)
398+
}
399+
400+
a.Activity.Timestamp = lastTimestamp
401+
a.Activity.LocalTimestamp = lastTimestamp.Add(time.Duration(a.Timezone) * time.Hour)
402+
a.Activity.TotalTimerTime = totalTimerTime
403+
a.Activity.Type = typedef.ActivityAutoMultiSport
404+
a.Activity.NumSessions = uint16(len(a.Sessions))
374405
}
375406

376407
// bytesBufferAt wraps bytes.Buffer to implement io.WriterAt enabling fast encoding.

src/wasm/activity-service/activity/session.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,9 @@ func (s *Session) EndTime() time.Time {
6060
func NewSessionFromLaps(laps []Lap) Session {
6161
ses := CreateSession(
6262
mesgdef.NewSession(nil).
63-
SetStartTime(laps[0].StartTime))
63+
SetStartTime(laps[0].StartTime).
64+
SetSport(laps[0].Sport),
65+
)
6466

6567
for i := range laps {
6668
aggregator.Aggregate(ses, laps[i].Lap)

src/wasm/activity-service/go.mod

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
module github.com/openivity/activity-service
22

3-
go 1.20
3+
go 1.22.0
4+
5+
toolchain go1.23.0
46

57
require (
6-
github.com/muktihari/fit v0.21.5
8+
github.com/muktihari/fit v0.23.5
79
github.com/muktihari/xmltokenizer v0.0.4
810
golang.org/x/text v0.18.0
911
)
1012

1113
require (
1214
github.com/google/go-cmp v0.6.0
13-
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8
15+
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0
1416
)

src/wasm/activity-service/go.sum

+4-6
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
22
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
3-
github.com/muktihari/fit v0.21.3 h1:k8EYHMQsNgofQQ7jOxZhh5ayYqNJ33rDfRN+TMXP61A=
4-
github.com/muktihari/fit v0.21.3/go.mod h1:zOEqw3seokejqhepYCI4rYTA6p+zblmXFub+XrJ8BTE=
5-
github.com/muktihari/fit v0.21.5 h1:DspCXWzNv4+zu3leWKExHPjHr2yuiR4OgswlkC1XITw=
6-
github.com/muktihari/fit v0.21.5/go.mod h1:zOEqw3seokejqhepYCI4rYTA6p+zblmXFub+XrJ8BTE=
3+
github.com/muktihari/fit v0.23.5 h1:Fu5JA29+oxH9qx4DjUQo+w1MNx5TqfBhrd6CdNHIBZo=
4+
github.com/muktihari/fit v0.23.5/go.mod h1:99RXB2OVc87XhcQzgHfUtCVE3VCJ4BvgmyWQI08MM4w=
75
github.com/muktihari/xmltokenizer v0.0.4 h1:x4DZWvfcEAJR+iBPb8XeiWL1J15SY21bCaem76wmmjw=
86
github.com/muktihari/xmltokenizer v0.0.4/go.mod h1:yTxhndrcpmZEPYqQGSN51MwkzPQgGSkRpqW5ZFBpdF0=
9-
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY=
10-
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI=
7+
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk=
8+
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY=
119
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
1210
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=

src/wasm/activity-service/manufacturers.json

+11-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
{ "id": 2, "name": "x10" },
1313
{ "id": 3, "name": "x6" },
1414
{ "id": 4, "name": "Memory Belt" },
15+
{ "id": 5, "name": "Smart Belt" },
1516
{ "id": 6, "name": "t6" },
1617
{ "id": 7, "name": "t6c" },
1718
{ "id": 8, "name": "t6d" },
@@ -140,11 +141,20 @@
140141
{ "id": 1505, "name": "Rider 310" },
141142
{ "id": 1508, "name": "Rider 530" },
142143
{ "id": 1509, "name": "Rider 330" },
144+
{ "id": 1610, "name": "Rider 10" },
143145
{ "id": 1703, "name": "Aero 60" },
144146
{ "id": 1704, "name": "Rider 450" },
145147
{ "id": 1706, "name": "Rider 410" },
146148
{ "id": 1803, "name": "Rider 15" },
147-
{ "id": 1901, "name": "Rider 420" }
149+
{ "id": 1804, "name": "Rider 860" },
150+
{ "id": 1901, "name": "Rider 420" },
151+
{ "id": 1902, "name": "Rider 750" },
152+
{ "id": 2001, "name": "Rider 320" },
153+
{ "id": 2004, "name": "Rider 15 neo" },
154+
{ "id": 2101, "name": "Rider S500" },
155+
{ "id": 2103, "name": "Rider S800" },
156+
{ "id": 2203, "name": "Rider 750 SE" },
157+
{ "id": 2205, "name": "Rider 460" }
148158
]
149159
},
150160
"289": {

src/wasm/activity-service/service/service.go

+16-4
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ import (
2727

2828
"github.com/muktihari/fit/profile/basetype"
2929
"github.com/muktihari/fit/profile/typedef"
30-
"github.com/muktihari/fit/profile/untyped/mesgnum"
3130
"github.com/openivity/activity-service/activity"
3231
"github.com/openivity/activity-service/aggregator"
3332
"github.com/openivity/activity-service/service/result"
@@ -317,6 +316,7 @@ func (s *Service) combineActivity(activities []activity.Activity, manufacturer t
317316
Creator: creator,
318317
Timezone: activities[0].Timezone,
319318
Sessions: activities[0].Sessions,
319+
SplitSummaries: activities[0].SplitSummaries,
320320
UnrelatedMessages: activities[0].UnrelatedMessages,
321321
}
322322

@@ -362,10 +362,22 @@ func (s *Service) combineActivity(activities []activity.Activity, manufacturer t
362362
newActivity.Sessions = append(newActivity.Sessions, cur.Sessions[1:]...)
363363
}
364364

365-
for j := 0; j < len(cur.UnrelatedMessages); j++ {
366-
if cur.UnrelatedMessages[j].Num == mesgnum.SplitSummary {
367-
continue // TODO: Still failed to upload to Garmin Connect if we include this message.
365+
for _, m := range cur.SplitSummaries {
366+
var ok bool
367+
for _, v := range newActivity.SplitSummaries {
368+
if v.SplitType == m.SplitType {
369+
aggregator.Aggregate(v, m)
370+
ok = true
371+
break
372+
}
368373
}
374+
if !ok {
375+
newActivity.SplitSummaries = append(newActivity.SplitSummaries, m)
376+
}
377+
continue
378+
}
379+
380+
for j := 0; j < len(cur.UnrelatedMessages); j++ {
369381
newActivity.UnrelatedMessages = append(newActivity.UnrelatedMessages, cur.UnrelatedMessages[j])
370382
}
371383
}

0 commit comments

Comments
 (0)