d604bfd77efbbfe3d4236ced93bb2cded9090fcd
[igor.git] / igor / record / variables.py
1 # Copyright
2
3 import io as _io
4
5 from .. import LOG as _LOG
6 from ..binarywave import TYPE_TABLE as _TYPE_TABLE
7 from ..binarywave import NullStaticStringField as _NullStaticStringField
8 from ..binarywave import DynamicStringField as _DynamicStringField
9 from ..struct import Structure as _Structure
10 from ..struct import DynamicStructure as _DynamicStructure
11 from ..struct import Field as _Field
12 from ..struct import DynamicField as _DynamicField
13 from ..util import byte_order as _byte_order
14 from ..util import need_to_reorder_bytes as _need_to_reorder_bytes
15 from .base import Record
16
17
18 class ListedStaticStringField (_NullStaticStringField):
19     """Handle string conversions for multi-count dynamic parents.
20
21     If a field belongs to a multi-count dynamic parent, the parent is
22     called multiple times to parse each count, and the field's
23     post-unpack hook gets called after the field is unpacked during
24     each iteration.  This requires alternative logic for getting and
25     setting the string data.  The actual string formatting code is not
26     affected.
27     """
28     def post_unpack(self, parents, data):
29         parent_structure = parents[-1]
30         parent_data = self._get_structure_data(parents, data, parent_structure)
31         d = self._normalize_string(parent_data[-1][self.name])
32         parent_data[-1][self.name] = d
33
34
35 class ListedStaticStringField (_NullStaticStringField):
36     """Handle string conversions for multi-count dynamic parents.
37
38     If a field belongs to a multi-count dynamic parent, the parent is
39     called multiple times to parse each count, and the field's
40     post-unpack hook gets called after the field is unpacked during
41     each iteration.  This requires alternative logic for getting and
42     setting the string data.  The actual string formatting code is not
43     affected.
44     """
45     def post_unpack(self, parents, data):
46         parent_structure = parents[-1]
47         parent_data = self._get_structure_data(parents, data, parent_structure)
48         d = self._normalize_string(parent_data[-1][self.name])
49         parent_data[-1][self.name] = d
50
51
52 class ListedDynamicStrDataField (_DynamicStringField, ListedStaticStringField):
53     _size_field = 'strLen'
54     _null_terminated = False
55
56     def _get_size_data(self, parents, data):
57         parent_structure = parents[-1]
58         parent_data = self._get_structure_data(parents, data, parent_structure)
59         return parent_data[-1][self._size_field]
60
61
62 class DynamicVarDataField (_DynamicField):
63     def pre_pack(self, parents, data):
64         raise NotImplementedError()
65
66     def post_unpack(self, parents, data):
67         var_structure = parents[-1]
68         var_data = self._get_structure_data(parents, data, var_structure)
69         data = var_data[self.name]
70         d = {}
71         for i,value in enumerate(data):
72             key,value = self._normalize_item(i, value)
73             d[key] = value
74         var_data[self.name] = d
75
76     def _normalize_item(self, index, value):
77         raise NotImplementedError()
78
79
80 class DynamicSysVarField (DynamicVarDataField):
81     def _normalize_item(self, index, value):
82         name = 'K{}'.format(index)
83         return (name, value)
84
85
86 class DynamicUserVarField (DynamicVarDataField):
87     def _normalize_item(self, index, value):
88         name = value['name']
89         value = value['num']
90         return (name, value)
91
92
93 class DynamicUserStrField (DynamicVarDataField):
94     def _normalize_item(self, index, value):
95         name = value['name']
96         value = value['data']
97         return (name, value)
98
99
100 class DynamicVarNumField (_DynamicField):
101     def post_unpack(self, parents, data):
102         parent_structure = parents[-1]
103         parent_data = self._get_structure_data(parents, data, parent_structure)
104         d = self._normalize_numeric_variable(parent_data[-1][self.name])
105         parent_data[-1][self.name] = d
106
107     def _normalize_numeric_variable(self, num_var):
108         t = _TYPE_TABLE[num_var['numType']]
109         if num_var['numType'] % 2:  # complex number
110             return t(complex(num_var['realPart'], num_var['imagPart']))
111         else:
112             return t(num_var['realPart'])
113
114
115 class DynamicFormulaField (_DynamicStringField):
116     _size_field = 'formulaLen'
117     _null_terminated = True
118
119
120 # From Variables.h
121 VarHeader1 = _Structure(  # `version` field pulled out into VariablesRecord
122     name='VarHeader1',
123     fields=[
124         _Field('h', 'numSysVars', help='Number of system variables (K0, K1, ...).'),
125         _Field('h', 'numUserVars', help='Number of user numeric variables -- may be zero.'),
126         _Field('h', 'numUserStrs', help='Number of user string variables -- may be zero.'),
127         ])
128
129 # From Variables.h
130 VarHeader2 = _Structure(  # `version` field pulled out into VariablesRecord
131     name='VarHeader2',
132     fields=[
133         _Field('h', 'numSysVars', help='Number of system variables (K0, K1, ...).'),
134         _Field('h', 'numUserVars', help='Number of user numeric variables -- may be zero.'),
135         _Field('h', 'numUserStrs', help='Number of user string variables -- may be zero.'),
136         _Field('h', 'numDependentVars', help='Number of dependent numeric variables -- may be zero.'),
137         _Field('h', 'numDependentStrs', help='Number of dependent string variables -- may be zero.'),
138         ])
139
140 # From Variables.h
141 UserStrVarRec1 = _DynamicStructure(
142     name='UserStrVarRec1',
143     fields=[
144         ListedStaticStringField('c', 'name', help='Name of the string variable.', count=32),
145         _Field('h', 'strLen', help='The real size of the following array.'),
146         ListedDynamicStrDataField('c', 'data'),
147         ])
148
149 # From Variables.h
150 UserStrVarRec2 = _DynamicStructure(
151     name='UserStrVarRec2',
152     fields=[
153         ListedStaticStringField('c', 'name', help='Name of the string variable.', count=32),
154         _Field('l', 'strLen', help='The real size of the following array.'),
155         _Field('c', 'data'),
156         ])
157
158 # From Variables.h
159 VarNumRec = _Structure(
160     name='VarNumRec',
161     fields=[
162         _Field('h', 'numType', help='Type from binarywave.TYPE_TABLE'),
163         _Field('d', 'realPart', help='The real part of the number.'),
164         _Field('d', 'imagPart', help='The imag part if the number is complex.'),
165         _Field('l', 'reserved', help='Reserved - set to zero.'),
166         ])
167
168 # From Variables.h
169 UserNumVarRec = _DynamicStructure(
170     name='UserNumVarRec',
171     fields=[
172         ListedStaticStringField('c', 'name', help='Name of the string variable.', count=32),
173         _Field('h', 'type', help='0 = string, 1 = numeric.'),
174         DynamicVarNumField(VarNumRec, 'num', help='Type and value of the variable if it is numeric.  Not used for string.'),
175         ])
176
177 # From Variables.h
178 UserDependentVarRec = _DynamicStructure(
179     name='UserDependentVarRec',
180     fields=[
181         ListedStaticStringField('c', 'name', help='Name of the string variable.', count=32),
182         _Field('h', 'type', help='0 = string, 1 = numeric.'),
183         _Field(VarNumRec, 'num', help='Type and value of the variable if it is numeric.  Not used for string.'),
184         _Field('h', 'formulaLen', help='The length of the dependency formula.'),
185         DynamicFormulaField('c', 'formula', help='Start of the dependency formula. A C string including null terminator.'),
186         ])
187
188
189 class DynamicVarHeaderField (_DynamicField):
190     def pre_pack(self, parents, data):
191         raise NotImplementedError()
192
193     def post_unpack(self, parents, data):
194         var_structure = parents[-1]
195         var_data = self._get_structure_data(
196             parents, data, var_structure)
197         var_header_structure = self.format
198         data = var_data['var_header']
199         sys_vars_field = var_structure.get_field('sysVars')
200         sys_vars_field.count = data['numSysVars']
201         sys_vars_field.setup()
202         user_vars_field = var_structure.get_field('userVars')
203         user_vars_field.count = data['numUserVars']
204         user_vars_field.setup()
205         user_strs_field = var_structure.get_field('userStrs')
206         user_strs_field.count = data['numUserStrs']
207         user_strs_field.setup()
208         if 'numDependentVars' in data:
209             dependent_vars_field = var_structure.get_field('dependentVars')
210             dependent_vars_field.count = data['numDependentVars']
211             dependent_vars_field.setup()
212             dependent_strs_field = var_structure.get_field('dependentStrs')
213             dependent_strs_field.count = data['numDependentStrs']
214             dependent_strs_field.setup()
215         var_structure.setup()
216
217
218 Variables1 = _DynamicStructure(
219     name='Variables1',
220     fields=[
221         DynamicVarHeaderField(VarHeader1, 'var_header', help='Variables header'),
222         DynamicSysVarField('f', 'sysVars', help='System variables', count=0),
223         DynamicUserVarField(UserNumVarRec, 'userVars', help='User numeric variables', count=0),
224         DynamicUserStrField(UserStrVarRec1, 'userStrs', help='User string variables', count=0),
225         ])
226
227
228 Variables2 = _DynamicStructure(
229     name='Variables2',
230     fields=[
231         DynamicVarHeaderField(VarHeader2, 'var_header', help='Variables header'),
232         DynamicSysVarField('f', 'sysVars', help='System variables', count=0),
233         DynamicUserVarField(UserNumVarRec, 'userVars', help='User numeric variables', count=0),
234         DynamicUserStrField(UserStrVarRec2, 'userStrs', help='User string variables', count=0),
235         _Field(UserDependentVarRec, 'dependentVars', help='Dependent numeric variables.', count=0),
236         _Field(UserDependentVarRec, 'dependentStrs', help='Dependent string variables.', count=0),
237         ])
238
239
240 class DynamicVersionField (_DynamicField):
241     def pre_pack(self, parents, byte_order):
242         raise NotImplementedError()
243
244     def post_unpack(self, parents, data):
245         variables_structure = parents[-1]
246         variables_data = self._get_structure_data(
247             parents, data, variables_structure)
248         version = variables_data['version']
249         if variables_structure.byte_order in '@=':
250             need_to_reorder_bytes = _need_to_reorder_bytes(version)
251             variables_structure.byte_order = _byte_order(need_to_reorder_bytes)
252             _LOG.debug(
253                 'get byte order from version: {} (reorder? {})'.format(
254                     variables_structure.byte_order, need_to_reorder_bytes))
255         else:
256             need_to_reorder_bytes = False
257
258         old_format = variables_structure.fields[-1].format
259         if version == 1:
260             variables_structure.fields[-1].format = Variables1
261         elif version == 2:
262             variables_structure.fields[-1].format = Variables2
263         elif not need_to_reorder_bytes:
264             raise ValueError(
265                 'invalid variables record version: {}'.format(version))
266
267         if variables_structure.fields[-1].format != old_format:
268             _LOG.debug('change variables record from {} to {}'.format(
269                     old_format, variables_structure.fields[-1].format))
270             variables_structure.setup()
271         elif need_to_reorder_bytes:
272             variables_structure.setup()
273
274         # we might need to unpack again with the new byte order
275         return need_to_reorder_bytes
276
277
278 VariablesRecordStructure = _DynamicStructure(
279     name='VariablesRecord',
280     fields=[
281         DynamicVersionField('h', 'version', help='Version number for this header.'),
282         _Field(Variables1, 'variables', help='The rest of the variables data.'),
283         ])
284
285
286 class VariablesRecord (Record):
287     def __init__(self, *args, **kwargs):
288         super(VariablesRecord, self).__init__(*args, **kwargs)
289         # self.header['version']  # record version always 0?
290         VariablesRecordStructure.byte_order = '='
291         VariablesRecordStructure.setup()
292         stream = _io.BytesIO(bytes(self.data))
293         self.variables = VariablesRecordStructure.unpack_stream(stream)
294         self.namespace = {}
295         for key,value in self.variables['variables'].items():
296             if key not in ['var_header']:
297                 _LOG.debug('update namespace {} with {} for {}'.format(
298                         self.namespace, value, key))
299                 self.namespace.update(value)