Skip to content

Commit

Permalink
reader: intial developer data fields support
Browse files Browse the repository at this point in the history
Updates #21.

Partially based on code provided by @beyoung:
#59
  • Loading branch information
tormoder committed Jan 11, 2022
1 parent 21e0bfe commit 5f540ea
Show file tree
Hide file tree
Showing 31 changed files with 117 additions and 43 deletions.
27 changes: 12 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,25 +21,21 @@ are required for running the full test suite and benchmarks.

_Note:_ 0.6.0 contains a breaking change, see #46.

### FIT SDK Version Support
### Version Support

**Current supported FIT SDK version:** 21.67
The current supported FIT SDK version is **21.67**.

**Warning:** Data Developers Fields are not yet supported (#21).
Developer data fields are currently only _partially_ supported.
At the moment the decoder parses Developer Data Field Descriptions, Developer Data ID Messages and Field Description Messages.
The decoder currently discards developer data fields found in records.

Older supported profile versions:
The encoder will currently (silently) ignore anything related to Developer data fields,
This also means that encoding will not fail if protocol version 2 is specified for a file header.

* 21.60
* 21.40
* 21.38
* 21.32
* 20.90
* 20.43
* 20.27
* 20.14
* 16.20

Other profile versions may work, but have not been tested.
Developer data fields support is tracked by
[#21](https://github.com/tormoder/fit/issues/21)
and
[#64](https://github.com/tormoder/fit/issues/64).

### Features

Expand Down Expand Up @@ -79,3 +75,4 @@ $ go get github.com/tormoder/fit
- [colinrgodsey](https://github.com/colinrgodsey)
- [bpg](https://github.com/bpg)
- [pieterclaerhout](https://github.com/pieterclaerhout)
- [beyoung](https://github.com/beyoung)
1 change: 1 addition & 0 deletions consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ const (
compressedLocalMesgNumMask byte = 0x60

mesgDefinitionMask byte = 0x40
devDataMask byte = 0x20
mesgHeaderMask byte = 0x00
localMesgNumMask byte = 0x0F

Expand Down
8 changes: 8 additions & 0 deletions file.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ type File struct {
FileCreator *FileCreatorMsg
TimestampCorrelation *TimestampCorrelationMsg

// Developer data fields.
fieldDescriptionMsgs []*FieldDescriptionMsg
developerDataIdMsgs []*DeveloperDataIdMsg

// UnknownMessages is a slice of unknown messages encountered during
// decoding. It is sorted by message number.
UnknownMessages []UnknownMessage
Expand Down Expand Up @@ -73,6 +77,10 @@ func (f *File) add(msg reflect.Value) {
f.FileCreator = &tmp
case TimestampCorrelationMsg:
f.TimestampCorrelation = &tmp
case FieldDescriptionMsg:
f.fieldDescriptionMsgs = append(f.fieldDescriptionMsgs, &tmp)
case DeveloperDataIdMsg:
f.developerDataIdMsgs = append(f.developerDataIdMsgs, &tmp)
default:
f.msgAdder.add(msg)
}
Expand Down
52 changes: 45 additions & 7 deletions reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -315,17 +315,18 @@ func (d *decoder) readFull(p []byte) error {
}

type defmsg struct {
localMsgType uint8
arch binary.ByteOrder
globalMsgNum MesgNum
fields byte
fieldDefs []fieldDef
localMsgType uint8
arch binary.ByteOrder
globalMsgNum MesgNum
fields byte
fieldDefs []fieldDef
devDataFieldDescs []devDataFieldDesc
}

func (dm defmsg) String() string {
return fmt.Sprintf(
"local: %d | global: %v | arch: %v | fields: %d",
dm.localMsgType, dm.globalMsgNum, dm.arch, dm.fields,
"local: %d | global: %v | arch: %v | fields: %d | data dev fields: %d",
dm.localMsgType, dm.globalMsgNum, dm.arch, dm.fields, len(dm.devDataFieldDescs),
)
}

Expand All @@ -339,6 +340,16 @@ func (fd fieldDef) String() string {
return fmt.Sprintf("num: %d | size: %d | btype: %v", fd.num, fd.size, fd.btype)
}

type devDataFieldDesc struct {
fieldNum byte
size byte
devDataIndex byte
}

func (ddfd devDataFieldDesc) String() string {
return fmt.Sprintf("field number: %d | size: %d | developer data index: %d", ddfd.fieldNum, ddfd.size, ddfd.devDataIndex)
}

func (d *decoder) parseFileIdMsg() error {
b, err := d.readByte()
if err != nil {
Expand Down Expand Up @@ -452,6 +463,26 @@ func (d *decoder) parseDefinitionMessage(recordHeader byte) (*defmsg, error) {
dm.fieldDefs[i] = fd
}

if recordHeader&devDataMask == devDataMask {
numDevFields, err := d.readByte()
if err != nil {
return nil, fmt.Errorf("error reading number of developer data fields: %w", err)
}

if err = d.readFull(d.tmp[0 : 3*uint16(numDevFields)]); err != nil {
return nil, fmt.Errorf("error reading developer data field description data: %w", err)
}

dm.devDataFieldDescs = make([]devDataFieldDesc, numDevFields)
for i, ddfd := range dm.devDataFieldDescs {
ddfd.fieldNum = d.tmp[i*3]
ddfd.size = d.tmp[(i*3)+1]
ddfd.devDataIndex = d.tmp[(i*3)+2]
dm.devDataFieldDescs[i] = ddfd
}

}

if d.debug {
d.opts.logger.Println("definition message parsed:", dm)
}
Expand Down Expand Up @@ -669,6 +700,13 @@ func (d *decoder) parseDataFields(dm *defmsg, knownMsg bool, msgv reflect.Value)
}
}

for i, ddfd := range dm.devDataFieldDescs {
err := d.readFull(d.tmp[0:int(ddfd.size)])
if err != nil {
return reflect.Value{}, fmt.Errorf("error parsing data developer message: %v (field %d [%v] for [%v])", err, i, ddfd, dm)
}
}

if knownMsg && !msgv.IsValid() {
panic("internal decoder error: parse data fields: known message, but not (reflect) valid")
}
Expand Down
72 changes: 51 additions & 21 deletions reader_files_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ var decodeTestFiles = [...]struct {
"me",
"activity-small-fenix2-run.fit",
false,
4159419868942230381,
16272604713108132935,
true,
tdoAllWithDiscardLogger,
true,
Expand All @@ -27,7 +27,7 @@ var decodeTestFiles = [...]struct {
"fitsdk",
"Activity.fit",
false,
17200987073157828903,
5393063379197673570,
true,
tdoNone,
false,
Expand All @@ -37,7 +37,7 @@ var decodeTestFiles = [...]struct {
"fitsdk",
"MonitoringFile.fit",
false,
17438554766263628503,
11936585269915402423,
true,
tdoNone,
true,
Expand All @@ -47,7 +47,7 @@ var decodeTestFiles = [...]struct {
"fitsdk",
"Settings.fit",
false,
13777716833885766673,
18422634047426156243,
true,
tdoNone,
false,
Expand All @@ -57,7 +57,7 @@ var decodeTestFiles = [...]struct {
"fitsdk",
"WeightScaleMultiUser.fit",
false,
5002902390457186100,
15668939108214135507,
true,
tdoNone,
false,
Expand All @@ -67,7 +67,7 @@ var decodeTestFiles = [...]struct {
"fitsdk",
"WorkoutCustomTargetValues.fit",
false,
1795447305759253807,
14786462533817802322,
true,
tdoNone,
false,
Expand All @@ -77,7 +77,7 @@ var decodeTestFiles = [...]struct {
"fitsdk",
"WorkoutIndividualSteps.fit",
false,
13432157117047365566,
5594723163894392697,
true,
tdoNone,
false,
Expand All @@ -87,7 +87,7 @@ var decodeTestFiles = [...]struct {
"fitsdk",
"WorkoutRepeatGreaterThanStep.fit",
false,
10036597401811670605,
17460594330644784595,
true,
tdoNone,
false,
Expand All @@ -97,7 +97,7 @@ var decodeTestFiles = [...]struct {
"fitsdk",
"WorkoutRepeatSteps.fit",
false,
2000919626000542509,
4256650519608265147,
true,
tdoNone,
false,
Expand All @@ -107,7 +107,7 @@ var decodeTestFiles = [...]struct {
"fitsdk",
"WeightScaleSingleUser.fit",
false,
170466418772016754,
16394851171432919279,
true,
tdoNone,
false,
Expand All @@ -117,7 +117,7 @@ var decodeTestFiles = [...]struct {
"python-fitparse",
"garmin-edge-500-activitiy.fit",
false,
6593572577964555599,
17514013668470756651,
true,
tdoNone,
false,
Expand All @@ -127,7 +127,7 @@ var decodeTestFiles = [...]struct {
"python-fitparse",
"sample-activity-indoor-trainer.fit",
false,
16971736118512546826,
945649812206588852,
true,
tdoNone,
true,
Expand All @@ -147,7 +147,7 @@ var decodeTestFiles = [...]struct {
"python-fitparse",
"antfs-dump.63.fit",
false,
17636932664957499573,
6282273622209975218,
true,
tdoNone,
true,
Expand All @@ -157,7 +157,7 @@ var decodeTestFiles = [...]struct {
"sram",
"Settings.fit",
false,
15334527618026463003,
5866657363356029809,
true,
tdoNone,
true,
Expand All @@ -167,7 +167,7 @@ var decodeTestFiles = [...]struct {
"sram",
"Settings2.fit",
false,
3203160144706376883,
15709312684722569429,
true,
tdoNone,
true,
Expand All @@ -177,7 +177,7 @@ var decodeTestFiles = [...]struct {
"dcrainmaker",
"Edge810-Vector-2013-08-16-15-35-10.fit",
false,
7445399672663916413,
12420128971150793206,
true,
tdoNone,
true,
Expand All @@ -187,7 +187,7 @@ var decodeTestFiles = [...]struct {
"misc",
"2013-02-06-12-11-14.fit",
false,
13418237519260555178,
11959686082894445424,
true,
tdoNone,
false,
Expand All @@ -197,7 +197,7 @@ var decodeTestFiles = [...]struct {
"misc",
"2015-10-13-08-43-15.fit",
false,
9824244170689694008,
16776362073923423348,
true,
tdoNone,
false,
Expand All @@ -207,7 +207,7 @@ var decodeTestFiles = [...]struct {
"bpg",
"garmin.fit",
false,
8578045933883478770,
11468599866908951097,
true,
tdoNone,
false,
Expand All @@ -217,7 +217,7 @@ var decodeTestFiles = [...]struct {
"corrupt",
"activity-filecrc.fit",
true,
1128179132232601357,
13015127050946751954,
true,
tdoNone,
false,
Expand All @@ -227,10 +227,40 @@ var decodeTestFiles = [...]struct {
"corrupt",
"activity-unexpected-eof.fit",
true,
3268441259118371812,
5112456444205297678,
true,
tdoNone,
false,
"",
},
{
"misc",
"0134902991.fit",
false,
1269717752691992296,
true,
tdoNone,
true,
"Contains developer data fields",
},
{
"misc",
"mornindew-broken.fit",
false,
864390381975294256,
true,
tdoNone,
true,
"Contains developer data fields",
},
{
"fitsdk",
"DeveloperData.fit",
false,
7735802126653373100,
true,
tdoNone,
true,
"Contains developer data fields",
},
}
Binary file modified testdata/bpg/garmin.fit.golden.gz
Binary file not shown.
Binary file modified testdata/corrupt/activity-filecrc.fit.golden.gz
Binary file not shown.
Binary file modified testdata/corrupt/activity-unexpected-eof.fit.golden.gz
Binary file not shown.
Binary file not shown.
Binary file modified testdata/fitsdk/Activity.fit.golden.gz
Binary file not shown.
Binary file added testdata/fitsdk/DeveloperData.fit.golden.gz
Binary file not shown.
Binary file modified testdata/fitsdk/MonitoringFile.fit.golden.gz
Binary file not shown.
Binary file modified testdata/fitsdk/Settings.fit.golden.gz
Binary file not shown.
Binary file modified testdata/fitsdk/WeightScaleMultiUser.fit.golden.gz
Binary file not shown.
Binary file modified testdata/fitsdk/WeightScaleSingleUser.fit.golden.gz
Binary file not shown.
Binary file modified testdata/fitsdk/WorkoutCustomTargetValues.fit.golden.gz
Binary file not shown.
Binary file modified testdata/fitsdk/WorkoutIndividualSteps.fit.golden.gz
Binary file not shown.
Binary file modified testdata/fitsdk/WorkoutRepeatGreaterThanStep.fit.golden.gz
Binary file not shown.
Binary file modified testdata/fitsdk/WorkoutRepeatSteps.fit.golden.gz
Binary file not shown.
Binary file modified testdata/me/activity-small-fenix2-run.fit.golden.gz
Binary file not shown.
Binary file added testdata/misc/0134902991.fit
Binary file not shown.
Binary file added testdata/misc/0134902991.fit.golden.gz
Binary file not shown.
Binary file modified testdata/misc/2013-02-06-12-11-14.fit.golden.gz
Binary file not shown.
Binary file modified testdata/misc/2015-10-13-08-43-15.fit.golden.gz
Binary file not shown.
Binary file added testdata/misc/mornindew-broken.fit
Binary file not shown.
Binary file added testdata/misc/mornindew-broken.fit.golden.gz
Binary file not shown.
Binary file modified testdata/python-fitparse/antfs-dump.63.fit.golden.gz
Binary file not shown.
Binary file modified testdata/python-fitparse/garmin-edge-500-activitiy.fit.golden.gz
Binary file not shown.
Binary file not shown.
Binary file modified testdata/sram/Settings.fit.golden.gz
Binary file not shown.
Binary file modified testdata/sram/Settings2.fit.golden.gz
Binary file not shown.

0 comments on commit 5f540ea

Please sign in to comment.