Skip to content

Commit e305f58

Browse files
Release latest (#121)
* bump FIT SDK for Go to latest version * fix messages arrival ordering when exporting to FIT * fix bug on combine on accumulation logic and split session logic * fix UI missing sport in Lap section --------- Co-authored-by: Hikmatulloh Hari Mukti <[email protected]>
1 parent f69ea17 commit e305f58

File tree

9 files changed

+295
-158
lines changed

9 files changed

+295
-158
lines changed

src/components/OpenActivity.vue

+15-4
Original file line numberDiff line numberDiff line change
@@ -664,10 +664,21 @@ export default {
664664

665665
for (let i = 0; i < combinedSessions.value.length; i++) {
666666
const ses = combinedSessions.value[i]
667-
for (let j = 0; j < ses.records.length; j++) {
668-
if (ses.records[j].distance == null) continue
669-
ses.records[j].distance! += prevSessionlastDistance
670-
lastDistance = ses.records[j].distance!
667+
let shouldAccumulate = false
668+
if (i != 0) {
669+
// Test if first record distance is already >= prev last record.
670+
for (let j = 0; j < ses.records.length; j++) {
671+
if (ses.records[j].distance == null) continue
672+
if (ses.records[j].distance! < prevSessionlastDistance) shouldAccumulate = true
673+
break
674+
}
675+
}
676+
if (shouldAccumulate || prevSessionlastDistance == 0) {
677+
for (let j = 0; j < ses.records.length; j++) {
678+
if (ses.records[j].distance == null) continue
679+
ses.records[j].distance! += prevSessionlastDistance
680+
lastDistance = ses.records[j].distance!
681+
}
671682
}
672683
prevSessionlastDistance = lastDistance
673684

src/components/TheLap.vue

+2-1
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,11 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. -->
2626
>
2727
<div class="row text-start">
2828
<div class="col-auto d-inline-block" style="height: 50px">
29-
<h6 style="text-align: left">
29+
<h6 style="text-align: left" class="mb-0">
3030
<i class="fa-solid fa-caret-right collapse-indicator"></i>
3131
<span class="px-1">Lap {{ index + 1 }}</span>
3232
</h6>
33+
<span>{{ lap.sport }}</span>
3334
</div>
3435
<div class="col">
3536
<div class="row overview-title">Distance</div>

src/spec/activity.ts

+1
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ export enum WorkoutType {
8080

8181
export class Lap {
8282
timestamp: string | null = null
83+
sport: string = SPORT_GENERIC
8384
totalMovingTime: number | null = null
8485
totalElapsedTime: number | null = null
8586
totalDistance: number | null = null

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

+1-46
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,7 @@ import (
1919
"errors"
2020
"strconv"
2121

22-
"github.com/muktihari/fit/factory"
23-
"github.com/muktihari/fit/kit/datetime"
24-
"github.com/muktihari/fit/profile/filedef"
2522
"github.com/muktihari/fit/profile/mesgdef"
26-
"github.com/muktihari/fit/profile/untyped/fieldnum"
27-
"github.com/muktihari/fit/profile/untyped/mesgnum"
2823
"github.com/muktihari/fit/proto"
2924
)
3025

@@ -36,6 +31,7 @@ type Activity struct {
3631
Timezone int8
3732
Sessions []Session
3833

34+
Sports []*mesgdef.Sport
3935
SplitSummaries []*mesgdef.SplitSummary // required for FIT file; entries must be unique within each split_type
4036
Activity *mesgdef.Activity // required for FIT file.
4137

@@ -52,47 +48,6 @@ func CreateActivity() Activity {
5248
}
5349
}
5450

55-
// ToFIT converts Activity into proto.FIT.
56-
func (a *Activity) ToFIT(options *mesgdef.Options) proto.FIT {
57-
size := 2 + len(a.Sessions) + len(a.SplitSummaries) + len(a.UnrelatedMessages)
58-
for i := range a.Sessions {
59-
ses := &a.Sessions[i]
60-
size += len(ses.Records) + len(ses.Laps)
61-
}
62-
63-
fit := proto.FIT{Messages: make([]proto.Message, 0, size)}
64-
fit.Messages = append(fit.Messages, a.Creator.FileId.ToMesg(options))
65-
fit.Messages = append(fit.Messages, a.UnrelatedMessages...)
66-
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-
75-
for i := range a.Sessions {
76-
ses := &a.Sessions[i]
77-
78-
for j := range ses.Records {
79-
rec := &ses.Records[j]
80-
fit.Messages = append(fit.Messages, rec.Record.ToMesg(options))
81-
}
82-
for j := range ses.Laps {
83-
lap := &ses.Laps[j]
84-
fit.Messages = append(fit.Messages, lap.Lap.ToMesg(options))
85-
}
86-
fit.Messages = append(fit.Messages, ses.Session.ToMesg(options))
87-
}
88-
89-
fit.Messages = append(fit.Messages, a.Activity.ToMesg(nil))
90-
91-
filedef.SortMessagesByTimestamp(fit.Messages[1:]) // Exclude FileId
92-
93-
return fit
94-
}
95-
9651
// MarshalAppendJSON appends the JSON format encoding of Activity to b, returning the result.
9752
func (a *Activity) MarshalAppendJSON(b []byte) []byte {
9853
b = append(b, '{')

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

+42-84
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"github.com/muktihari/fit/decoder"
2727
"github.com/muktihari/fit/encoder"
2828
"github.com/muktihari/fit/kit/datetime"
29+
"github.com/muktihari/fit/profile/basetype"
2930
"github.com/muktihari/fit/profile/filedef"
3031
"github.com/muktihari/fit/profile/mesgdef"
3132
"github.com/muktihari/fit/profile/typedef"
@@ -57,6 +58,9 @@ func (s *DecodeEncoder) Decode(ctx context.Context, r io.Reader) ([]activity.Act
5758
lis := filedef.NewListener()
5859
defer lis.Close()
5960

61+
lis.Reset(filedef.WithFileFunc(typedef.FileActivity,
62+
func() filedef.File { return &wrapActivity{activity: filedef.NewActivity()} }))
63+
6064
dec := decoderPool.Get().(*decoder.Decoder)
6165
defer decoderPool.Put(dec)
6266

@@ -85,12 +89,12 @@ func (s *DecodeEncoder) Decode(ctx context.Context, r io.Reader) ([]activity.Act
8589
return nil, fmt.Errorf("could not decode: %w", err)
8690
}
8791

88-
activityFile := lis.File().(*filedef.Activity)
89-
if len(activityFile.Records) == 0 {
92+
wa := lis.File().(*wrapActivity)
93+
if len(wa.activity.Records) == 0 {
9094
continue
9195
}
9296

93-
activities = append(activities, s.convertToActivity(activityFile))
97+
activities = append(activities, s.convertToActivity(wa.activity))
9498
}
9599

96100
if len(activities) == 0 {
@@ -131,9 +135,10 @@ func (s *DecodeEncoder) convertToActivity(activityFile *filedef.Activity) activi
131135
act := activity.Activity{
132136
Creator: activity.CreateCreator(&fileId),
133137
Timezone: timezone,
138+
Sports: activityFile.Sports,
134139
SplitSummaries: activityFile.SplitSummaries,
135140
Activity: activityFile.Activity,
136-
UnrelatedMessages: s.handleUnrelatedMessages(activityFile),
141+
UnrelatedMessages: activityFile.UnrelatedMessages,
137142
}
138143

139144
// Convert Records, Laps and Sessions to activity's structs
@@ -255,84 +260,6 @@ func (s *DecodeEncoder) recalculateSummary(ses *activity.Session) {
255260
ses.Summarize()
256261
}
257262

258-
func (s *DecodeEncoder) handleUnrelatedMessages(activityFile *filedef.Activity) []proto.Message {
259-
size := len(activityFile.DeveloperDataIds) +
260-
len(activityFile.FieldDescriptions) +
261-
len(activityFile.DeviceInfos) +
262-
len(activityFile.Events) +
263-
len(activityFile.Lengths) +
264-
len(activityFile.SegmentLaps) +
265-
len(activityFile.ZonesTargets) +
266-
len(activityFile.Workouts) +
267-
len(activityFile.WorkoutSteps) +
268-
len(activityFile.HRs) +
269-
len(activityFile.HRVs) +
270-
len(activityFile.GpsMetadatas) +
271-
len(activityFile.TimeInZones) +
272-
len(activityFile.Splits) +
273-
len(activityFile.Sports) +
274-
len(activityFile.UnrelatedMessages)
275-
276-
if activityFile.UserProfile != nil {
277-
size += 1
278-
}
279-
280-
unrelatedMessages := make([]proto.Message, 0, size)
281-
282-
for i := range activityFile.DeveloperDataIds {
283-
unrelatedMessages = append(unrelatedMessages, activityFile.DeveloperDataIds[i].ToMesg(nil))
284-
}
285-
for i := range activityFile.FieldDescriptions {
286-
unrelatedMessages = append(unrelatedMessages, activityFile.FieldDescriptions[i].ToMesg(nil))
287-
}
288-
if activityFile.UserProfile != nil {
289-
unrelatedMessages = append(unrelatedMessages, activityFile.UserProfile.ToMesg(nil))
290-
}
291-
for i := range activityFile.DeviceInfos {
292-
unrelatedMessages = append(unrelatedMessages, activityFile.DeviceInfos[i].ToMesg(nil))
293-
}
294-
for i := range activityFile.Events {
295-
unrelatedMessages = append(unrelatedMessages, activityFile.Events[i].ToMesg(nil))
296-
}
297-
for i := range activityFile.Lengths {
298-
unrelatedMessages = append(unrelatedMessages, activityFile.Lengths[i].ToMesg(nil))
299-
}
300-
for i := range activityFile.SegmentLaps {
301-
unrelatedMessages = append(unrelatedMessages, activityFile.SegmentLaps[i].ToMesg(nil))
302-
}
303-
for i := range activityFile.ZonesTargets {
304-
unrelatedMessages = append(unrelatedMessages, activityFile.ZonesTargets[i].ToMesg(nil))
305-
}
306-
for i := range activityFile.Workouts {
307-
unrelatedMessages = append(unrelatedMessages, activityFile.Workouts[i].ToMesg(nil))
308-
}
309-
for i := range activityFile.WorkoutSteps {
310-
unrelatedMessages = append(unrelatedMessages, activityFile.WorkoutSteps[i].ToMesg(nil))
311-
}
312-
for i := range activityFile.HRs {
313-
unrelatedMessages = append(unrelatedMessages, activityFile.HRs[i].ToMesg(nil))
314-
}
315-
for i := range activityFile.HRVs {
316-
unrelatedMessages = append(unrelatedMessages, activityFile.HRVs[i].ToMesg(nil))
317-
}
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-
}
330-
331-
unrelatedMessages = append(unrelatedMessages, activityFile.UnrelatedMessages...)
332-
333-
return unrelatedMessages
334-
}
335-
336263
func (s *DecodeEncoder) Encode(ctx context.Context, activities []activity.Activity) ([][]byte, error) {
337264
buf := mem.GetBuffer()
338265
defer mem.PutBuffer(buf)
@@ -344,8 +271,26 @@ func (s *DecodeEncoder) Encode(ctx context.Context, activities []activity.Activi
344271

345272
bs := make([][]byte, len(activities))
346273
for i := range activities {
347-
s.makeLastSummary(&activities[i])
348-
fit := activities[i].ToFIT(nil)
274+
a := &activities[i]
275+
s.makeLastSummary(a)
276+
277+
wa := wrapActivity{activity: filedef.NewActivity()}
278+
wa.activity.FileId = *a.Creator.FileId
279+
for j := range a.Sessions {
280+
ses := &a.Sessions[j]
281+
for k := range ses.Laps {
282+
wa.activity.Laps = append(wa.activity.Laps, ses.Laps[k].Lap)
283+
}
284+
for k := range ses.Records {
285+
wa.activity.Records = append(wa.activity.Records, ses.Records[k].Record)
286+
}
287+
wa.activity.Sessions = append(wa.activity.Sessions, ses.Session)
288+
}
289+
wa.activity.SplitSummaries = a.SplitSummaries
290+
wa.activity.Activity = a.Activity
291+
wa.activity.UnrelatedMessages = a.UnrelatedMessages
292+
293+
fit := wa.ToFIT(nil)
349294

350295
enc.Reset(bufAt,
351296
encoder.WithProtocolVersion(proto.V2),
@@ -389,6 +334,19 @@ func (s *DecodeEncoder) makeLastSummary(a *activity.Activity) {
389334
}
390335
}
391336

337+
// Ensure we got the latest timestamp across all messages.
338+
lastTimestampUint32 := datetime.ToUint32(lastTimestamp)
339+
for i := range a.UnrelatedMessages {
340+
timestamp := a.UnrelatedMessages[i].FieldValueByNum(proto.FieldNumTimestamp).Uint32()
341+
if timestamp == basetype.Uint32Invalid {
342+
continue
343+
}
344+
if timestamp < lastTimestampUint32 {
345+
break
346+
}
347+
lastTimestamp = datetime.ToTime(timestamp) // We get latest timestamp
348+
}
349+
392350
for i := range a.Sessions {
393351
a.Sessions[i].Timestamp = lastTimestamp
394352
}

0 commit comments

Comments
 (0)