-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathezynq_feature_config.py
executable file
·329 lines (306 loc) · 14.3 KB
/
ezynq_feature_config.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
#!/usr/bin/env python
# Copyright (C) 2013, Elphel.inc.
# process configuration of features
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
__author__ = "Andrey Filippov"
__copyright__ = "Copyright 2013, Elphel, Inc."
__license__ = "GPL"
__version__ = "3.0+"
__maintainer__ = "Andrey Filippov"
__email__ = "[email protected]"
__status__ = "Development"
class EzynqFeatures:
#Modify for this class
ERRORS={
'ERR_NOT_A_VARIANT': 'Specified value is not a valid variant',
'ERR_NOT_AN_INTEGER': 'Value is not an integer',
'ERR_NOT_A_FLOAT': 'Value is not a float',
'ERR_NOT_A_BOOLEAN': 'Value is not a boolean'
}
BOOLEANS=(('0','FALSE','DISABLE','DISABLED','N','OFF'),
('1','TRUE', 'ENABLE','ENABLED','Y','ON'))
# defines - a list, order determines HTML output order
# Each element has fields:
# 'NAME' - unique name to access this parameter
# 'CONF_NAME' - how it appears in configuration file, '@' may be replaced by str(channel)
# 'TYPE' - either "I" for integer, F - float, T - text, B- boolean (may be false/true, 0/1 or enable{d}/disable{d} or tuple with valid options for value
# 'MANDATORY' - boolean: this parameter is mandatory (either directly specified or derived from some others)
# 'DERIVED' - TBD later
# 'DEFAULT' - default value (to be suggested on error? Use if not mandatory parameter?
# 'DESCRIPTION' - Parameter description - use in error message?
def _choice_val(self,t,value):
if isinstance(t,tuple) :
for c in t:
if isinstance(c,int):
try:
if c==int(value,0):
return c
except:
pass
elif isinstance(c,float):
try:
if c==float(value):
return c
except:
pass
elif isinstance(c, bool):
if value in self.BOOLEANS[1]:
return True
elif value in self.BOOLEANS[0]:
return False
elif isinstance(c, str):
try:
if c.upper()==value.upper():
return c
except:
pass
# return value
else:
return None
def __init__(self,defines,channel=0):
self.defs={}
for i,feature in enumerate(defines):
self.defs[feature['NAME']]=feature
self.defs[feature['NAME']]['INDEX']=i
self.channel=channel
self.pars={}
self.target={} # target values (as specified)
self.config_names={}
self.defined=set()
self.calculated=set()
for name in self.defs:
cn=self.defs[name];
self.config_names[cn['CONF_NAME'].replace('@',str(channel))]=name
def parse_features(self,raw_configs):
for line in raw_configs:
conf_name = line['KEY']
value = line['VALUE']
value=str(value).upper()
try:
name=self.config_names[conf_name]
except:
continue
feature= self.defs[name]
if (value=='HELP'):
try:
print conf_name+': '+feature['DESCRIPTION']
except:
print conf_name+': description is not available'
continue
if isinstance(feature['TYPE'], tuple):
val=self._choice_val(feature['TYPE'],value)
if val is None:
raise Exception(self.ERRORS['ERR_NOT_A_VARIANT']+': '+line['VALUE'] +' is not a valid variant for parameter '+
conf_name+'. Valid are:'+str(feature['TYPE']))
else:
value=val
elif (feature['TYPE']=='I') or (feature['TYPE']=='H'):
try:
value= int(value,0)
except:
if value == 'Y':
value=1
else:
raise Exception(self.ERRORS['ERR_NOT_AN_INTEGER']+': '+line['VALUE'] +' is not a valid INTEGER value for parameter '+ conf_name)
elif (feature['TYPE']=='F'):
if value == 'Y':
value=1.0
else:
try:
value= float(value)
except:
raise Exception(self.ERRORS['ERR_NOT_A_FLOAT']+': '+line['VALUE'] +' is not a valid FLOAT value for parameter '+ conf_name)
elif (feature['TYPE']=='B'):
if value in self.BOOLEANS[1]:
value=True
elif value in self.BOOLEANS[0]:
value=False
else:
# print line['VALUE'],type(line['VALUE'])
# print line['VALUE'] in self.BOOLEANS[1]
raise Exception(self.ERRORS['ERR_NOT_A_BOOLEAN']+': '+line['VALUE'] +' is not a valid boolean value for parameter '+ conf_name+
'. Valid for "True" are:'+str(self.BOOLEANS[1])+', for "False" - '+str(self.BOOLEANS[0]))
elif (feature['TYPE']=='T'):
if value == 'Y':
value='1'
pass #keep string value
self.pars[name]=value
self.defined.add(name)
self.target[name]=value
#check after calculating derivative parameters
def check_missing_features(self):
all_set=True
for name in self.defs:
if (not name in self.pars):
if self.defs[name]['MANDATORY']:
all_set=False
print "Configuration file is missing mandatory parameter "+self.defs[name]['CONF_NAME']+': '+self.defs[name]['DESCRIPTION']
else:
if not self.defs[name]['DEFAULT'] is None:
# use default parameter
# print 'Adding default : ',name,'=', self.defs[name]['DEFAULT']
self.pars[name]=self.defs[name]['DEFAULT']
return all_set
def get_par_names(self):
# name_offs=sorted([(name,self.registers[name]['OFFS']) for name in self.registers], key = lambda l: l[1])
# print '---self.registers=',self.registers
# unsorted_name_offs=[(name,self.defs[name]['OFFS']) for name in self.registers]
# print '---unsorted_name_offs=',unsorted_name_offs
# name_offs=sorted(unsorted_name_offs, key = lambda l: l[1])
# print '---name_offs=',name_offs
# return [n for n in name_offs]
# sort register names in the order of addresses
# name_index=sorted([(name,self.defs[name]['INDEX']) for name in self.pars], key = lambda l: l[1])
# return [n for n in sorted([(name,self.defs[name]['OFFS']) for name in self.registers], key = lambda l: l[1])]
return [n[0] for n in sorted([(name,self.defs[name]['INDEX']) for name in self.pars], key = lambda l: l[1])]
#TODO: Use SELECT for options?
def get_par_confname(self,name):
try:
return self.defs[name]['CONF_NAME']
except:
raise Exception (name+' not found in self.defs') # should not happen with wrong data, program bug
def get_par_description(self,name):
try:
return self.defs[name]['DESCRIPTION']
except:
raise Exception (name+' not found in self.defs') # should not happen with wrong data, program bug
def get_par_value_or_none(self,name):
try:
return self.pars[name]
except:
try:
_=self.defs[name]['CONF_NAME']
except:
raise Exception (name+' not found in self.defs') # should not happen with wrong data, program bug
return
def get_par_value(self,name):
try:
return self.pars[name]
except:
# print 'name=',name
# print self.pars
try:
config_name=self.defs[name]['CONF_NAME']
except:
raise Exception (name+' not found in self.defs') # should not happen with wrong data, program bug
raise Exception (config_name+' is not defined, nor calculated')
def get_par_value_or_default(self,name):
try:
return self.pars[name]
except:
# print 'name=',name
# print self.pars
try:
config_name=self.defs[name]['CONF_NAME']
except:
raise Exception (name+' not found in self.defs') # should not happen with wrong data, program bug
try:
return self.defs[name]['DEFAULT']
except:
raise Exception (config_name+' is not defined, nor calculated and no default is provided')
def get_par_target(self,name):
try:
return self.target[name]
except:
try:
config_name=self.defs[name]['CONF_NAME']
except:
raise Exception (name+' not found in self.defs') # should not happen with wrong data, program bug
raise Exception ('Target value for '+config_name+' is not defined')
def is_known(self,name): # specified or calculated
return (name in self.defined) or (name in self.calculated)
def is_mandatory(self,name):
try:
return self.defs[name]['MANDATORY']
except:
raise Exception (name+' not found in self.defs') # should not happen with wrong data, program bug
def is_specified(self,name): # directly specified
return name in self.defined
def undefine_parameter(self,name):
if name in self.pars:
self.pars[name]=None
def set_calculated_value(self,name,value,force=True):
if (not force) and (name in self.defined):
config_name=self.defs[name]['CONF_NAME']
raise Exception (name+' ('+config_name+') is specifically defined in configuration file, value will not change') # should not happen with wrong data, program bug
else:
self.pars[name]=value
self.calculated.add(name)
if name in self.defined:
self.defined.remove(name)
def set_max_value(self,name,value):
# print 'set_max_value (',name,',',value,')'
if (not self.is_known(name)) or (value>self.pars[name]):
self.set_calculated_value(name,value,True)
def set_min_value(self,name,value):
if (not self.is_known(name)) or (value<self.pars[name]):
self.set_calculated_value(name,value,True)
def html_list_features(self,html_file):
if not html_file:
return
html_file.write('<table border="1">\n')
# html_file.write('<tr><th>Configuration name</th><th>Value<br/>(Target)</th><th>Type/<br/>Choices</th><th>Mandatory</th><th>Origin</th><th>Default</th><th>Description</th></tr>\n')
html_file.write('<tr><th>Configuration name</th><th>Value (Target)</th><th>Type/<br/>Choices</th><th>Mandatory</th><th>Origin</th><th>Default</th><th>Description</th></tr>\n')
# print self.get_par_names()
# for name in self.pars:
row_class="even"
for name in self.get_par_names():
# name= self.config_names[conf_name]
feature= self.defs[name]
value= self.get_par_value(name)
if value is None:
value='None'
else:
if isinstance(value,int):
if (feature['TYPE']=='H'):
value=hex(value)
else:
value=str(value)
try:
target_value=self.get_par_target(name)
if isinstance(target_value,int):
if (feature['TYPE']=='H'):
target_value=hex(target_value)
else:
target_value=str(target_value)
if value != target_value: # Do not show target_value if it is the same as actual
# value+='<br/>('+str(target_value)+')'
value='<b>'+str(value)+'</b> ('+str(target_value)+')'
except: # target_value is not set
pass
if name in self.defined:
origin="Defined"
elif name in self.calculated:
origin="Calculated"
else:
origin="Default"
if isinstance (feature['TYPE'],tuple):
par_type='<select>\n'
for t in feature['TYPE']:
par_type+=' <option>'+str(t)+'</option>\n'
# par_type+=('','<br/>')[i>0]+str(t)
par_type+='</select>\n'
else:
par_type={'H':'Integer','I':'Integer','F':'Float','B':'Boolean','T':'Text'}[feature['TYPE']]
# if name=='BAUD_RATE':
# print value
if row_class=="odd": row_class="even"
else: row_class="odd"
if (feature['TYPE']=='H') and isinstance(feature['DEFAULT'],int) and (feature['DEFAULT']>9):
sDefault=hex(feature['DEFAULT'])
else:
sDefault=str(feature['DEFAULT'])
html_file.write('<tr class="'+row_class+'"><td><b>'+feature['CONF_NAME']+'</b></td><td>'+str(value)+'</td><td>'+par_type+
'</td><td>'+('-','Y')[feature['MANDATORY']]+'</td><td>'+origin+'</td><td>'+sDefault+'</td><td>'+feature['DESCRIPTION']+'</td></tr>\n')
html_file.write('</table>\n')