tormoder / fit

A Go package for decoding and encoding Garmin FIT files
MIT License
245 stars 42 forks source link

TimeInHrZone empty on Sessions and Laps #89

Open honza opened 5 months ago

honza commented 5 months ago

I tried to to hack around and even tried the fitgen tool and can't see where the bug could be. The fit file definitely has that information. Any pointers would be appreciated.

jcolp commented 5 months ago

Can you provide a FIT file which has this information that exhibits the issue? I just tested FIT files from the Wahoo application and the Wahoo ROAM 2 and confirmed that the parsed information was present in the session.

honza commented 5 months ago

The TimeInZoneMsg struct doesn't have any fields.

If I add the field, it works:

commit a5cdbad5f996a27f42ad44b470def542c07da60c
Author: Honza Pokorny <honza@redhat.com>
Date:   Thu Jun 13 19:38:57 2024 -0300

    Add time in zone

diff --git a/file_types.go b/file_types.go
index ff9f8df..16dcb60 100644
--- a/file_types.go
+++ b/file_types.go
 // ActivityFile represents the Activity FIT file type.
 // Records sensor data and events from active sessions.
@@ -25,6 +27,8 @@ type ActivityFile struct {
    Hrs          []*HrMsg
    Hrvs         []*HrvMsg

+   TimeInZone []*TimeInZoneMsg
+
    // Requested messages by users.
    Sport *SportMsg
 }
@@ -206,6 +210,8 @@ func (a *ActivityFile) add(msg reflect.Value) {
        a.Hrvs = append(a.Hrvs, &tmp)
    case SportMsg:
        a.Sport = &tmp
+   case TimeInZoneMsg:
+       a.TimeInZone = append(a.TimeInZone, &tmp)
    default:
    }
 }
diff --git a/messages.go b/messages.go
index 34129c9..5c5d307 100644
--- a/messages.go
+++ b/messages.go
@@ -662,12 +662,15 @@ func NewOhrSettingsMsg() *OhrSettingsMsg {

 // TimeInZoneMsg represents the time_in_zone FIT message type.
 type TimeInZoneMsg struct {
+   TimeInHrZone []uint32
 }

 // NewTimeInZoneMsg returns a time_in_zone FIT message
 // initialized to all-invalid values.
 func NewTimeInZoneMsg() *TimeInZoneMsg {
-   return &TimeInZoneMsg{}
+   return &TimeInZoneMsg{
+       TimeInHrZone: nil,
+   }
 }

 // ZonesTargetMsg represents the zones_target FIT message type.
diff --git a/profile.go b/profile.go
index 282a57d..c301b76 100644
--- a/profile.go
+++ b/profile.go
@@ -313,7 +313,9 @@ var _fields = [...][256]*field{

    MesgNumOhrSettings: {},

-   MesgNumTimeInZone: {},
+   MesgNumTimeInZone: {
+       2: {0, 1, types.Fit(38), 8},
+   },

    MesgNumZonesTarget: {
        1: {0, 1, types.Fit(2), 1},

I'm pretty sure this is a bug in the fitgen module. Parsing the spreadsheet works correctly but when it's time to generate the code, we get empty structs. The codegen module is pretty complicated and I haven't had time to debug further.

jcolp commented 5 months ago

I accessed mine using session.TimeInHrZone

honza commented 5 months ago

I accessed mine using session.TimeInHrZone

That field is nil for me. This is a Garmin file. I'll try to produce a minimal example file for testing.

jcolp commented 5 months ago

Ah yeah, they don't put TIZs as fields they use the messages thus why it worked for me on Wahoo. That explains that.

tormoder commented 5 months ago

The TimeInZoneMsg struct doesn't have any fields.

If I add the field, it works:

commit a5cdbad5f996a27f42ad44b470def542c07da60c
Author: Honza Pokorny <honza@redhat.com>
Date:   Thu Jun 13 19:38:57 2024 -0300

    Add time in zone

diff --git a/file_types.go b/file_types.go
index ff9f8df..16dcb60 100644
--- a/file_types.go
+++ b/file_types.go
 // ActivityFile represents the Activity FIT file type.
 // Records sensor data and events from active sessions.
@@ -25,6 +27,8 @@ type ActivityFile struct {
  Hrs          []*HrMsg
  Hrvs         []*HrvMsg

+ TimeInZone []*TimeInZoneMsg
+
  // Requested messages by users.
  Sport *SportMsg
 }
@@ -206,6 +210,8 @@ func (a *ActivityFile) add(msg reflect.Value) {
      a.Hrvs = append(a.Hrvs, &tmp)
  case SportMsg:
      a.Sport = &tmp
+ case TimeInZoneMsg:
+     a.TimeInZone = append(a.TimeInZone, &tmp)
  default:
  }
 }
diff --git a/messages.go b/messages.go
index 34129c9..5c5d307 100644
--- a/messages.go
+++ b/messages.go
@@ -662,12 +662,15 @@ func NewOhrSettingsMsg() *OhrSettingsMsg {

 // TimeInZoneMsg represents the time_in_zone FIT message type.
 type TimeInZoneMsg struct {
+ TimeInHrZone []uint32
 }

 // NewTimeInZoneMsg returns a time_in_zone FIT message
 // initialized to all-invalid values.
 func NewTimeInZoneMsg() *TimeInZoneMsg {
- return &TimeInZoneMsg{}
+ return &TimeInZoneMsg{
+     TimeInHrZone: nil,
+ }
 }

 // ZonesTargetMsg represents the zones_target FIT message type.
diff --git a/profile.go b/profile.go
index 282a57d..c301b76 100644
--- a/profile.go
+++ b/profile.go
@@ -313,7 +313,9 @@ var _fields = [...][256]*field{

  MesgNumOhrSettings: {},

- MesgNumTimeInZone: {},
+ MesgNumTimeInZone: {
+     2: {0, 1, types.Fit(38), 8},
+ },

  MesgNumZonesTarget: {
      1: {0, 1, types.Fit(2), 1},

I'm pretty sure this is a bug in the fitgen module. Parsing the spreadsheet works correctly but when it's time to generate the code, we get empty structs. The codegen module is pretty complicated and I haven't had time to debug further.

No fields for the TimeInZone message are enabled in the default product profile called EXAMPLE in Profile spreadsheet, i.e. all those rows a have no value in the EXAMPLE column. A value of 1 will make fitgen generate those fields.

The additions you did for file_types.go still needs to be there; I can look at adding those.

honza commented 5 months ago

No fields for the TimeInZone message are enabled in the default product profile called EXAMPLE in Profile spreadsheet, i.e. all those rows a have no value in the EXAMPLE column. A value of 1 will make fitgen generate those fields.

TIL, thanks!