File.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Linq;
  6. namespace NationalInstruments.Tdms
  7. {
  8. public class File : IEnumerable<Group>, IDisposable
  9. {
  10. private readonly Lazy<Stream> _stream;
  11. private File()
  12. {
  13. Properties = new Dictionary<string, object>();
  14. Groups = new Dictionary<string, Group>();
  15. }
  16. public File(Stream stream) : this()
  17. {
  18. if (!stream.CanSeek) throw new ArgumentException("Only seekable streams supported.", "stream");
  19. _stream = new Lazy<Stream>(() => stream);
  20. }
  21. public File(string path) : this()
  22. {
  23. _stream = new Lazy<Stream>(() => new FileStream(path, FileMode.Open, FileAccess.Read));
  24. }
  25. public IDictionary<string, object> Properties { get; private set; }
  26. public IDictionary<string, Group> Groups { get; private set; }
  27. public File Open()
  28. {
  29. var reader = new Reader(_stream.Value);
  30. var metadata = LoadMetadata(reader).ToList();
  31. LoadFile(metadata);
  32. LoadGroups(Groups, metadata);
  33. LoadChannels(Groups, metadata, reader);
  34. return this;
  35. }
  36. /// <summary>
  37. /// This will re-write the TDMS file. Mostly used for write demonstration. Although, this will also defragment the file.
  38. /// </summary>
  39. /// <param name="path"></param>
  40. public void ReWrite(string path)
  41. {
  42. using (FileStream stream = new FileStream(path, FileMode.Create, FileAccess.Write))
  43. ReWrite(stream);
  44. }
  45. /// <summary>
  46. /// This will re-write the TDMS file. Mostly used for write demonstration. Although, this will also defragment the file.
  47. /// </summary>
  48. /// <param name="stream"></param>
  49. public void ReWrite(Stream stream)
  50. {
  51. WriteSegment segment = new WriteSegment(stream);
  52. segment.Header.TableOfContents.HasRawData = Groups.SelectMany(g => g.Value.Channels.Values, (g, c) => c.HasData).Any();
  53. //when we re-write the file, no data shall be interleaved. (It's an all or nothing situation, with only 1 segment)
  54. //segment.Header.TableOfContents.RawDataIsInterleaved = Groups.SelectMany(g => g.Value.Channels.Values, (g, c) => c.RawData.First().IsInterleaved).Any();
  55. //Top level
  56. Reader.Metadata m = new Reader.Metadata();
  57. m.Path = new string[0];
  58. m.Properties = Properties;
  59. segment.MetaData.Add(m);
  60. //Groups
  61. foreach(KeyValuePair<string, Group> group in Groups)
  62. {
  63. m = new Reader.Metadata();
  64. m.Path = new string[] { group.Key };
  65. m.Properties = group.Value.Properties;
  66. segment.MetaData.Add(m);
  67. //Channels
  68. foreach (KeyValuePair<string, Channel> channel in group.Value.Channels)
  69. {
  70. Reader.RawData[] raws = channel.Value.RawData.ToArray();
  71. //Add first part
  72. m = new Reader.Metadata();
  73. m.Path = new string[] { group.Key, channel.Key };
  74. m.Properties = channel.Value.Properties;
  75. m.RawData = raws?[0];
  76. segment.MetaData.Add(m);
  77. //Add the other parts (if any)
  78. for(int i = 1; i < raws?.Length; i++)
  79. {
  80. m = new Reader.Metadata();
  81. m.Path = new string[] { group.Key, channel.Key };
  82. m.RawData = raws[i];
  83. segment.MetaData.Add(m);
  84. }
  85. }
  86. }
  87. //Write all raw data
  88. Writer writer = segment.Open();
  89. var reader = new Reader(_stream.Value);
  90. foreach (KeyValuePair<string, Group> group in Groups)
  91. foreach (KeyValuePair<string, Channel> channel in group.Value.Channels)
  92. foreach (Reader.RawData raw in channel.Value.RawData)
  93. {
  94. var data = reader.ReadRawData(raw);
  95. raw.IsInterleaved = false; //when we re-write the file, no data shall be interleaved
  96. writer.WriteRawData(raw, data);
  97. }
  98. //close up
  99. segment.Close();
  100. }
  101. private void LoadFile(IEnumerable<Reader.Metadata> metadata)
  102. {
  103. metadata.Where(x => x.Path.Length == 0)
  104. .SelectMany(m => m.Properties).ToList()
  105. .ForEach(x => Properties[x.Key] = x.Value);
  106. }
  107. private static void LoadGroups(IDictionary<string, Group> groups, IEnumerable<Reader.Metadata> metadata)
  108. {
  109. var groupMetadata = metadata.Where(x => x.Path.Length == 1).
  110. GroupBy(x => x.Path[0], (k, r) => r.OrderByDescending(y => y.Properties.Count).First());
  111. foreach (var group in groupMetadata)
  112. groups.Add(group.Path[0], new Group(group.Path[0], group.Properties));
  113. // add implicit groups
  114. foreach (var groupName in metadata.Where(x => x.Path.Length > 1 && !groups.ContainsKey(x.Path[0])).Select(x => x.Path[0]))
  115. groups.Add(groupName, new Group(groupName, new Dictionary<string, object>()));
  116. }
  117. private static void LoadChannels(IDictionary<string, Group> groups, IEnumerable<Reader.Metadata> metadata, Reader reader)
  118. {
  119. var channelMetadata = metadata
  120. .Where(x => x.Path.Length == 2)
  121. .GroupBy(x => Tuple.Create(x.Path[0], x.Path[1]))
  122. .Join(groups, x => x.First().Path[0], x => x.Key, (c, g) => Tuple.Create(g.Value, c));
  123. foreach (var channel in channelMetadata)
  124. channel.Item1.Channels.Add(channel.Item2.First().Path[1], new Channel(channel.Item2.First().Path[1],
  125. channel.Item2.OrderByDescending(y => y.Properties.Count).First().Properties,
  126. channel.Item2.Where(x => x.RawData.Count > 0).Select(x => x.RawData), reader));
  127. }
  128. private static IEnumerable<Reader.Metadata> LoadMetadata(Reader reader)
  129. {
  130. var segments = GetSegments(reader).ToList();
  131. var prevMetaDataLookup = new Dictionary<string, Dictionary<string, Reader.Metadata>>();
  132. foreach (var segment in segments)
  133. {
  134. if (!(segment.TableOfContents.ContainsNewObjects ||
  135. segment.TableOfContents.HasDaqMxData ||
  136. segment.TableOfContents.HasMetaData ||
  137. segment.TableOfContents.HasRawData)) {
  138. continue;
  139. }
  140. var metadatas = reader.ReadMetadata(segment);
  141. long rawDataSize = 0;
  142. long nextOffset = segment.RawDataOffset;
  143. foreach (var m in metadatas)
  144. {
  145. if (m.RawData.Count == 0 && m.Path.Length > 1)
  146. {
  147. // apply previous metadata if available
  148. if (prevMetaDataLookup.ContainsKey(m.Path[0]) && prevMetaDataLookup[m.Path[0]].ContainsKey(m.Path[1]))
  149. {
  150. var prevMetaData = prevMetaDataLookup[m.Path[0]][m.Path[1]];
  151. if (prevMetaData != null)
  152. {
  153. m.RawData.Count = segment.TableOfContents.HasRawData ? prevMetaData.RawData.Count : 0;
  154. m.RawData.DataType = prevMetaData.RawData.DataType;
  155. m.RawData.ClrDataType = prevMetaData.RawData.ClrDataType;
  156. m.RawData.Offset = segment.RawDataOffset + rawDataSize;
  157. m.RawData.IsInterleaved = prevMetaData.RawData.IsInterleaved;
  158. m.RawData.InterleaveStride = prevMetaData.RawData.InterleaveStride;
  159. m.RawData.Size = prevMetaData.RawData.Size;
  160. m.RawData.Dimension = prevMetaData.RawData.Dimension;
  161. }
  162. }
  163. }
  164. if (m.RawData.IsInterleaved && segment.NextSegmentOffset <= 0)
  165. {
  166. m.RawData.Count = segment.NextSegmentOffset > 0
  167. ? (segment.NextSegmentOffset - m.RawData.Offset + m.RawData.InterleaveStride - 1)/
  168. m.RawData.InterleaveStride
  169. : (reader.FileSize - m.RawData.Offset + m.RawData.InterleaveStride - 1)/
  170. m.RawData.InterleaveStride;
  171. }
  172. if (m.Path.Length > 1)
  173. {
  174. rawDataSize += m.RawData.Size;
  175. nextOffset += m.RawData.Size;
  176. }
  177. }
  178. var implicitMetadatas = new List<Reader.Metadata>();
  179. if (metadatas.All(m => !m.RawData.IsInterleaved && m.RawData.Size > 0))
  180. {
  181. while (nextOffset < segment.NextSegmentOffset ||
  182. (segment.NextSegmentOffset == -1 && nextOffset < reader.FileSize))
  183. {
  184. // Incremental Meta Data see http://www.ni.com/white-paper/5696/en/#toc1
  185. foreach (var m in metadatas)
  186. {
  187. if (m.Path.Length > 1)
  188. {
  189. var implicitMetadata = new Reader.Metadata()
  190. {
  191. Path = m.Path,
  192. RawData = new Reader.RawData()
  193. {
  194. Count = m.RawData.Count,
  195. DataType = m.RawData.DataType,
  196. ClrDataType = m.RawData.ClrDataType,
  197. Offset = nextOffset,
  198. IsInterleaved = m.RawData.IsInterleaved,
  199. Size = m.RawData.Size,
  200. Dimension = m.RawData.Dimension
  201. },
  202. Properties = m.Properties
  203. };
  204. implicitMetadatas.Add(implicitMetadata);
  205. nextOffset += implicitMetadata.RawData.Size;
  206. }
  207. }
  208. }
  209. }
  210. var metadataWithImplicit = metadatas.Concat(implicitMetadatas).ToList();
  211. foreach (var metadata in metadataWithImplicit)
  212. {
  213. if (metadata.Path.Length == 2)
  214. {
  215. if (!prevMetaDataLookup.ContainsKey(metadata.Path[0]))
  216. {
  217. prevMetaDataLookup[metadata.Path[0]] = new Dictionary<string, Reader.Metadata>();
  218. }
  219. prevMetaDataLookup[metadata.Path[0]][metadata.Path[1]] = metadata;
  220. }
  221. yield return metadata;
  222. }
  223. }
  224. }
  225. private static IEnumerable<Reader.Segment> GetSegments(Reader reader)
  226. {
  227. var segment = reader.ReadFirstSegment();
  228. while (segment != null)
  229. {
  230. yield return segment;
  231. segment = reader.ReadSegment(segment.NextSegmentOffset);
  232. }
  233. }
  234. public IEnumerator<Group> GetEnumerator() { return Groups.Values.GetEnumerator(); }
  235. IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
  236. public void Dispose()
  237. {
  238. _stream.Value.Dispose();
  239. }
  240. }
  241. }