1 # Copyright (C) 2012 W. Trevor King <wking@tremily.us>
3 # This file is part of igor.
5 # igor is free software: you can redistribute it and/or modify it under the
6 # terms of the GNU Lesser General Public License as published by the Free
7 # Software Foundation, either version 3 of the License, or (at your option) any
10 # igor is distributed in the hope that it will be useful, but WITHOUT ANY
11 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12 # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
15 # You should have received a copy of the GNU Lesser General Public License
16 # along with igor. If not, see <http://www.gnu.org/licenses/>.
20 from .. import LOG as _LOG
21 from ..binarywave import TYPE_TABLE as _TYPE_TABLE
22 from ..binarywave import NullStaticStringField as _NullStaticStringField
23 from ..binarywave import DynamicStringField as _DynamicStringField
24 from ..struct import Structure as _Structure
25 from ..struct import DynamicStructure as _DynamicStructure
26 from ..struct import Field as _Field
27 from ..struct import DynamicField as _DynamicField
28 from ..util import byte_order as _byte_order
29 from ..util import need_to_reorder_bytes as _need_to_reorder_bytes
30 from .base import Record
33 class ListedStaticStringField (_NullStaticStringField):
34 """Handle string conversions for multi-count dynamic parents.
36 If a field belongs to a multi-count dynamic parent, the parent is
37 called multiple times to parse each count, and the field's
38 post-unpack hook gets called after the field is unpacked during
39 each iteration. This requires alternative logic for getting and
40 setting the string data. The actual string formatting code is not
43 def post_unpack(self, parents, data):
44 parent_structure = parents[-1]
45 parent_data = self._get_structure_data(parents, data, parent_structure)
46 d = self._normalize_string(parent_data[-1][self.name])
47 parent_data[-1][self.name] = d
50 class ListedStaticStringField (_NullStaticStringField):
51 """Handle string conversions for multi-count dynamic parents.
53 If a field belongs to a multi-count dynamic parent, the parent is
54 called multiple times to parse each count, and the field's
55 post-unpack hook gets called after the field is unpacked during
56 each iteration. This requires alternative logic for getting and
57 setting the string data. The actual string formatting code is not
60 def post_unpack(self, parents, data):
61 parent_structure = parents[-1]
62 parent_data = self._get_structure_data(parents, data, parent_structure)
63 d = self._normalize_string(parent_data[-1][self.name])
64 parent_data[-1][self.name] = d
67 class ListedDynamicStrDataField (_DynamicStringField, ListedStaticStringField):
68 _size_field = 'strLen'
69 _null_terminated = False
71 def _get_size_data(self, parents, data):
72 parent_structure = parents[-1]
73 parent_data = self._get_structure_data(parents, data, parent_structure)
74 return parent_data[-1][self._size_field]
77 class DynamicVarDataField (_DynamicField):
78 def __init__(self, *args, **kwargs):
79 if 'array' not in kwargs:
80 kwargs['array'] = True
81 super(DynamicVarDataField, self).__init__(*args, **kwargs)
83 def pre_pack(self, parents, data):
84 raise NotImplementedError()
86 def post_unpack(self, parents, data):
87 var_structure = parents[-1]
88 var_data = self._get_structure_data(parents, data, var_structure)
89 data = var_data[self.name]
91 for i,value in enumerate(data):
92 key,value = self._normalize_item(i, value)
94 var_data[self.name] = d
96 def _normalize_item(self, index, value):
97 raise NotImplementedError()
100 class DynamicSysVarField (DynamicVarDataField):
101 def _normalize_item(self, index, value):
102 name = 'K{}'.format(index)
106 class DynamicUserVarField (DynamicVarDataField):
107 def _normalize_item(self, index, value):
113 class DynamicUserStrField (DynamicVarDataField):
114 def _normalize_item(self, index, value):
116 value = value['data']
120 class DynamicVarNumField (_DynamicField):
121 def post_unpack(self, parents, data):
122 parent_structure = parents[-1]
123 parent_data = self._get_structure_data(parents, data, parent_structure)
124 d = self._normalize_numeric_variable(parent_data[-1][self.name])
125 parent_data[-1][self.name] = d
127 def _normalize_numeric_variable(self, num_var):
128 t = _TYPE_TABLE[num_var['numType']]
129 if num_var['numType'] % 2: # complex number
130 return t(complex(num_var['realPart'], num_var['imagPart']))
132 return t(num_var['realPart'])
135 class DynamicFormulaField (_DynamicStringField):
136 _size_field = 'formulaLen'
137 _null_terminated = True
141 VarHeader1 = _Structure( # `version` field pulled out into VariablesRecord
144 _Field('h', 'numSysVars', help='Number of system variables (K0, K1, ...).'),
145 _Field('h', 'numUserVars', help='Number of user numeric variables -- may be zero.'),
146 _Field('h', 'numUserStrs', help='Number of user string variables -- may be zero.'),
150 VarHeader2 = _Structure( # `version` field pulled out into VariablesRecord
153 _Field('h', 'numSysVars', help='Number of system variables (K0, K1, ...).'),
154 _Field('h', 'numUserVars', help='Number of user numeric variables -- may be zero.'),
155 _Field('h', 'numUserStrs', help='Number of user string variables -- may be zero.'),
156 _Field('h', 'numDependentVars', help='Number of dependent numeric variables -- may be zero.'),
157 _Field('h', 'numDependentStrs', help='Number of dependent string variables -- may be zero.'),
161 UserStrVarRec1 = _DynamicStructure(
162 name='UserStrVarRec1',
164 ListedStaticStringField('c', 'name', help='Name of the string variable.', count=32),
165 _Field('h', 'strLen', help='The real size of the following array.'),
166 ListedDynamicStrDataField('c', 'data'),
170 UserStrVarRec2 = _DynamicStructure(
171 name='UserStrVarRec2',
173 ListedStaticStringField('c', 'name', help='Name of the string variable.', count=32),
174 _Field('l', 'strLen', help='The real size of the following array.'),
179 VarNumRec = _Structure(
182 _Field('h', 'numType', help='Type from binarywave.TYPE_TABLE'),
183 _Field('d', 'realPart', help='The real part of the number.'),
184 _Field('d', 'imagPart', help='The imag part if the number is complex.'),
185 _Field('l', 'reserved', help='Reserved - set to zero.'),
189 UserNumVarRec = _DynamicStructure(
190 name='UserNumVarRec',
192 ListedStaticStringField('c', 'name', help='Name of the string variable.', count=32),
193 _Field('h', 'type', help='0 = string, 1 = numeric.'),
194 DynamicVarNumField(VarNumRec, 'num', help='Type and value of the variable if it is numeric. Not used for string.'),
198 UserDependentVarRec = _DynamicStructure(
199 name='UserDependentVarRec',
201 ListedStaticStringField('c', 'name', help='Name of the string variable.', count=32),
202 _Field('h', 'type', help='0 = string, 1 = numeric.'),
203 _Field(VarNumRec, 'num', help='Type and value of the variable if it is numeric. Not used for string.'),
204 _Field('h', 'formulaLen', help='The length of the dependency formula.'),
205 DynamicFormulaField('c', 'formula', help='Start of the dependency formula. A C string including null terminator.'),
209 class DynamicVarHeaderField (_DynamicField):
210 def pre_pack(self, parents, data):
211 raise NotImplementedError()
213 def post_unpack(self, parents, data):
214 var_structure = parents[-1]
215 var_data = self._get_structure_data(
216 parents, data, var_structure)
217 var_header_structure = self.format
218 data = var_data['var_header']
219 sys_vars_field = var_structure.get_field('sysVars')
220 sys_vars_field.count = data['numSysVars']
221 sys_vars_field.setup()
222 user_vars_field = var_structure.get_field('userVars')
223 user_vars_field.count = data['numUserVars']
224 user_vars_field.setup()
225 user_strs_field = var_structure.get_field('userStrs')
226 user_strs_field.count = data['numUserStrs']
227 user_strs_field.setup()
228 if 'numDependentVars' in data:
229 dependent_vars_field = var_structure.get_field('dependentVars')
230 dependent_vars_field.count = data['numDependentVars']
231 dependent_vars_field.setup()
232 dependent_strs_field = var_structure.get_field('dependentStrs')
233 dependent_strs_field.count = data['numDependentStrs']
234 dependent_strs_field.setup()
235 var_structure.setup()
238 Variables1 = _DynamicStructure(
241 DynamicVarHeaderField(VarHeader1, 'var_header', help='Variables header'),
242 DynamicSysVarField('f', 'sysVars', help='System variables', count=0),
243 DynamicUserVarField(UserNumVarRec, 'userVars', help='User numeric variables', count=0),
244 DynamicUserStrField(UserStrVarRec1, 'userStrs', help='User string variables', count=0),
248 Variables2 = _DynamicStructure(
251 DynamicVarHeaderField(VarHeader2, 'var_header', help='Variables header'),
252 DynamicSysVarField('f', 'sysVars', help='System variables', count=0),
253 DynamicUserVarField(UserNumVarRec, 'userVars', help='User numeric variables', count=0),
254 DynamicUserStrField(UserStrVarRec2, 'userStrs', help='User string variables', count=0),
255 _Field(UserDependentVarRec, 'dependentVars', help='Dependent numeric variables.', count=0, array=True),
256 _Field(UserDependentVarRec, 'dependentStrs', help='Dependent string variables.', count=0, array=True),
260 class DynamicVersionField (_DynamicField):
261 def pre_pack(self, parents, byte_order):
262 raise NotImplementedError()
264 def post_unpack(self, parents, data):
265 variables_structure = parents[-1]
266 variables_data = self._get_structure_data(
267 parents, data, variables_structure)
268 version = variables_data['version']
269 if variables_structure.byte_order in '@=':
270 need_to_reorder_bytes = _need_to_reorder_bytes(version)
271 variables_structure.byte_order = _byte_order(need_to_reorder_bytes)
273 'get byte order from version: {} (reorder? {})'.format(
274 variables_structure.byte_order, need_to_reorder_bytes))
276 need_to_reorder_bytes = False
278 old_format = variables_structure.fields[-1].format
280 variables_structure.fields[-1].format = Variables1
282 variables_structure.fields[-1].format = Variables2
283 elif not need_to_reorder_bytes:
285 'invalid variables record version: {}'.format(version))
287 if variables_structure.fields[-1].format != old_format:
288 _LOG.debug('change variables record from {} to {}'.format(
289 old_format, variables_structure.fields[-1].format))
290 variables_structure.setup()
291 elif need_to_reorder_bytes:
292 variables_structure.setup()
294 # we might need to unpack again with the new byte order
295 return need_to_reorder_bytes
298 VariablesRecordStructure = _DynamicStructure(
299 name='VariablesRecord',
301 DynamicVersionField('h', 'version', help='Version number for this header.'),
302 _Field(Variables1, 'variables', help='The rest of the variables data.'),
306 class VariablesRecord (Record):
307 def __init__(self, *args, **kwargs):
308 super(VariablesRecord, self).__init__(*args, **kwargs)
309 # self.header['version'] # record version always 0?
310 VariablesRecordStructure.byte_order = '='
311 VariablesRecordStructure.setup()
312 stream = _io.BytesIO(bytes(self.data))
313 self.variables = VariablesRecordStructure.unpack_stream(stream)
315 for key,value in self.variables['variables'].items():
316 if key not in ['var_header']:
317 _LOG.debug('update namespace {} with {} for {}'.format(
318 self.namespace, value, key))
319 self.namespace.update(value)