Index: pyglider/trunk/pyglider =================================================================== --- pyglider/trunk/pyglider (revision 468) +++ (revision ) @@ -1,522 +1,0 @@ -"""pyglider.py A module of utilities to run on dockserver for glider operations - - parse_glider_mail - generate_track_kml - -""" - -REAL_RE_STR = '\\s*(-?\\d(\\.\\d+|)[Ee][+\\-]\\d\\d?|-?(\\d+\\.\\d*|\\d*\\.\\d+)|-?\\d+)\\s*' - -import sys -import os -import re - -import time -import datetime - -def load_data(inFile): - lines=None - if os.path.exists(inFile): - f = open(inFile, 'r') - lines = f.readlines() - f.close() - if len(lines)<=0: - print 'Empty file: '+ inFile - else: - print 'File does not exist: '+ inFile - return lines - -def display_time_diff(diff): - """Display time difference in HH:MM and days (D) if necessary""" - days = diff.days - minutes, seconds = divmod(diff.seconds, 60) - hours, minutes = divmod(minutes, 60) - if (days<=1): - if minutes<1: - str = "%02:%02d:%02d" % (hours, minutes,seconds) - else: - str = "%02d:%02d" % (hours, minutes) - elif (days>1): - str = "%d Days %02d:%02d" % (days, hours, minutes) - else: - str = "%02d:%02d" % (hours, minutes) - return str - -# ------------------------------------------------------------------- -# playground -# fn = '/var/spool/mail/localuser' -# lines = load_data(fn) -# glider = 'ramses' -# data = parse_glider_mail(lines, glider) - -# ------------------------------------------------------------------- -def parse_glider_mail(lines, glider): - msg_split_patt = r'From root\@dockserver' - msgs = re.split(msg_split_patt, ''.join(lines)) - - gms = [] - # select messages for specific glider based on "Subject:" line - for msg in msgs: - m = re.search(r'^Subject: Glider: (\w*)', msg, re.MULTILINE) - if m: - if m.group(1) == glider: - gms.append(msg) - - data = [] - for msg in gms: - m = re.search(r'^Subject: Glider: (\w*).*$', msg, re.MULTILINE) - if m: - subject_string = m.group(0) - subject_glider = m.group(1) - # print subject_string - else: - continue - - m = re.search(r'\s*(Event)*: (.*?)', subject_string, re.MULTILINE) - if m: - subject_event = m.group(2) - else: - subject_event = None - - m = re.search(r'\s*(Reason)*:\s*(.*)$', subject_string, re.MULTILINE) - if m: - subject_reason = m.group(2) - else: - subject_reason = None - - m = re.search(r'^(Vehicle Name:)\s+(\w*)', msg, re.MULTILINE) - if m: glidername = m.group(2) - else: glidername = None - - m = re.search(r'^(Curr Time:)\s+(.*)\s+MT:', msg, re.MULTILINE) - if m: - try: - t = time.strptime(m.group(2), "%a %b %d %H:%M:%S %Y") - # the '*' operator unpacks the tuple, producing the argument list. - dt = datetime.datetime(*t[0:6]) - diff = datetime.datetime.utcnow() - dt - hours_ago = display_time_diff(diff) - dt_str = datetime.date.strftime(dt, "%Y-%m-%d %H:%M:%S UTC") - if (diff.days)>0: - # dt_str_short = datetime.date.strftime(dt, "%b-%d") - dt_str_short = "" - if (diff.days) <= 0: - dt_str_short = datetime.date.strftime(dt, "%H:%M") - except ValueError, e: - dt_str = None - dt_str_short = None - hours_ago = None - - m = re.search(r'^(GPS Location:)\s+(-?\d{2})(\d{2}\.\d+)\s+([NnSs])'+ \ - r'\s+(-?\d{2})(\d{2}\.\d+)\s+([EeWw]).*$', msg, re.MULTILINE) - if m: - # - lat_deg = float(m.group(2)) - lat_min = float(m.group(3)) - lat_hem = m.group(4).upper() - # - lon_deg = float(m.group(5)) - lon_min = float(m.group(6)) - lon_hem = m.group(7).upper() - if lat_deg<0: - lat = lat_deg - lat_min/60. - else: - lat = lat_deg + lat_min/60. - if lon_deg<0: - lon = lon_deg - lon_min/60. - else: - lon = lon_deg + lon_min/60. - gps_str = m.group(0) - m = re.search(r'GPS Location: (.*) measured', gps_str) - if m: gps_posn_str = m.group(1) - else: gps_posn_str = None - m = re.search(r'measured\s*(\d*)\.\d* secs ago', gps_str) - if m: - gps_secs_ago = int(m.group(1)) - gps_dt = dt - datetime.timedelta(0,gps_secs_ago,0) - gps_dt_str = datetime.date.strftime(gps_dt, "%Y-%m-%d %H:%M:%S UTC") - else: gps_dt_str = None - else: - lat = None - lon = None - - m = re.search(r'(MT:)\s+(.*)$', msg, re.MULTILINE) - if m: mt = m.group(2) - else: mt = None - - m = re.search(r'(sensor:m_battery.*?=)\s*(-?\d+\.\d*)', msg, re.MULTILINE) - if m: batt = float(m.group(2)) - else: batt = None - - m = re.search(r'(sensor:m_leakdetect.*?=)\s*(-?\d+\.\d*)', msg, re.MULTILINE) - if m: leak = float(m.group(2)) - else: leak = None - - m = re.search(r'(sensor:m_vacuum.*?=)\s*(-?\d+\.\d*)', msg, re.MULTILINE) - if m: vacuum = float(m.group(2)) - else: vacuum = None - - m = re.search(r'^(Because:)\s*(.*)', msg, re.MULTILINE) - if m: because = m.group(2) - else: because = 'Unknown' - - m = re.search(r'^(MissionName:)\s*(.*?)\s+', msg, re.MULTILINE) - if m: mission_name = m.group(2) - else: mission_name = 'Unknown' - - m = re.search(r'\s+(MissionNum:)(.*)', msg, re.MULTILINE) - if m: mission_num = m.group(2) - else: mission_num = 'Unknown' - - m = re.search(r'^(Waypoint:)\s+(\(.*\)).*$', msg, re.MULTILINE) - if m: - wp_str = m.group(0) - waypoint_posn = m.group(2) - else: - wp_str = None - waypoint_posn = 'Unknown' - waypoint_range = 'Unknown' - waypoint_bearing = 'Unknown' - waypoint_age = 'Unknown' - wlat=None - wlon=None - - if wp_str: - # parse out the next waypoint for a place mark - m = re.search(r'\((-?\d{2})(\d{2}\.\d+),(-?\d{2})(\d{2}\.\d+)\)', waypoint_posn) - if m: - # - lat_deg = float(m.group(1)) - lat_min = float(m.group(2)) - # - lon_deg = float(m.group(3)) - lon_min = float(m.group(4)) - if lat_deg<0: - wlat = lat_deg - lat_min/60. - else: - wlat = lat_deg + lat_min/60. - if lon_deg<0: - wlon = lon_deg - lon_min/60. - else: - wlon = lon_deg + lon_min/60. - else: - wlat = None - wlon = None - m = re.search(r'(Range:)\s+(.*?),', wp_str) - if m: waypoint_range = m.group(2) - else: waypoint_range = 'Unknown' - m = re.search(r'(Bearing:)\s+(.*?),', wp_str) - if m: waypoint_bearing = m.group(2) - else: waypoint_bearing = 'Unknown' - m = re.search(r'(Age:)\s+(.*?)$', wp_str) - if m: waypoint_age = m.group(2) - else: waypoint_age = 'Unknown' - - if lat and lon: - # generate report table for google earth - # using ![CDATA[ {html} ]] inside of KML description tag - html_report = [ - ' ', - '
', - '

Surface Report

', - '', - '', - '' % (glider, dt_str,), - '', - ''] - html_report.extend( - [ - '' % (gps_posn_str,), - '' % (gps_dt_str,) - ]) - html_report.extend( - [ - '' % (subject_event,), - '' % (subject_reason,), - '' % (because,), - ]) - html_report.extend( - [ - '' % (mission_name,), - '' % (mission_num,), - '' % (mt,) - ]) - - if batt: - if batt < 10.: - html_report.append('' % (batt,)) - elif (batt>=10) and (batt<11): - html_report.append('' % (batt,)) - else: - html_report.append('' % (batt,)) - else: - html_report.append('') - - if vacuum: - if (vacuum>=10) or (vacuum<6): - html_report.append('' % (vacuum,)) - else: - html_report.append('' % (vacuum,)) - else: - html_report.append('') - # need tolerances for flagging leak detect in report - if leak: - html_report.append('' % (leak,)) - else: - html_report.append('') - # - html_report.extend( - [ - '' % (waypoint_posn,), - '' % (waypoint_range,), - '' % (waypoint_bearing,), - '' % (waypoint_age,) - ]) - # close the report tbody table div and CDATA - html_report.extend( - ['', - '
Glider: %s%s
GPS Location:%s
GPS Time:%s
Surface Event:%s
Surface Reason:%s
Surface Because:%s
Mission Name:%s
Mission Number:%s
Mission Time:%s
Battery (volts):%g
Battery (volts):%g
Battery (volts):%f
Battery (volts):Unknown
Vacuum (inHg):%g
Vacuum (inHg):%g
Vacuum (inHg):Unknown
Leak Detect (volts):%f
Leak Detect (Volts):Unknown
Waypoint:%s
Waypoint Range:%s
Waypoint Bearing:%s
Waypoint Age:%s
', - '
', - ' ' - ]) - # make a readable CDATA string - html_report_str = '\n'.join(html_report) - # can create a KML object since extracted lat and lon - data.append({'glider': glider, - 'dt': dt, - 'datetime': dt_str, - 'name': dt_str_short, - 'hours_ago': hours_ago, - 'lat': lat, - 'lon': lon, - 'description': html_report_str, - 'wlat': wlat, - 'wlon': wlon, - }) - else: - # if no lat lon, can't create a KML object without lat and lon - # but want to append info to last known surface report - html_report = [ - '
', - '

', - '', - '', - 'Glider: %s%s' % (glider, dt_str,), - '', - ''] - html_report.extend( - ['Glider Location--', - 'GPS Fix:%s' % (gps_posn_str,), - 'Time of Fix:%s' % (gps_dt_str,) - ]) - # close the report tbody table div and CDATA - html_report.extend( - ['', - '', - '
', - ' ' - ]) - - # close for-loop of msg in gms: - return data - -def generate_track_kml(data, glider): - """ Use pykml to generate kml file for each glider from parsed mail data - A glider track consists of a line and placemarks for each surfacing - - Usage: kml_doc_str = surface_report_kml(data, glider) - """ - - from pykml.factory import KML_ElementMaker as KML - import lxml.etree - - # ***** append LookAt after checking that lat, lon exist in data[-1] - d = data[-1] - # print '(%f, %f)' % (d['lon'], d['lat']) - - # start a KML file skeleton with track styles - doc = KML.kml( - KML.Document( - KML.Name(glider + "_track"), - KML.LookAt( - KML.longitude('%f' % d['lon']), - KML.latitude('%f' % d['lat']), - KML.heading('0'), - KML.tilt('0'), - KML.range('60000'), - KML.altitudeMode('absolute') - ), - KML.Style( - KML.IconStyle( - KML.scale(0.7), - KML.color("ff00ff00"), - KML.Icon(KML.href("icons/square.png")) - ), - KML.LabelStyle( - KML.color("ff00ff00"), - KML.scale(0.8)), - id="lastPosnIcon") - ) - ) - doc.Document.append( - KML.Style( - KML.IconStyle( - KML.scale(0.8), - KML.color("ff0000ff"), - KML.Icon(KML.href("icons/cross-hairs.png"))), - KML.LabelStyle( - KML.scale(0.8)), - id="lastWayPosnIcon") - ) - doc.Document.append( - KML.Style( - KML.IconStyle( - KML.scale(0.7), - KML.color("7d00ffff"), - KML.Icon(KML.href("icons/donut.png"))), - KML.LabelStyle( - KML.color("7d00ffff"), - KML.scale(0.8)), - id="prevPosnIcon") - ) - doc.Document.append( - KML.Style( - KML.IconStyle( - KML.scale(0.9), - KML.color("ff00ff00"), - KML.Icon(KML.href("icons/donut.png"))), - KML.LabelStyle( - KML.color("ff00ff00"), - KML.scale(0.7)), - id="histPosnIcon") - ) - doc.Document.append( - KML.Style( - KML.IconStyle( - KML.scale(0.8), - KML.color("7d0000ff"), - KML.Icon(KML.href("icons/cross-hairs.png"))), - KML.LabelStyle( - KML.scale(0.8)), - id="histWayPosnIcon") - ) - doc.Document.append( - KML.Style( - KML.LineStyle( - KML.color("7dff0000"), - KML.width(2) - ), - id="transBlueLine") - ) - doc.Document.append( - KML.Style( - KML.LineStyle( - KML.color("7d00ff00"), - KML.width(2) - ), - id="transGreenLine") - ) - - if glider == 'ramses': - linestyle = "#transBlueLine" - elif glider == 'pelagia': - linestyle = "#transGreenLine" - else: - linestyle = "" - - coord_str = "" - for d in data: - if d['lat'] and d['lon']: - coord_str = coord_str + "%f,%f,%f\n" % (d['lon'], d['lat'], 0.) - - track_line = KML.Placemark( - KML.name(glider), - KML.styleUrl(linestyle), - KML.LineString( - KML.altitudeMode("absolute"), - KML.coordinates(coord_str) - ) - ) - # glider placemarks (pms) - pms = [] - for d in data[:-1]: - pms.append( - KML.Placemark( - # short time stamp - KML.name(d['name']), - # surface report data - KML.description(d['description']), - KML.styleUrl("#prevPosnIcon"), - KML.Point( - KML.altitudeMode("absolute"), - KML.coordinates("%f,%f,%f" % (d['lon'], d['lat'], 0.)) - ) - ) - ) - # glider history placemarks (hpms) using timestamp tag - histpms = [] - for idx, d in enumerate(data[:-1]): - dt=d['dt'] - # YYYY-MM-DDTHH:MM:SS + Z for kml - dt_str_begin = dt.isoformat()+'Z' - dt_str_end = (data[idx+1]['dt']).isoformat()+'Z' - - histpms.append( - KML.Placemark( - # ISO time stamp to be displayed with marker - KML.name(dt_str_begin), - # surface report data (not for history placemarks - # KML.description(d['description']), - KML.styleUrl("#histPosnIcon"), - KML.Point( - KML.altitudeMode("absolute"), - KML.coordinates("%f,%f,%f" % (d['lon'], d['lat'], 0.)) - ), - KML.TimeSpan( - KML.begin(dt_str_begin), - KML.end(dt_str_end) - ) - ) - ) - - - d=data[-1] - last_pm = KML.Placemark( - # short time stamp - KML.name(d['name']), - # surface report data - KML.description(d['description']), - KML.styleUrl("#lastPosnIcon"), - KML.Point( - KML.altitudeMode("absolute"), - KML.coordinates("%f,%f,%f" % (d['lon'], d['lat'], 0.)) - ) - ) - if d['wlon'] and d['wlat']: - wp_pm = KML.Placemark( - KML.name('Target Waypoint'), - # surface report data - KML.description(''), - KML.styleUrl("#lastWayPosnIcon"), - KML.Point( - KML.altitudeMode("absolute"), - KML.coordinates("%f,%f,%f" % (d['wlon'], d['wlat'], 0.)) - ) - ) - track_folder = KML.Folder( - KML.name(d['glider']), - track_line, - ) - for pm in pms: - track_folder.append(pm) - for hpm in histpms: - track_folder.append(hpm) - track_folder.append(last_pm) - if d['wlon'] and d['wlat']: - track_folder.append(wp_pm) - doc.Document.append( - track_folder - ) - track_kml = lxml.etree.tostring(doc, pretty_print=True) - return track_kml -