Package ais :: Module aisxml2wxpy
[hide private]
[frames] | no frames]

Source Code for Module ais.aisxml2wxpy

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