tdms 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.travis.yml +10 -0
- data/LICENSE.txt +25 -0
- data/README.md +34 -0
- data/Rakefile +15 -0
- data/demo.rb +16 -0
- data/doc/data_types.txt +23 -0
- data/doc/example_disasm.txt +47 -0
- data/doc/tdms_format.txt +101 -0
- data/doc/usage.txt +46 -0
- data/lib/tdms.rb +8 -0
- data/lib/tdms/aggregate.rb +71 -0
- data/lib/tdms/channel.rb +100 -0
- data/lib/tdms/datatypes.rb +173 -0
- data/lib/tdms/document.rb +114 -0
- data/lib/tdms/path.rb +58 -0
- data/lib/tdms/property.rb +17 -0
- data/lib/tdms/segment.rb +12 -0
- data/lib/tdms/streaming.rb +82 -0
- data/test/build_fixtures/README.txt +25 -0
- data/test/build_fixtures/type_01_int8_one_segment.vi +0 -0
- data/test/build_fixtures/type_01_int8_three_segments.vi +0 -0
- data/test/build_fixtures/type_01_int8_two_channels_one_segment.vi +0 -0
- data/test/build_fixtures/type_02_int16_one_segment.vi +0 -0
- data/test/build_fixtures/type_02_int16_three_segments.vi +0 -0
- data/test/build_fixtures/type_02_int16_two_channels_one_segment.vi +0 -0
- data/test/build_fixtures/type_03_int32_one_segment.vi +0 -0
- data/test/build_fixtures/type_03_int32_three_segments.vi +0 -0
- data/test/build_fixtures/type_03_int32_two_channels_one_segment.vi +0 -0
- data/test/build_fixtures/type_04_int64_one_segment.vi +0 -0
- data/test/build_fixtures/type_04_int64_three_segments.vi +0 -0
- data/test/build_fixtures/type_04_int64_two_channels_one_segment.vi +0 -0
- data/test/build_fixtures/type_05_uint8_one_segment.vi +0 -0
- data/test/build_fixtures/type_05_uint8_three_segments.vi +0 -0
- data/test/build_fixtures/type_05_uint8_two_channels_one_segment.vi +0 -0
- data/test/build_fixtures/type_06_uint16_one_segment.vi +0 -0
- data/test/build_fixtures/type_06_uint16_three_segments.vi +0 -0
- data/test/build_fixtures/type_06_uint16_two_channels_one_segment.vi +0 -0
- data/test/build_fixtures/type_07_uint32_one_segment.vi +0 -0
- data/test/build_fixtures/type_07_uint32_three_segments.vi +0 -0
- data/test/build_fixtures/type_07_uint32_two_channels_one_segment.vi +0 -0
- data/test/build_fixtures/type_08_uint64_one_segment.vi +0 -0
- data/test/build_fixtures/type_08_uint64_three_segments.vi +0 -0
- data/test/build_fixtures/type_08_uint64_two_channels_one_segment.vi +0 -0
- data/test/build_fixtures/type_09_single_one_segment.vi +0 -0
- data/test/build_fixtures/type_09_single_three_segments.vi +0 -0
- data/test/build_fixtures/type_09_single_two_channels_one_segment.vi +0 -0
- data/test/build_fixtures/type_0a_double_one_segment.vi +0 -0
- data/test/build_fixtures/type_0a_double_three_segments.vi +0 -0
- data/test/build_fixtures/type_0a_double_two_channels_one_segment.vi +0 -0
- data/test/build_fixtures/type_20_string_one_segment.vi +0 -0
- data/test/build_fixtures/type_20_string_three_segments.vi +0 -0
- data/test/build_fixtures/type_20_string_two_channels_one_segment.vi +0 -0
- data/test/build_fixtures/type_21_boolean_one_segment.vi +0 -0
- data/test/build_fixtures/type_21_boolean_three_segments.vi +0 -0
- data/test/build_fixtures/type_21_boolean_two_channels_one_segment.vi +0 -0
- data/test/build_fixtures/type_44_datetime_one_segment.vi +0 -0
- data/test/build_fixtures/type_44_timestamp_three_segments.vi +0 -0
- data/test/build_fixtures/type_44_timestamp_two_channels_one_segment.vi +0 -0
- data/test/fixtures/example.tdms +0 -0
- data/test/fixtures/type_01_int8_one_segment.tdms +0 -0
- data/test/fixtures/type_01_int8_three_segments.tdms +0 -0
- data/test/fixtures/type_01_int8_two_channels_one_segment.tdms +0 -0
- data/test/fixtures/type_02_int16_one_segment.tdms +0 -0
- data/test/fixtures/type_02_int16_three_segments.tdms +0 -0
- data/test/fixtures/type_02_int16_two_channels_one_segment.tdms +0 -0
- data/test/fixtures/type_03_int32_one_segment.tdms +0 -0
- data/test/fixtures/type_03_int32_three_segments.tdms +0 -0
- data/test/fixtures/type_03_int32_two_channels_one_segment.tdms +0 -0
- data/test/fixtures/type_04_int64_one_segment.tdms +0 -0
- data/test/fixtures/type_04_int64_three_segments.tdms +0 -0
- data/test/fixtures/type_04_int64_two_channels_one_segment.tdms +0 -0
- data/test/fixtures/type_05_uint8_one_segment.tdms +0 -0
- data/test/fixtures/type_05_uint8_three_segments.tdms +0 -0
- data/test/fixtures/type_05_uint8_two_channels_one_segment.tdms +0 -0
- data/test/fixtures/type_06_uint16_one_segment.tdms +0 -0
- data/test/fixtures/type_06_uint16_three_segments.tdms +0 -0
- data/test/fixtures/type_06_uint16_two_channels_one_segment.tdms +0 -0
- data/test/fixtures/type_07_uint32_one_segment.tdms +0 -0
- data/test/fixtures/type_07_uint32_three_segments.tdms +0 -0
- data/test/fixtures/type_07_uint32_two_channels_one_segment.tdms +0 -0
- data/test/fixtures/type_08_uint64_one_segment.tdms +0 -0
- data/test/fixtures/type_08_uint64_three_segments.tdms +0 -0
- data/test/fixtures/type_08_uint64_two_channels_one_segment.tdms +0 -0
- data/test/fixtures/type_09_single_one_segment.tdms +0 -0
- data/test/fixtures/type_09_single_three_segments.tdms +0 -0
- data/test/fixtures/type_09_single_two_channels_one_segment.tdms +0 -0
- data/test/fixtures/type_0a_double_one_segment.tdms +0 -0
- data/test/fixtures/type_0a_double_three_segments.tdms +0 -0
- data/test/fixtures/type_0a_double_two_channels_one_segment.tdms +0 -0
- data/test/fixtures/type_19_single_with_unit_one_segment.tdms +0 -0
- data/test/fixtures/type_19_single_with_unit_three_segments.tdms +0 -0
- data/test/fixtures/type_19_single_with_unit_two_channels_one_segment.tdms +0 -0
- data/test/fixtures/type_1a_double_with_unit_one_segment.tdms +0 -0
- data/test/fixtures/type_1a_double_with_unit_three_segments.tdms +0 -0
- data/test/fixtures/type_1a_double_with_unit_two_channels_one_segment.tdms +0 -0
- data/test/fixtures/type_20_double_two_channels_one_segment.tdms +0 -0
- data/test/fixtures/type_20_string_one_segment.tdms +0 -0
- data/test/fixtures/type_20_string_three_segments.tdms +0 -0
- data/test/fixtures/type_20_string_two_channels_one_segment.tdms +0 -0
- data/test/fixtures/type_20_string_two_segments.tdms +0 -0
- data/test/fixtures/type_21_boolean_one_segment.tdms +0 -0
- data/test/fixtures/type_21_boolean_three_segments.tdms +0 -0
- data/test/fixtures/type_21_boolean_two_channels_one_segment.tdms +0 -0
- data/test/fixtures/type_44_timestamp_one_segment.tdms +0 -0
- data/test/fixtures/type_44_timestamp_three_segments.tdms +0 -0
- data/test/fixtures/type_44_timestamp_two_channels_one_segment.tdms +0 -0
- data/test/read_type_01_int8_test.rb +58 -0
- data/test/read_type_02_int16_test.rb +58 -0
- data/test/read_type_03_int32_test.rb +58 -0
- data/test/read_type_04_int64_test.rb +64 -0
- data/test/read_type_05_uint8_test.rb +56 -0
- data/test/read_type_06_uint16_test.rb +58 -0
- data/test/read_type_07_uint32_test.rb +58 -0
- data/test/read_type_08_uint64_test.rb +58 -0
- data/test/read_type_09_single_test.rb +58 -0
- data/test/read_type_0a_double_test.rb +58 -0
- data/test/read_type_19_single_with_unit_test.rb +58 -0
- data/test/read_type_1a_double_with_unit_test.rb +58 -0
- data/test/read_type_20_string_test.rb +60 -0
- data/test/read_type_21_boolean_test.rb +56 -0
- data/test/read_type_44_timestamp_test.rb +60 -0
- data/test/test_helper.rb +9 -0
- metadata +284 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
module Tdms
|
|
2
|
+
|
|
3
|
+
module DataType
|
|
4
|
+
|
|
5
|
+
class Base
|
|
6
|
+
attr_accessor :value
|
|
7
|
+
|
|
8
|
+
def initialize(value=nil)
|
|
9
|
+
@value = value
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class Int8 < Base
|
|
14
|
+
Id = 0x01
|
|
15
|
+
LengthInBytes = 1
|
|
16
|
+
|
|
17
|
+
def self.read_from_stream(tdms_file)
|
|
18
|
+
new(tdms_file.read_i8)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class Int16 < Base
|
|
23
|
+
Id = 0x02
|
|
24
|
+
LengthInBytes = 2
|
|
25
|
+
|
|
26
|
+
def self.read_from_stream(tdms_file)
|
|
27
|
+
new(tdms_file.read_i16)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
class Int32 < Base
|
|
32
|
+
Id = 0x03
|
|
33
|
+
LengthInBytes = 4
|
|
34
|
+
|
|
35
|
+
def self.read_from_stream(tdms_file)
|
|
36
|
+
new(tdms_file.read_i32)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class Int64 < Base
|
|
41
|
+
Id = 0x04
|
|
42
|
+
LengthInBytes = 8
|
|
43
|
+
|
|
44
|
+
def self.read_from_stream(tdms_file)
|
|
45
|
+
new(tdms_file.read_i64)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
class Uint8 < Base
|
|
50
|
+
Id = 0x05
|
|
51
|
+
LengthInBytes = 1
|
|
52
|
+
|
|
53
|
+
def self.read_from_stream(tdms_file)
|
|
54
|
+
new(tdms_file.read_u8)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
class Uint16 < Base
|
|
59
|
+
Id = 0x06
|
|
60
|
+
LengthInBytes = 2
|
|
61
|
+
|
|
62
|
+
def self.read_from_stream(tdms_file)
|
|
63
|
+
new(tdms_file.read_u16)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
class Uint32 < Base
|
|
68
|
+
Id = 0x07
|
|
69
|
+
LengthInBytes = 4
|
|
70
|
+
|
|
71
|
+
def self.read_from_stream(tdms_file)
|
|
72
|
+
new(tdms_file.read_u32)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
class Uint64 < Base
|
|
77
|
+
Id = 0x08
|
|
78
|
+
LengthInBytes = 8
|
|
79
|
+
|
|
80
|
+
def self.read_from_stream(tdms_file)
|
|
81
|
+
new(tdms_file.read_u64)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
class Single < Base
|
|
86
|
+
Id = 0x09
|
|
87
|
+
LengthInBytes = 4
|
|
88
|
+
|
|
89
|
+
def self.read_from_stream(tdms_file)
|
|
90
|
+
new(tdms_file.read_single)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
class Double < Base
|
|
95
|
+
Id = 0x0A
|
|
96
|
+
LengthInBytes = 8
|
|
97
|
+
|
|
98
|
+
def self.read_from_stream(tdms_file)
|
|
99
|
+
new(tdms_file.read_double)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
class SingleWithUnit < Base
|
|
104
|
+
Id = 0x19
|
|
105
|
+
LengthInBytes = 4
|
|
106
|
+
|
|
107
|
+
def self.read_from_stream(tdms_file)
|
|
108
|
+
new(tdms_file.read_single)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
class DoubleWithUnit < Base
|
|
113
|
+
Id = 0x1A
|
|
114
|
+
LengthInBytes = 8
|
|
115
|
+
|
|
116
|
+
def self.read_from_stream(tdms_file)
|
|
117
|
+
new(tdms_file.read_double)
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
class Utf8String < Base
|
|
122
|
+
Id = 0x20
|
|
123
|
+
LengthInBytes = nil
|
|
124
|
+
|
|
125
|
+
def self.read_from_stream(tdms_file)
|
|
126
|
+
new(tdms_file.read_utf8_string)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
class Boolean < Base
|
|
131
|
+
Id = 0x21
|
|
132
|
+
LengthInBytes = 1
|
|
133
|
+
|
|
134
|
+
def self.read_from_stream(tdms_file)
|
|
135
|
+
new(tdms_file.read_bool)
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
class Timestamp < Base
|
|
140
|
+
Id = 0x44
|
|
141
|
+
LengthInBytes = 16
|
|
142
|
+
|
|
143
|
+
def self.read_from_stream(tdms_file)
|
|
144
|
+
new(tdms_file.read_timestamp)
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
DataTypesById = {
|
|
149
|
+
Int8::Id => Int8,
|
|
150
|
+
Int16::Id => Int16,
|
|
151
|
+
Int32::Id => Int32,
|
|
152
|
+
Int64::Id => Int64,
|
|
153
|
+
Uint8::Id => Uint8,
|
|
154
|
+
Uint16::Id => Uint16,
|
|
155
|
+
Uint32::Id => Uint32,
|
|
156
|
+
Uint64::Id => Uint64,
|
|
157
|
+
Single::Id => Single,
|
|
158
|
+
SingleWithUnit::Id => SingleWithUnit,
|
|
159
|
+
Double::Id => Double,
|
|
160
|
+
DoubleWithUnit::Id => DoubleWithUnit,
|
|
161
|
+
Utf8String::Id => Utf8String,
|
|
162
|
+
Boolean::Id => Boolean,
|
|
163
|
+
Timestamp::Id => Timestamp
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
def find_by_id(id_byte)
|
|
167
|
+
DataTypesById[id_byte] || raise(ArgumentError, "Don't know type %d" % id_byte)
|
|
168
|
+
end
|
|
169
|
+
module_function :find_by_id
|
|
170
|
+
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
end
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
module Tdms
|
|
2
|
+
|
|
3
|
+
class Document
|
|
4
|
+
attr_reader :segments, :channels, :file
|
|
5
|
+
|
|
6
|
+
def initialize(file)
|
|
7
|
+
@file = file
|
|
8
|
+
parse_segments
|
|
9
|
+
build_aggregates
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def parse_segments
|
|
15
|
+
@segments = []
|
|
16
|
+
|
|
17
|
+
until file.eof?
|
|
18
|
+
segment = Tdms::Segment.new
|
|
19
|
+
segment.prev_segment = @segments[-1]
|
|
20
|
+
@segments << segment
|
|
21
|
+
|
|
22
|
+
lead_in = @file.read(0x1C)
|
|
23
|
+
metadata_pos = @file.pos
|
|
24
|
+
|
|
25
|
+
unpacked = lead_in.unpack("a4VVQQ")
|
|
26
|
+
tdms_tag = unpacked[0] # char[4]
|
|
27
|
+
toc_flags = unpacked[1] # u32
|
|
28
|
+
tdms_version = unpacked[2] # u32
|
|
29
|
+
next_seg_pos = unpacked[3] + metadata_pos # u64
|
|
30
|
+
raw_data_pos = unpacked[4] + metadata_pos # u64
|
|
31
|
+
|
|
32
|
+
new_changed_objs = @file.read_u32
|
|
33
|
+
|
|
34
|
+
raw_data_pos_obj = raw_data_pos
|
|
35
|
+
|
|
36
|
+
1.upto(new_changed_objs) do |obj_index|
|
|
37
|
+
path = Tdms::Path.new(:path => @file.read_utf8_string)
|
|
38
|
+
index_block_len = @file.read_u32
|
|
39
|
+
|
|
40
|
+
if index_block_len == 0xFFFFFFFF
|
|
41
|
+
# no index block
|
|
42
|
+
|
|
43
|
+
elsif index_block_len == 0x000000
|
|
44
|
+
# index block is same as this channel in the last segment
|
|
45
|
+
prev_chan = segment.prev_segment.objects.find {|o| o.path == path }
|
|
46
|
+
|
|
47
|
+
chan = Tdms::Channel.new
|
|
48
|
+
chan.file = @file
|
|
49
|
+
chan.raw_data_pos = raw_data_pos_obj
|
|
50
|
+
chan.path = prev_chan.path
|
|
51
|
+
chan.data_type_id = prev_chan.data_type_id
|
|
52
|
+
chan.dimension = prev_chan.dimension
|
|
53
|
+
chan.num_values = prev_chan.num_values
|
|
54
|
+
|
|
55
|
+
segment.objects << chan
|
|
56
|
+
else
|
|
57
|
+
# XXX why does the number of properties seem to be
|
|
58
|
+
# included in the raw data index block size?
|
|
59
|
+
# -4 is a hack
|
|
60
|
+
index_block = @file.read(index_block_len - 4)
|
|
61
|
+
decoded = index_block.unpack("VVQ")
|
|
62
|
+
|
|
63
|
+
chan = Tdms::Channel.new
|
|
64
|
+
chan.file = @file
|
|
65
|
+
chan.raw_data_pos = raw_data_pos_obj
|
|
66
|
+
chan.path = path
|
|
67
|
+
chan.data_type_id = decoded[0] # first 4 bytes u32
|
|
68
|
+
chan.dimension = decoded[1] # next 4 bytes u32
|
|
69
|
+
chan.num_values = decoded[2] # next 8 bytes u64
|
|
70
|
+
|
|
71
|
+
data_type = Tdms::DataType.find_by_id(chan.data_type_id)
|
|
72
|
+
fixed_length = data_type::LengthInBytes
|
|
73
|
+
|
|
74
|
+
raw_data_pos_obj += if fixed_length
|
|
75
|
+
chan.num_values * fixed_length
|
|
76
|
+
else
|
|
77
|
+
# if the values are variable length (strings only) then
|
|
78
|
+
# the index block contains 8 additional bytes at the
|
|
79
|
+
# end with the total length of the raw data in u64
|
|
80
|
+
index_block[-8,8].unpack("Q")[0]
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
segment.objects << chan
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# TODO store properties
|
|
87
|
+
num_props = @file.read_u32
|
|
88
|
+
1.upto(num_props) do |n|
|
|
89
|
+
prop = @file.read_property
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
@file.seek next_seg_pos
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def build_aggregates
|
|
99
|
+
@channels = []
|
|
100
|
+
|
|
101
|
+
channels_by_path = {}
|
|
102
|
+
segments.each do |segment|
|
|
103
|
+
segment.objects.select { |o| o.path.channel? }.each do |ch|
|
|
104
|
+
(channels_by_path[ch.path.to_s] ||= []) << ch
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
channels_by_path.each do |path, channels|
|
|
109
|
+
@channels << AggregateChannel.new(channels)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
end
|
data/lib/tdms/path.rb
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
module Tdms
|
|
2
|
+
|
|
3
|
+
class Path
|
|
4
|
+
attr_reader :group, :channel
|
|
5
|
+
|
|
6
|
+
def initialize(options={})
|
|
7
|
+
load(options[:path]) if options[:path]
|
|
8
|
+
@group = options[:group] if options[:group]
|
|
9
|
+
@channel = options[:channel] if options[:channel]
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def load(path)
|
|
13
|
+
segments = path.split("/").map do |seg|
|
|
14
|
+
seg.sub(/^'/,'').sub(/'$/,'').sub("''", "'")
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
_, @group, @channel = *segments
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def ==(other)
|
|
21
|
+
if other.is_a?(String)
|
|
22
|
+
self.to_s == other
|
|
23
|
+
elsif other.is_a?(Path)
|
|
24
|
+
self.dump == other.dump
|
|
25
|
+
else
|
|
26
|
+
super
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def dump
|
|
31
|
+
raise ArgumentError if channel && group.nil?
|
|
32
|
+
|
|
33
|
+
parts = [""]
|
|
34
|
+
parts << ("'" + group.sub("'","''") + "'") if group
|
|
35
|
+
parts << ("'" + channel.sub("'","''") + "'") if channel
|
|
36
|
+
|
|
37
|
+
dumped = parts.join("/")
|
|
38
|
+
dumped.empty? ? "/" : dumped
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def to_s
|
|
42
|
+
dump
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def root?
|
|
46
|
+
(! channel?) && (! group?)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def group?
|
|
50
|
+
(! @group.nil?) && (@channel.nil?)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def channel?
|
|
54
|
+
(! @channel.nil?)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
end
|
data/lib/tdms/segment.rb
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
require 'date'
|
|
2
|
+
|
|
3
|
+
module Tdms
|
|
4
|
+
|
|
5
|
+
module Streaming
|
|
6
|
+
def read_property
|
|
7
|
+
name = read_utf8_string
|
|
8
|
+
type_id = read_u32
|
|
9
|
+
|
|
10
|
+
data = Tdms::DataType.find_by_id(type_id).read_from_stream(self)
|
|
11
|
+
Tdms::Property.new(name, data)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def read_bool
|
|
15
|
+
read(1) == "\001"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def read_u8
|
|
19
|
+
read(1).unpack("C")[0]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def read_u16
|
|
23
|
+
read(2).unpack("v")[0]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def read_u32
|
|
27
|
+
read(4).unpack("V")[0]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def read_u64
|
|
31
|
+
lo_hi = read(8).unpack("VV")
|
|
32
|
+
lo_hi[0] + (lo_hi[1] << 32)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def read_i8
|
|
36
|
+
read(2).unpack("c")[0]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def read_i16
|
|
40
|
+
read(2).unpack("s")[0] # TODO little endian not native
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def read_i32
|
|
44
|
+
read(4).unpack("l")[0] # TODO little endian not native
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def read_i64
|
|
48
|
+
read(8).unpack("q")[0] # TODO little endian not native
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def read_single
|
|
52
|
+
read(4).unpack("e")[0]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def read_double
|
|
56
|
+
read(8).unpack("E")[0]
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def read_utf8_string
|
|
60
|
+
length = read_u32
|
|
61
|
+
read length
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def read_timestamp
|
|
65
|
+
positive_fractions_of_second = read_u64 # ignored
|
|
66
|
+
seconds_since_labview_epoch = read(8).unpack("q")[0] # TODO little endian not native
|
|
67
|
+
|
|
68
|
+
labview_epoch = ::DateTime.new(1904, 1, 1)
|
|
69
|
+
labview_epoch + Rational(seconds_since_labview_epoch, 86400)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
class File < ::File
|
|
74
|
+
include Streaming
|
|
75
|
+
|
|
76
|
+
def self.parse(filename)
|
|
77
|
+
f = self.open(filename, "rb")
|
|
78
|
+
Document.new(f)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
end
|