1
2
3 __version__ = '$Revision: 4791 $'.split()[1]
4 __date__ = '$Date: 2006-09-24 14:01:41 -0400 (Sun, 24 Sep 2006) $'.split()[1]
5 __author__ = 'Kurt Schwehr'
6
7 __doc__='''
8
9 Build a wxpython interface that uses the aisxmlbinmsg2py generated file to create a message string.
10
11 aisxmlbinmsg2py was getting too long, so this functionality is completely broken out.
12
13 @requires: U{lxml<http://codespeak.net/lxml/>}
14 @requires: U{epydoc<http://epydoc.sourceforge.net/>} >= 3.0alpha3
15 @requires: U{BitVector<http://cheeseshop.python.org/pypi/BitVector>} >= 1.2
16
17 @author: U{'''+__author__+'''<http://schwehr.org/>}
18 @version: ''' + __version__ +'''
19 @copyright: 2006
20 @var __date__: Date of last svn commit
21 @undocumented: __version__ __author__ __doc__ parser
22 @since: 2006-Sep-24
23 @status: under development
24 @organization: U{CCOM<http://ccom.unh.edu/>}
25 @license: GPL v2
26
27
28 '''
29
30
31 import sys, os
32 from lxml import etree
33
35 '''
36 @return: true if the tag a sub tag with name subtag
37 '''
38 if 0<len(et.xpath(subtag)): return True
39 return False
40
42 '''
43 @return: true there exists a bool type field
44 @param et: message element tree
45 '''
46 if 0<len(et.xpath('field[@type="bool"]')): return True
47 return False
48
49
51 '''
52 @return: true if should use a wxChoice for this field
53 @param field: Field element tree
54 '''
55 fieldType = field.attrib['type']
56 if fieldType not in ['int','uint','bool']:
57 return False
58
59 if fieldType == 'bool': return True
60 if int(field.attrib['numberofbits'])>4: return False
61 if hasSubTag(field,'lookuptable'): return True
62 return False
63
65 '''Create the wx.Choice list of entries'''
66 lookup = fieldET.xpath('lookuptable')[0]
67 name = fieldET.attrib['name']
68 fieldType = fieldET.attrib['type']
69 if fieldType == 'int':
70 assert False
71 elif fieldType == 'uint':
72 o.write('\t'+name+'List = [\n')
73 lastVal=0
74 for entry in lookup.xpath('entry'):
75 entryKey = int(entry.attrib['key'])
76
77 for i in range(lastVal,entryKey):
78 o.write('\t\t\''+str(i)+'\',\n')
79 lastVal = entryKey + 1
80 o.write('\t\t\''+str(entryKey)+' - '+entry.text+'\',\n')
81 o.write('\t\t]\n')
82 elif fieldType == 'bool':
83 pass
84 else:
85 print 'ERROR: not handling the choice for ',name,fieldType
86
87
88
90 '''
91 @param infile: xml ais binary message definition file
92 @param outfile: where to dump the python code
93 '''
94
95 aisMsgsET = etree.parse(infile).getroot()
96
97 o = file(outfile,'w')
98 os.chmod(outfile,0755)
99
100 print 'FIX: make the python command user selectable'
101 o.write('''#!/usr/bin/env pythonw # mac specific
102 # FIX: put some documentation here!
103
104 import wx
105 ''')
106
107
108
109 print 'FIX: make a more rebust determination of the module name'
110 o.write('#import '+outfile.split('.')[0]+'\t# FIX: turn this back on\n')
111
112
113 o.write('''
114
115 testParams = {'COG': 34.5,
116 'MessageID': 1,
117 'NavigationStatus': 3,
118 'PositionAccuracy': 1,
119 'Position_latitude': 37.424458333333334,
120 'Position_longitude': -122.16328055555556,
121 'RAIM': False,
122 'ROT': -2,
123 'RegionalReserved': 0,
124 'RepeatIndicator': 1,
125 'SOG': 101.9,
126 'Spare': 0,
127 'TimeStamp': 35,
128 'TrueHeading': 41,
129 'UserID': 1193046,
130 'slotoffset': 1221,
131 'syncstate': 2}
132
133 ''')
134
135 for msgET in aisMsgsET.xpath('message'):
136
137 print msgET.tag, msgET.attrib['name']
138
139 if len(msgET.xpath('include-struct')) > 0:
140 sys.exit("ERROR: cannot handle xml that still has include-struct tags.\n Please use expandais.py.")
141 buildWxPythonMsg(o,msgET,prefixName=prefixName,verbose=verbose)
142
143
145 '''
146 Write a class for the wx python.
147
148 @param o: open file where resulting code will be written
149 @param msgET: Element Tree starting at a message node
150
151 @todo: for lookuptable/entry values, make it also print the decoded value.
152 @todo: use a different name for message and field
153 '''
154 assert(msgET.tag=='message')
155 msgName = msgET.attrib['name']
156
157
158 className = 'MsgFrame'
159 if prefixName: className = msgName+'MsgFrame'
160
161
162 o.write('class '+className+'(wx.Frame):\n')
163 o.write('''\t\'\'\'
164 # FIX: write doc string here for the frame
165 \'\'\'
166
167 ''')
168 for field in msgET.xpath('field'):
169 if verbose: print 'Does field',field.attrib['name'],'use choice ...',
170 if useChoice(field):
171 createChoiceList(o,field)
172 if verbose: print 'yes'
173 elif verbose: print 'no'
174
175
176 if hasBoolField(msgET): o.write('\tBoolList=[\'False\',\'True\']\n')
177
178
179 o.write('''
180 def __init__(self,parent,title,msgDict):
181 \'\'\'
182 @param msgDict: Default values to use.
183 Overwritten with the return values.
184 Values that are required will be ignored.
185 @type msgDict: dict
186 \'\'\'
187 wx.Frame.__init__(self,parent,-1,title,size=(640,480)) # FIX: what size?
188 self.msgDict = msgDict # Save this so we can edit and return valies in the incoming dict
189
190 defaults = testParams # FIX: turn off this hack and use the unavailable values
191
192 sizer=wx.FlexGridSizer(rows=1,cols=2, vgap=13, hgap=13)
193 sizer.AddGrowableCol(1)
194 self.SetSizer(sizer)
195
196 ''')
197
198 for field in msgET.xpath('field'):
199
200 name = field.attrib['name']
201 fieldType = field.attrib['type']
202 numBits = int(field.attrib['numberofbits'])
203
204 o.write('\n\t\t############################ Field '+name+' - '+fieldType+'\n')
205 o.write('\t\ttxt = wx.StaticText(self,label=\''+name+'\')\n')
206 if hasSubTag(field,'required'):
207 o.write('\t\tself.'+name+'Widget=wx.StaticText(self,label=\''+field.xpath('required')[0].text+'\')\n')
208 else:
209 o.write('\t\tvalue=str(defaults[\''+name+'\'])\n')
210 o.write('\t\tif \''+name+'\' in msgDict: value = str(msgDict[\''+name+'\'])\n')
211 o.write('\t\tself.'+name+'Widget=')
212 if 'uint'==fieldType:
213 if useChoice(field):
214 o.write('wx.Choice(self,-1, choices = self.'+name+'List)\n')
215 o.write('\t\tself.'+name+'Widget.SetSelection(int(value))\n')
216 else: o.write('wx.SpinCtrl(self,value=value,min=0,max='+str(2**numBits - 1)+')\n')
217 elif 'int'==fieldType:
218 if useChoice(field):
219 o.write('wx.Choice(self,-1, choices = self.'+name+'List)\n')
220
221 assert False
222 else: o.write('wx.SpinCtrl(self,value=value,min='+str(2**(numBits-1))+',max='+str(2**(numBits-1) - 1)+')\n')
223 elif 'bool'==fieldType:
224 o.write('wx.Choice(self,-1, choices = self.BoolList)\n')
225 o.write('\t\tif defaults[\''+name+'\']: self.'+name+'Widget.SetSelection(1)\n')
226 o.write('\t\telse: self.'+name+'Widget.SetSelection(0)\n')
227 elif fieldType in ['udecimal','decimal']:
228 scale = float(field.xpath('scale')[0].text)
229 if fieldType=='udecimal':
230 o.write('wx.Slider(self,-1,float(value),0,'+str((2**numBits -1) / scale))
231 else:
232
233
234 start = '-'+str((2**(numBits-1)) / scale)
235 end = str((2**(numBits-1) - 1) / scale)
236 if hasSubTag(field,'range'):
237
238 range = field.xpath('range')[0]
239 start = float(range.attrib['min'])
240 end = float(range.attrib['max'])
241 if hasSubTag(field,'unavailable'):
242 unavailable = float(field.xpath('unavailable')[0].text)
243 if unavailable < start: start = unavailable
244 if end < unavailable: end = unavailable
245
246
247 o.write('wx.Slider(self,-1,float(value),'+str(start)+','+str(end))
248 o.write(',style=wx.SL_HORIZONTAL|wx.SL_AUTOTICKS | wx.SL_LABELS)\n')
249
250
251 else:
252 print 'Please follow the GPS navigation system over the cliff',name,fieldType
253 assert(False)
254
255 o.write('\t\tdel value\n')
256
257 o.write('\n')
258 o.write('\t\tsizer.Add(item=txt); del txt\n')
259 o.write('\t\tsizer.Add(self.'+name+'Widget,flag=wx.EXPAND)\n')
260
261 o.write('''
262 #######
263 ####### FINISH UP __init__
264 #######
265 btnDone = wx.Button(self,label='Done')
266
267 sizer.Add((1,35),0,wx.ADJUST_MINSIZE,0)
268 sizer.Add(item=btnDone)
269 btnDone.Bind(wx.EVT_BUTTON,self.OnDone)
270
271 self.Layout() # Get yourself organized
272 self.Show()
273
274 def OnDone(self,evt):
275 \'\'\'
276 Put all values into msgDict so that they get returned to the caller
277 \'\'\'
278 ''')
279 for field in msgET.xpath('field'):
280 name = field.attrib['name']
281 fieldType = field.attrib['type']
282
283 if hasSubTag(field,'required'):
284
285 if fieldType in ['int','uint']:
286 o.write('\t\tself.msgDict[\''+name+'\']='+field.xpath('required')[0].text+'\n')
287 else:
288 print 'FIX: need to write this case!'
289 assert False
290 else:
291 if fieldType in ['int','uint','bool','udecimal','decimal']:
292 if useChoice(field): o.write('\t\tself.msgDict[\''+name+'\']=self.'+name+'Widget.GetSelection()\n')
293 else: o.write('\t\tself.msgDict[\''+name+'\']=self.'+name+'Widget.GetValue()\n')
294 elif fieldType in ['decimal','udecimal']:
295 print 'FIX: what do I do about decimals?'
296 o.write('\t\t#FIX: handle '+name+' '+fieldType+'\n')
297 else:
298 print 'FIX: need to write the other cases here', name, fieldType
299 o.write('\t\t#FIX: handle '+name+' '+fieldType+'\n')
300 assert False
301
302 o.write('\t\tself.Close(True)\n')
303
304
305
306
307
308 o.write('''
309 if __name__=='__main__':
310 app = wx.PySimpleApp()
311 theData={}
312 frame = MsgFrame(None,\''''+msgName.capitalize()+''' Message Editor',theData)
313 app.MainLoop()
314
315 print 'finishing up', theData
316 ''')
317
318
319
320 if __name__=='__main__':
321 from optparse import OptionParser
322 parser = OptionParser(usage="%prog [options]",
323 version="%prog "+__version__)
324
325 parser.add_option('-o','--output',dest='outputFileName',default=None,
326 help='Name of the python file to write')
327
328
329 parser.add_option('-i','-x','--xml-definition',dest='xmlFileName',default=None,
330 help='XML definition file for the msg to use')
331
332 parser.add_option('--doc-test',dest='doctest',default=False,action='store_true',
333 help='run the documentation tests')
334
335 parser.add_option('-p','--prefix',dest='prefix',default=False,action='store_true',
336 help='put the field name in front of all function names.'
337 +' Allows multiple messages in one file')
338
339 parser.add_option('-v','--verbose',dest='verbose',default=False,action='store_true',
340 help='run the tests run in verbose mode')
341
342 (options,args) = parser.parse_args()
343
344 success=True
345
346 if options.doctest:
347 import os; print os.path.basename(sys.argv[0]), 'doctests ...',
348 argvOrig = sys.argv
349 sys.argv= [sys.argv[0]]
350 if options.verbose: sys.argv.append('-v')
351 import doctest
352 numfail,numtests=doctest.testmod()
353 if numfail==0: print 'ok'
354 else:
355 print 'FAILED'
356 success=False
357 sys.argv = argvOrig
358 del argvOrig
359 sys.exit()
360
361 if None==options.xmlFileName:
362 sys.exit('ERROR: must specify an xml definition file.')
363 if None==options.outputFileName:
364 sys.exit('ERROR: must specify an python file to write to.')
365 generateWxPython(options.xmlFileName,options.outputFileName,prefixName=options.prefix
366 ,verbose=options.verbose)
367
368 print '\nrecommend running pychecker like this:'
369 print ' pychecker -q',options.outputFileName
370