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   
 28  ''' 
 29   
 30   
 31  import sys, os 
 32  from lxml import etree  
 33   
34 -def hasSubTag(et,subtag):
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
41 -def hasBoolField(et):
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
50 -def useChoice(field):
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 # FIX: check for arrays... what to do? 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
64 -def createChoiceList(o,fieldET):
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 # FIX: write me! 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 #print lastVal, entryKey, range(lastVal,entryKey) 77 for i in range(lastVal,entryKey): 78 o.write('\t\t\''+str(i)+'\',\n') 79 lastVal = entryKey + 1 # Ready for the next key 80 o.write('\t\t\''+str(entryKey)+' - '+entry.text+'\',\n') 81 o.write('\t\t]\n') 82 elif fieldType == 'bool': 83 pass # Just one bool list 84 else: 85 print 'ERROR: not handling the choice for ',name,fieldType
86 #assert False 87 88
89 -def generateWxPython(infile,outfile, prefixName=False,verbose=False):
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 #for msgET in aisMsgsET.xpath('message'): 108 #o.write('#import '+msgET.attrib['name']+' \t# FIX: turn this back on\n') 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 # FIX: NUKE THIS HACK... 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 #if msgET.tag != 'message': continue 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
144 -def buildWxPythonMsg(o,msgET, verbose=False, prefixName=False):
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 # All bools use the same lookup list 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 # FIX: does not cope with arrays of anything other than strings 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=') # Need to lookup what to do... 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 # FIX: need to figure out how to select choices when they could be negative 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 #print name, numBits 233 #print 2**(numBits-1) 234 start = '-'+str((2**(numBits-1)) / scale) 235 end = str((2**(numBits-1) - 1) / scale) 236 if hasSubTag(field,'range'): 237 # FIX: need to also create a validator that allow min .. max plus the unavailable 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 #print 'decimal',start,end 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 # FIX: need to set the type right on the values 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 # FIX: put in command line interface here... 307 # FIX: what if there is more than one message?? 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 # help='Name of the python file to write [default: stdout]') 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 # Restore the original args 358 del argvOrig # hide from epydoc 359 sys.exit() # FIX: Will this exit success? 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