1 /**********************************************************************************************
2 Copyright (C) 2015 Ivo Kronenberg <>
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17 **********************************************************************************************/
18
19 #include "gis/fit/decoder/CFitFieldBuilder.h"
20 #include "gis/fit/decoder/CFitFieldDataState.h"
21 #include "gis/fit/defs/CFitBaseType.h"
22 #include "gis/fit/defs/CFitProfileLookup.h"
23 #include "gis/fit/defs/fit_const.h"
24 #include "gis/fit/defs/fit_enums.h"
25 #include "gis/fit/defs/fit_fields.h"
26
reset()27 void CFitFieldDataState::reset()
28 {
29 fieldDataIndex = 0;
30 fieldIndex = 0;
31 devFieldIndex = 0;
32 }
33
34
process(quint8 & dataByte)35 decode_state_e CFitFieldDataState::process(quint8& dataByte)
36 {
37 CFitMessage& mesg = *latestMessage();
38 CFitDefinitionMessage* defMesg = definition(mesg.getLocalMesgNr());
39
40 // add the read byte to the data array
41 fieldData[fieldDataIndex++] = dataByte;
42
43 handleFitField();
44 bool allFieldRead = fieldIndex >= defMesg->getNrOfFields();
45 if(allFieldRead)
46 {
47 handleDevField();
48 }
49 bool allDevFielRead = devFieldIndex >= defMesg->getNrOfDevFields();
50
51 if (allFieldRead && allDevFielRead)
52 {
53 // Now that the entire message is decoded we may evaluate subfields and expand components
54 CFitFieldBuilder::evaluateSubfieldsAndExpandComponents(mesg);
55
56 devProfile(mesg);
57
58 reset();
59 FITDEBUG(2, qDebug() << mesg.messageInfo())
60 // after all fields read, go to next record header
61 return eDecoderStateRecord;
62 }
63
64 // there are more fields to read for the current message
65 return eDecoderStateFieldData;
66 }
67
68
handleFitField()69 bool CFitFieldDataState::handleFitField()
70 {
71 CFitMessage& mesg = *latestMessage();
72 CFitDefinitionMessage* defMesg = definition(mesg.getLocalMesgNr());
73
74 if (fieldIndex < defMesg->getNrOfFields())
75 {
76 const CFitFieldDefinition& fieldDef = defMesg->getFieldByIndex(fieldIndex);
77 if (fieldDataIndex >= fieldDef.getSize())
78 {
79 // all bytes are read for current field
80
81 // new field with data
82 CFitField f = CFitFieldBuilder::buildField(fieldDef, fieldData, mesg);
83 mesg.addField(f);
84
85 // The special case time record.
86 // timestamp has always the same value for all enums. it does not matter against which we're comparing.
87 if (fieldDef.getDefNr() == eRecordTimestamp)
88 {
89 setTimestamp(f.getValue().toUInt());
90 }
91
92 // new field follows, reset
93 fieldDataIndex = 0;
94 fieldIndex++;
95 }
96 }
97 return fieldIndex >= defMesg->getNrOfFields();
98 }
99
handleDevField()100 bool CFitFieldDataState::handleDevField()
101 {
102 CFitMessage& mesg = *latestMessage();
103 CFitDefinitionMessage* defMesg = definition(mesg.getLocalMesgNr());
104
105 if (devFieldIndex < defMesg->getNrOfDevFields())
106 {
107 const CFitFieldDefinition& fieldDef = defMesg->getDevFieldByIndex(devFieldIndex);
108 if (fieldDataIndex >= fieldDef.getSize())
109 {
110 // handling developer data for mapping the field data to its definitions:
111 // part 2, reading field data and attach dynamic profile
112 CFitFieldProfile* fieldProfile = devFieldProfile(fieldDef.getDevProfileId());
113 if (fieldProfile->getBaseType().nr() == eBaseTypeNrInvalid)
114 {
115 // test if profile exists
116 throw tr("Missing field definition for development field.");
117 }
118
119 CFitField f = CFitFieldBuilder::buildField(*fieldProfile, fieldDef, fieldData, mesg);
120 mesg.addField(f);
121
122 // new field follows, reset
123 fieldDataIndex = 0;
124 devFieldIndex++;
125 }
126 }
127 return devFieldIndex >= defMesg->getNrOfDevFields();
128 }
129
devProfile(CFitMessage & mesg)130 void CFitFieldDataState::devProfile(CFitMessage& mesg)
131 {
132 // handling developer data for mapping the field data to its definitions:
133 // part 1, dynamic profiles creation
134 if (mesg.getGlobalMesgNr() == eMesgNumDeveloperDataId)
135 {
136 // Get developer ID
137 quint8 devDataIdx = fitDevDataIndexInvalid;
138 const QList<CFitField>& fields = mesg.getFields();
139 for (const CFitField& field : fields)
140 {
141 if (field.isValidValue() && field.getFieldDefNr() == eDeveloperDataIdDeveloperDataIndex)
142 {
143 devDataIdx = (quint8) field.getValue().toUInt();
144 break;
145 }
146 }
147 // Delete all developer field profiles for this ID
148 if ( devDataIdx != fitDevDataIndexInvalid )
149 {
150 clearDevFieldProfiles(devDataIdx);
151 }
152 }
153 if (mesg.getGlobalMesgNr() == eMesgNumFieldDescription)
154 {
155 CFitFieldProfile devFieldProfile = buildDevFieldProfile(mesg);
156 FITDEBUG(2, qDebug() << devFieldProfile.fieldProfileInfo());
157 addDevFieldProfile(devFieldProfile);
158 }
159 }
160
buildDevFieldProfile(CFitMessage & mesg)161 CFitFieldProfile CFitFieldDataState::buildDevFieldProfile(CFitMessage& mesg)
162 {
163 QString fieldName;
164 quint8 fieldDefNr = 0;
165 quint8 devDataIdx = 0;
166 quint8 baseType = eBaseTypeNrInvalid;
167 qreal scale = 0;
168 quint8 array = 0;
169 QString components;
170 qint16 offset = 0;
171 QString units;
172 QString bits;
173 QString accumulate;
174 quint8 baseUnitId = 0;
175 quint8 natvieMesgNum = 0;
176 quint8 nativeFieldNum = 0;
177
178 const QList<CFitField>& fields = mesg.getFields();
179 for (const CFitField& field : fields)
180 {
181 if (field.isValidValue())
182 {
183 switch (field.getFieldDefNr())
184 {
185 case eFieldDescriptionDeveloperDataIndex:
186 devDataIdx = (quint8) field.getValue().toUInt();
187 break;
188
189 case eFieldDescriptionFieldDefinitionNumber:
190 fieldDefNr = (quint8) field.getValue().toUInt();
191 break;
192
193 case eFieldDescriptionFitBaseTypeId:
194 baseType = (quint8) field.getValue().toUInt(); // enum
195 break;
196
197 case eFieldDescriptionFieldName:
198 fieldName = field.getValue().toString();
199 break;
200
201 case eFieldDescriptionArray:
202 array = (quint8) field.getValue().toUInt();
203 break;
204
205 case eFieldDescriptionComponents:
206 components = field.getValue().toString();
207 break;
208
209 case eFieldDescriptionScale:
210 scale = (qreal) field.getValue().toDouble();
211 break;
212
213 case eFieldDescriptionOffset:
214 offset = (qint16) field.getValue().toInt();
215 break;
216
217 case eFieldDescriptionUnits:
218 units = field.getValue().toString();
219 break;
220
221 case eFieldDescriptionBits:
222 bits = field.getValue().toString();
223 break;
224
225 case eFieldDescriptionAccumulate:
226 accumulate = field.getValue().toString();
227 break;
228
229 case eFieldDescriptionFitBaseUnitId:
230 baseUnitId = (quint8) field.getValue().toUInt(); // enum
231 break;
232
233 case eFieldDescriptionNativeMesgNum:
234 natvieMesgNum = (quint8) field.getValue().toUInt(); // enum
235 break;
236
237 case eFieldDescriptionNativeFieldNum:
238 nativeFieldNum = (quint8) field.getValue().toUInt();
239 break;
240
241 default:
242 throw tr("FIT decoding error: invalid field def nr %1 while creating dev field profile.")
243 .arg(field.getFieldDefNr());
244 break;
245 }
246 }
247 }
248
249 Q_UNUSED(array)
250 Q_UNUSED(baseUnitId)
251
252 if (natvieMesgNum && nativeFieldNum)
253 {
254 const CFitFieldProfile* nativeFieldProfile = CFitProfileLookup::getFieldForProfile(natvieMesgNum, nativeFieldNum);
255 if (nativeFieldProfile->getBaseType().nr() == eBaseTypeNrInvalid)
256 {
257 qWarning() << "DEV field" << fieldName << " field profile for mesg num" << natvieMesgNum << "and field num" << nativeFieldNum << "does not exist.";
258 }
259 if (nativeFieldProfile->getUnits() != units)
260 {
261 qWarning() << "DEV field" << fieldName << "units" << units << " do not match existing profile units" << nativeFieldProfile->getUnits();
262 }
263 // scale and offset not allowed if a fit field is overwritten.
264 scale = 0;
265 offset = 0;
266 }
267
268 CFitFieldProfile devFieldProfile = CFitFieldProfile(nullptr, fieldName, *CFitBaseTypeMap::get(baseType), fieldDefNr, devDataIdx,
269 scale, offset, units, eFieldTypeDevelopment);
270 // for documentation: the development field profile may contain more fields in the future as can be adumbrated by
271 // the description field enumeration. According to the FIT specification Rev 2.3 table 4-9 - Field Description Messages
272 // those fields are not yet used.
273
274 return devFieldProfile;
275 }
276