File.cs 12 KB

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