Mesg.cs 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912
  1. #region Copyright
  2. ////////////////////////////////////////////////////////////////////////////////
  3. // The following FIT Protocol software provided may be used with FIT protocol
  4. // devices only and remains the copyrighted property of Dynastream Innovations Inc.
  5. // The software is being provided on an "as-is" basis and as an accommodation,
  6. // and therefore all warranties, representations, or guarantees of any kind
  7. // (whether express, implied or statutory) including, without limitation,
  8. // warranties of merchantability, non-infringement, or fitness for a particular
  9. // purpose, are specifically disclaimed.
  10. //
  11. // Copyright 2016 Dynastream Innovations Inc.
  12. ////////////////////////////////////////////////////////////////////////////////
  13. // ****WARNING**** This file is auto-generated! Do NOT edit this file.
  14. // Profile Version = 16.60Release
  15. // Tag = production-akw-16.60.00-0-g5d3d436
  16. ////////////////////////////////////////////////////////////////////////////////
  17. #endregion
  18. using System;
  19. using System.Collections.Generic;
  20. using System.Diagnostics;
  21. using System.Text;
  22. using System.IO;
  23. using Dynastream.Utility;
  24. namespace Dynastream.Fit
  25. {
  26. /// <summary>
  27. ///
  28. /// </summary>
  29. public class Mesg
  30. {
  31. #region Fields
  32. protected byte localNum = 0;
  33. protected uint systemTimeOffset = 0;
  34. public List<Field> fields = new List<Field>();
  35. #endregion
  36. #region Properties
  37. public string Name { get; set; }
  38. public ushort Num { get; set; }
  39. public byte LocalNum
  40. {
  41. get
  42. {
  43. return localNum;
  44. }
  45. set
  46. {
  47. if (value > Fit.LocalMesgNumMask)
  48. {
  49. throw new FitException("Mesg:LocalNum - Invalid Local message number " + value + ". Local message number must be < " + Fit.LocalMesgNumMask);
  50. }
  51. else
  52. {
  53. localNum = value;
  54. }
  55. }
  56. }
  57. #endregion
  58. #region Constructors
  59. public Mesg(Mesg mesg)
  60. {
  61. if (mesg == null)
  62. {
  63. this.Name = "unknown";
  64. this.Num = (ushort)MesgNum.Invalid;
  65. return;
  66. }
  67. this.Name = mesg.Name;
  68. this.Num = mesg.Num;
  69. this.LocalNum = mesg.LocalNum;
  70. this.systemTimeOffset = mesg.systemTimeOffset;
  71. foreach (Field field in mesg.fields)
  72. {
  73. if (field.GetNumValues() > 0)
  74. {
  75. this.fields.Add(new Field(field));
  76. }
  77. }
  78. }
  79. public Mesg(string name, ushort num)
  80. {
  81. this.Name = name;
  82. this.Num = num;
  83. }
  84. internal Mesg(ushort mesgNum)
  85. : this(Profile.GetMesg(mesgNum))
  86. {
  87. }
  88. public Mesg(Stream fitStream, MesgDefinition defnMesg)
  89. : this(defnMesg.GlobalMesgNum)
  90. {
  91. Read(fitStream, defnMesg);
  92. }
  93. #endregion
  94. #region Methods
  95. public void Read(Stream inStream, MesgDefinition defnMesg)
  96. {
  97. inStream.Position = 1;
  98. EndianBinaryReader mesgReader = new EndianBinaryReader(inStream, defnMesg.IsBigEndian);
  99. LocalNum = defnMesg.LocalMesgNum;
  100. foreach (FieldDefinition fieldDef in defnMesg.GetFields())
  101. {
  102. // It's possible the field type found in the field definition may
  103. // not agree with the type defined in the profile. The profile
  104. // type will be preferred for decode.
  105. Field field = GetField(fieldDef.Num);
  106. if (field == null)
  107. {
  108. // We normally won't have fields attached to our skeleton message,
  109. // as we add values we need to add the fields too based on the mesg,field
  110. // combo in the profile. Must derive from the profile so the scale etc
  111. // is correct
  112. field = new Field(Profile.GetMesg(this.Num).GetField(fieldDef.Num));
  113. if (field.Num == Fit.FieldNumInvalid)
  114. {
  115. // If there was no info in the profile the FieldNum will get set to invalid
  116. // so preserve the unknown fields info while we know it
  117. field.Num = fieldDef.Num;
  118. field.Type = fieldDef.Type;
  119. }
  120. SetField(field);
  121. }
  122. object value;
  123. // strings may be an array and are of variable length
  124. if ((field.Type & Fit.BaseTypeNumMask) == Fit.String)
  125. {
  126. List<byte> utf8Bytes = new List<byte>();
  127. byte b = new byte();
  128. for (int i = 0; i < fieldDef.Size; i++)
  129. {
  130. b = mesgReader.ReadByte();
  131. if (b == 0x00)
  132. {
  133. field.AddValue(utf8Bytes.ToArray());
  134. utf8Bytes.Clear();
  135. }
  136. else
  137. {
  138. utf8Bytes.Add(b);
  139. }
  140. }
  141. if (utf8Bytes.Count != 0)
  142. {
  143. field.AddValue(utf8Bytes.ToArray());
  144. utf8Bytes.Clear();
  145. }
  146. }
  147. else
  148. {
  149. int numElements = (int)fieldDef.Size / Fit.BaseType[field.Type & Fit.BaseTypeNumMask].size;
  150. for (int i = 0; i < numElements; i++)
  151. {
  152. bool invalid = true;
  153. switch (field.Type & Fit.BaseTypeNumMask)
  154. {
  155. case Fit.Enum:
  156. case Fit.Byte:
  157. case Fit.UInt8:
  158. case Fit.UInt8z:
  159. value = mesgReader.ReadByte();
  160. if ((byte)value != (byte)Fit.BaseType[field.Type & Fit.BaseTypeNumMask].invalidValue)
  161. invalid = false;
  162. break;
  163. case Fit.SInt8:
  164. value = mesgReader.ReadSByte();
  165. if ((sbyte)value != (sbyte)Fit.BaseType[field.Type & Fit.BaseTypeNumMask].invalidValue)
  166. invalid = false;
  167. break;
  168. case Fit.SInt16:
  169. value = mesgReader.ReadInt16();
  170. if ((short)value != (short)Fit.BaseType[field.Type & Fit.BaseTypeNumMask].invalidValue)
  171. invalid = false;
  172. break;
  173. case Fit.UInt16:
  174. case Fit.UInt16z:
  175. value = mesgReader.ReadUInt16();
  176. if ((ushort)value != (ushort)Fit.BaseType[field.Type & Fit.BaseTypeNumMask].invalidValue)
  177. invalid = false;
  178. break;
  179. case Fit.SInt32:
  180. value = mesgReader.ReadInt32();
  181. if ((int)value != (int)Fit.BaseType[field.Type & Fit.BaseTypeNumMask].invalidValue)
  182. invalid = false;
  183. break;
  184. case Fit.UInt32:
  185. case Fit.UInt32z:
  186. value = mesgReader.ReadUInt32();
  187. if ((uint)value != (uint)Fit.BaseType[field.Type & Fit.BaseTypeNumMask].invalidValue)
  188. invalid = false;
  189. break;
  190. case Fit.Float32:
  191. value = mesgReader.ReadSingle();
  192. // TODO! Unless we find a different way to store floats, we dont have a good way to compare
  193. if (!float.IsNaN((float)value))
  194. invalid = false;
  195. break;
  196. case Fit.Float64:
  197. value = mesgReader.ReadDouble();
  198. // TODO! Unless we find a different way to store floats, we dont have a good way to compare
  199. if (!double.IsNaN((double)value))
  200. invalid = false;
  201. break;
  202. default:
  203. value = mesgReader.ReadBytes(fieldDef.Size);
  204. break;
  205. }
  206. if (!invalid || numElements > 1)
  207. field.SetRawValue(i, value);
  208. }
  209. }
  210. }
  211. }
  212. public void Write(Stream outStream)
  213. {
  214. Write(outStream, null);
  215. }
  216. public void Write(Stream outStream, MesgDefinition mesgDef)
  217. {
  218. if (mesgDef == null)
  219. {
  220. mesgDef = new MesgDefinition(this);
  221. }
  222. EndianBinaryWriter bw = new EndianBinaryWriter(outStream, mesgDef.IsBigEndian);
  223. bw.Write(LocalNum);
  224. foreach (FieldDefinition fieldDef in mesgDef.GetFields())
  225. {
  226. Field field = GetField(fieldDef.Num);
  227. if (null == field)
  228. {
  229. field = Profile.GetField(this.Num, fieldDef.Num);
  230. if( null != field )
  231. {
  232. fields.Add(field);
  233. }
  234. else
  235. {
  236. //Field does not exist in profile, continue to next field
  237. continue;
  238. }
  239. }
  240. // The field could be blank, correctly formed or partially filled
  241. while (field.GetSize() < fieldDef.Size)
  242. {
  243. if ((field.GetNumValues() == 1) && ((fieldDef.Type & Fit.BaseTypeNumMask) == Fit.String))
  244. {
  245. //Has to be a string.
  246. try
  247. {
  248. byte[] value = (byte[])field.GetValue();
  249. List<byte> temp = new List<byte>(value);
  250. while (temp.Count < fieldDef.Size)
  251. temp.Add(Convert.ToByte(Fit.BaseType[fieldDef.Type & Fit.BaseTypeNumMask].invalidValue));
  252. field.SetValue(temp.ToArray());
  253. }
  254. catch (Exception) { throw new FitException("Exception occurred while resizing field to match definition."); }
  255. }
  256. else
  257. {
  258. field.AddValue(Fit.BaseType[fieldDef.Type & Fit.BaseTypeNumMask].invalidValue);
  259. }
  260. }
  261. for (int i = 0; i < field.GetNumValues(); i++)
  262. {
  263. object value = field.GetRawValue(i);
  264. if (value == null)
  265. {
  266. value = Fit.BaseType[fieldDef.Type & Fit.BaseTypeNumMask].invalidValue;
  267. }
  268. switch (fieldDef.Type & Fit.BaseTypeNumMask)
  269. {
  270. case Fit.Enum:
  271. case Fit.Byte:
  272. case Fit.UInt8:
  273. case Fit.UInt8z:
  274. bw.Write((byte)value);
  275. break;
  276. case Fit.SInt8:
  277. bw.Write((sbyte)value);
  278. break;
  279. case Fit.SInt16:
  280. bw.Write((short)value);
  281. break;
  282. case Fit.UInt16:
  283. case Fit.UInt16z:
  284. bw.Write((ushort)value);
  285. break;
  286. case Fit.SInt32:
  287. bw.Write((int)value);
  288. break;
  289. case Fit.UInt32:
  290. case Fit.UInt32z:
  291. bw.Write((uint)value);
  292. break;
  293. case Fit.Float32:
  294. bw.Write((float)value);
  295. break;
  296. case Fit.Float64:
  297. bw.Write((double)value);
  298. break;
  299. case Fit.String:
  300. bw.Write((byte[])value);
  301. // Write the null terminator
  302. bw.Write((byte)0x00);
  303. break;
  304. default:
  305. break;
  306. }
  307. }
  308. }
  309. }
  310. #region FieldList Manipulation Functions
  311. public bool HasField(byte fieldNum)
  312. {
  313. foreach (Field field in fields)
  314. {
  315. if (field.Num == fieldNum)
  316. {
  317. return true;
  318. }
  319. }
  320. return false;
  321. }
  322. /// <summary>
  323. /// Replace an existing field, otherwise add a reference to fields list
  324. /// </summary>
  325. /// <param name="field">Caller allocated field</param>
  326. public void SetField(Field field)
  327. {
  328. for (int i = 0; i < fields.Count; i++)
  329. {
  330. if (fields[i].Num == field.Num)
  331. {
  332. fields[i] = field;
  333. return;
  334. }
  335. }
  336. fields.Add(field);
  337. }
  338. /// <summary>
  339. /// Insert a field at the desired index. If the field already exists in the mesg it is first removed.
  340. /// </summary>
  341. /// <param name="index">Index to insert the field, if index is out of range, the field is added to the end of the list</param>
  342. /// <param name="field">Caller allocated field</param>
  343. public void InsertField(int index, Field field)
  344. {
  345. // if message already contains this field, remove it
  346. for (int i = 0; i < fields.Count; i++)
  347. {
  348. if (fields[i].Num == field.Num)
  349. {
  350. fields.RemoveAt(i);
  351. }
  352. }
  353. // if the index is out of range, add to the end
  354. if (index < 0 || index > fields.Count)
  355. {
  356. fields.Add(field);
  357. }
  358. // insert the new field at desired index
  359. else
  360. {
  361. fields.Insert(index, field);
  362. }
  363. }
  364. public void SetFields(Mesg mesg)
  365. {
  366. if (mesg.Num != Num)
  367. {
  368. return;
  369. }
  370. foreach (Field field in mesg.fields)
  371. {
  372. SetField(new Field(field));
  373. }
  374. }
  375. public int GetNumFields()
  376. {
  377. return fields.Count;
  378. }
  379. public Field GetField(byte fieldNum)
  380. {
  381. foreach (Field field in fields)
  382. {
  383. if (field.Num == fieldNum)
  384. {
  385. return field;
  386. }
  387. }
  388. return null;
  389. }
  390. public Field GetField(string fieldName)
  391. {
  392. return GetField(fieldName, true);
  393. }
  394. public Field GetField(string fieldName, bool checkMesgSupportForSubFields)
  395. {
  396. foreach (Field field in fields)
  397. {
  398. if (field.Name == fieldName)
  399. {
  400. return field;
  401. }
  402. foreach (Subfield subfield in field.subfields)
  403. {
  404. if ((subfield.Name == fieldName) && (!checkMesgSupportForSubFields || (subfield.CanMesgSupport(this))))
  405. {
  406. return field;
  407. }
  408. }
  409. }
  410. return null;
  411. }
  412. public ushort GetActiveSubFieldIndex(byte fieldNum)
  413. {
  414. Field testField = new Field(this.GetField(fieldNum));
  415. if (testField == null)
  416. {
  417. return Fit.SubfieldIndexMainField;
  418. }
  419. for (ushort i = 0; i < testField.subfields.Count; i++)
  420. {
  421. if (testField.subfields[i].CanMesgSupport(this))
  422. {
  423. return i;
  424. }
  425. }
  426. return Fit.SubfieldIndexMainField;
  427. }
  428. public string GetActiveSubFieldName(byte fieldNum)
  429. {
  430. Field testField = new Field(this.GetField(fieldNum));
  431. if (testField == null)
  432. {
  433. return Fit.SubfieldNameMainField;
  434. }
  435. foreach (Subfield subfield in testField.subfields)
  436. {
  437. if (subfield.CanMesgSupport(this))
  438. {
  439. return subfield.Name;
  440. }
  441. }
  442. return Fit.SubfieldNameMainField;
  443. }
  444. #endregion
  445. public int GetNumFieldValues(byte fieldNum)
  446. {
  447. Field field = GetField(fieldNum);
  448. if (field != null)
  449. {
  450. return field.GetNumValues();
  451. }
  452. return 0;
  453. }
  454. public int GetNumFieldValues(String fieldName)
  455. {
  456. Field field = GetField(fieldName);
  457. if (field != null)
  458. {
  459. return field.GetNumValues();
  460. }
  461. return 0;
  462. }
  463. public int GetNumFieldValues(byte fieldNum, ushort subfieldIndex)
  464. {
  465. Field field = GetField(fieldNum);
  466. if (field == null)
  467. {
  468. return 0;
  469. }
  470. if (subfieldIndex == Fit.SubfieldIndexActiveSubfield)
  471. {
  472. return field.GetNumValues();
  473. }
  474. Subfield subfield = field.GetSubfield(subfieldIndex);
  475. if ((subfield == null) || (subfield.CanMesgSupport(this)))
  476. {
  477. return field.GetNumValues();
  478. }
  479. else
  480. {
  481. return 0;
  482. }
  483. }
  484. public int GetNumFieldValues(byte fieldNum, string subfieldName)
  485. {
  486. Field field = GetField(fieldNum);
  487. if (field == null)
  488. {
  489. return 0;
  490. }
  491. Subfield subfield = field.GetSubfield(subfieldName);
  492. if ((subfield == null) || (subfield.CanMesgSupport(this)))
  493. {
  494. return field.GetNumValues();
  495. }
  496. else
  497. {
  498. return 0;
  499. }
  500. }
  501. public object GetFieldValue(byte fieldNum)
  502. {
  503. return GetFieldValue(fieldNum, 0, Fit.SubfieldIndexActiveSubfield);
  504. }
  505. public object GetFieldValue(byte fieldNum, int fieldArrayIndex)
  506. {
  507. return GetFieldValue(fieldNum, fieldArrayIndex, Fit.SubfieldIndexActiveSubfield);
  508. }
  509. public object GetFieldValue(byte fieldNum, int fieldArrayIndex, ushort subFieldIndex)
  510. {
  511. Field field = GetField(fieldNum);
  512. if (field == null)
  513. {
  514. return null;
  515. }
  516. if (subFieldIndex == Fit.SubfieldIndexActiveSubfield)
  517. {
  518. return field.GetValue(fieldArrayIndex, GetActiveSubFieldIndex(fieldNum));
  519. }
  520. else
  521. {
  522. Subfield subfield = field.GetSubfield(subFieldIndex);
  523. if ((subfield == null) || (subfield.CanMesgSupport(this)))
  524. {
  525. return field.GetValue(fieldArrayIndex, subFieldIndex);
  526. }
  527. else
  528. {
  529. return null;
  530. }
  531. }
  532. }
  533. public object GetFieldValue(byte fieldNum, int fieldArrayIndex, string subfieldName)
  534. {
  535. Field field = GetField(fieldNum);
  536. if (field == null)
  537. {
  538. return null;
  539. }
  540. Subfield subfield = field.GetSubfield(subfieldName);
  541. if ((subfield == null) || (subfield.CanMesgSupport(this)))
  542. {
  543. return field.GetValue(fieldArrayIndex, subfieldName);
  544. }
  545. else
  546. {
  547. return null;
  548. }
  549. }
  550. public object GetFieldValue(string name)
  551. {
  552. return GetFieldValue(name, 0);
  553. }
  554. public object GetFieldValue(string name, int fieldArrayIndex)
  555. {
  556. Field field = GetField(name, false);
  557. if (field == null)
  558. {
  559. return null;
  560. }
  561. Subfield subfield = field.GetSubfield(name);
  562. if ((subfield == null) || (subfield.CanMesgSupport(this)))
  563. {
  564. return field.GetValue(fieldArrayIndex, name);
  565. }
  566. else
  567. {
  568. return null;
  569. }
  570. }
  571. public bool GetIsFieldAccumulated(byte num)
  572. {
  573. Field field = GetField(num);
  574. if (field != null)
  575. {
  576. return field.IsAccumulated;
  577. }
  578. return false;
  579. }
  580. public void SetFieldValue(byte fieldNum, Object value)
  581. {
  582. SetFieldValue(fieldNum, 0, value, Fit.SubfieldIndexActiveSubfield);
  583. }
  584. public void SetFieldValue(byte fieldNum, int fieldArrayIndex, Object value)
  585. {
  586. SetFieldValue(fieldNum, fieldArrayIndex, value, Fit.SubfieldIndexActiveSubfield);
  587. }
  588. public void SetFieldValue(byte fieldNum, int fieldArrayIndex, Object value, ushort subfieldIndex)
  589. {
  590. if (subfieldIndex == Fit.SubfieldIndexActiveSubfield)
  591. {
  592. subfieldIndex = GetActiveSubFieldIndex(fieldNum);
  593. }
  594. else
  595. {
  596. Field testField = new Field(this.GetField(fieldNum));
  597. Subfield subfield = testField.GetSubfield(subfieldIndex);
  598. if ((subfield != null) && !(subfield.CanMesgSupport(this)))
  599. {
  600. return;
  601. }
  602. }
  603. Field field = GetField(fieldNum);
  604. if (field == null)
  605. {
  606. // We normally won't have fields attached to our skeleton message,
  607. // as we add values we need to add the fields too based on the mesg,field
  608. // combo in the profile.
  609. field = new Field(Profile.GetMesg(this.Num).GetField(fieldNum));
  610. if (field.Num == Fit.FieldNumInvalid)
  611. {
  612. // If there was no info in the profile our FieldNum will get set to invalid,
  613. // at least preserve FieldNum while we know it
  614. field.Num = fieldNum;
  615. }
  616. SetField(field);
  617. }
  618. field.SetValue(fieldArrayIndex, value, subfieldIndex);
  619. }
  620. public void SetFieldValue(byte fieldNum, int fieldArrayIndex, Object value, String subfieldName)
  621. {
  622. Field testField = new Field(this.GetField(fieldNum));
  623. Subfield subfield = testField.GetSubfield(subfieldName);
  624. if ((subfield != null) && !(subfield.CanMesgSupport(this)))
  625. {
  626. return;
  627. }
  628. Field field = GetField(fieldNum);
  629. if (field == null)
  630. {
  631. // We normally won't have fields attached to our skeleton message,
  632. // as we add values we need to add the fields too based on the mesg,field
  633. // combo in the profile.
  634. field = new Field(Profile.GetMesg(this.Num).GetField(fieldNum));
  635. if (field.Num == Fit.FieldNumInvalid)
  636. {
  637. // If there was no info in the profile our FieldNum will get set to invalid,
  638. // at least preserve FieldNum while we know it
  639. field.Num = fieldNum;
  640. }
  641. SetField(field);
  642. }
  643. field.SetValue(fieldArrayIndex, value, subfieldName);
  644. }
  645. public void SetFieldValue(String name, Object value)
  646. {
  647. SetFieldValue(name, 0, value);
  648. }
  649. public void SetFieldValue(String name, int fieldArrayIndex, Object value)
  650. {
  651. Field testField = new Field(this.GetField(name));
  652. Subfield subfield = testField.GetSubfield(name);
  653. if ((subfield != null) && !(subfield.CanMesgSupport(this)))
  654. {
  655. return;
  656. }
  657. Field field = GetField(name, false);
  658. if (field == null)
  659. {
  660. field = new Field(Profile.GetMesg(this.Num).GetField(name));
  661. SetField(field);
  662. }
  663. field.SetValue(fieldArrayIndex, value, name);
  664. }
  665. public DateTime TimestampToDateTime(uint timestamp)
  666. {
  667. DateTime dateTime = new DateTime(timestamp);
  668. dateTime.ConvertSystemTimeToUTC(systemTimeOffset);
  669. return dateTime;
  670. }
  671. public DateTime TimestampToDateTime(uint? timestamp)
  672. {
  673. DateTime dateTime = null;
  674. if (timestamp != null)
  675. {
  676. dateTime = new DateTime(timestamp ?? 0);
  677. dateTime.ConvertSystemTimeToUTC(systemTimeOffset);
  678. }
  679. return dateTime;
  680. }
  681. private IEnumerable<FieldComponentExpansion> ExpandComponentsInList(List<FieldComponent> componentList, Field currentField, int offset, Accumulator accumulator)
  682. {
  683. // When components.Count > 0 a field will be created and appended to the field list
  684. if ((componentList != null) && (componentList.Count > 0))
  685. {
  686. foreach (FieldComponent fC in componentList)
  687. {
  688. if (fC.fieldNum != Fit.FieldNumInvalid)
  689. {
  690. //Create a new field to expand into
  691. Field newField = new Field(Profile.GetMesg(this.Num).GetField(fC.fieldNum));
  692. //cache a field that we use to set properties on
  693. Field f = this.GetField(newField.Num);
  694. // GetBitsValue will not return more bits than the componentField type can hold.
  695. // This means strings are built one letter at a time when using components
  696. // which is a bad idea to start with)
  697. long? bitsValue = currentField.GetBitsValue(offset, fC.bits, newField.Type);
  698. if (bitsValue == null)
  699. {
  700. break;
  701. }
  702. if (true == fC.accumulate)
  703. {
  704. bitsValue = accumulator.Accumulate(this.Num, fC.fieldNum, bitsValue.Value, fC.bits);
  705. }
  706. if (newField.IsNumeric())
  707. {
  708. double fbitsValue = Convert.ToDouble(bitsValue);
  709. fbitsValue = ((double)fbitsValue / fC.scale) - fC.offset;
  710. if (true == this.HasField(newField.Num))
  711. {
  712. f.SetValue(f.values.Count, fbitsValue);
  713. }
  714. else
  715. {
  716. newField.SetValue(fbitsValue);
  717. }
  718. }
  719. // Shouldn't apply scale/offset to string or enum
  720. else
  721. {
  722. object nonNumericBitsValue;
  723. // Ensure strings are added as byte[]
  724. if ((newField.Type & Fit.BaseTypeNumMask) == Fit.String)
  725. {
  726. nonNumericBitsValue = new byte[] { (byte)bitsValue };
  727. }
  728. else
  729. {
  730. nonNumericBitsValue = bitsValue;
  731. }
  732. if (true == this.HasField(newField.Num))
  733. {
  734. f.SetValue(f.values.Count, nonNumericBitsValue);
  735. }
  736. else
  737. {
  738. newField.SetValue(nonNumericBitsValue);
  739. }
  740. }
  741. offset += fC.bits;
  742. //Return each field as we iterate
  743. yield return new FieldComponentExpansion(newField, offset);
  744. }
  745. }
  746. }
  747. }
  748. public void ExpandComponents()
  749. {
  750. ExpandComponents(null);
  751. }
  752. public void ExpandComponents(Accumulator accumulator)
  753. {
  754. // Traverse the field list
  755. // Change to for loop so we can add items as we iterate
  756. for (int i = 0; i < fields.Count; ++i)
  757. {
  758. List<FieldComponent> componentList = null;
  759. // Determine the active subfield
  760. ushort activeSubfield = GetActiveSubFieldIndex(fields[i].Num);
  761. if (activeSubfield == Fit.SubfieldIndexMainField)
  762. {
  763. componentList = fields[i].components;
  764. }
  765. else
  766. {
  767. componentList = fields[i].GetSubfield(activeSubfield).Components;
  768. }
  769. // Traverse the component list
  770. int offset = 0;
  771. foreach (FieldComponentExpansion f in ExpandComponentsInList(componentList, fields[i], offset, accumulator))
  772. {
  773. //Add the new field
  774. fields.Add(f.GetField());
  775. //update offset
  776. offset = f.GetOffset();
  777. }
  778. }
  779. }
  780. #endregion
  781. }
  782. internal class FieldComponentExpansion
  783. {
  784. private int offset;
  785. private Field field;
  786. public FieldComponentExpansion(Field f, int offset)
  787. {
  788. field = f;
  789. this.offset = offset;
  790. }
  791. public int GetOffset()
  792. {
  793. return offset;
  794. }
  795. public Field GetField()
  796. {
  797. return field;
  798. }
  799. }
  800. } // namespace