#!/usr/bin/env python
#
#    Copyright (c) 2012 Tom Keffer <tkeffer@gmail.com>
#
#    See the file LICENSE.txt for your full rights.
#
#    $Revision$
#    $Author$
#    $Date$
#
"""Command line utility for configuring Davis Vantage weather stations"""

import optparse
import sys
import syslog
import time

import weewx.drivers.vantage
import weeutil.weeutil

description = """Configures the Davis Vantage weather station."""

usage="""%prog: [config_file] [--help] [--info] [--clear]
                           [--set-interval=SECONDS] [--set-altitude=FEET] [--set-barometer=inHg] 
                           [--set-bucket=CODE] [--set-rain-year-start=MM] [--set-time]
                           [--set-lamp=['on'|'off]] [--dump] [--logger_summary=FILE] [--start | --stop]"""

epilog = """Mutating actions will request confirmation before proceeding."""

def main():

    # Set defaults for the system logger:
    syslog.openlog('wee_config_vantage', syslog.LOG_PID|syslog.LOG_CONS)

    # Create a command line parser:
    parser = optparse.OptionParser(description=description, usage=usage, epilog=epilog)
    
    # Add the various options:
    parser.add_option("--config", dest="cfgfn", type=str, metavar="FILE",
                      help="use configuration file FILE")
    parser.add_option("--info", action="store_true", dest="info",
                      help="To print configuration, reception, and barometer calibration information about your weather station.")
    parser.add_option("--clear", action="store_true", dest="clear",
                      help="To clear the memory of your weather station.")
    parser.add_option("--set-interval", type=int, dest="set_interval", metavar="SECONDS",
                      help="Sets the archive interval to the specified number of seconds. "\
                          "Valid values are 60, 300, 600, 900, 1800, 3600, or 7200.")
    parser.add_option("--set-altitude", type=float, dest="set_altitude", metavar="FEET",
                      help="Sets the altitude of the station to the specified number of feet.") 
    parser.add_option("--set-barometer", type=float, dest="set_barometer", metavar="inHg",
                      help="Sets the barometer reading of the station to a known correct value in inches of mercury. "\
                          "Specify 0 (zero) to have the console pick a sensible value.")
    parser.add_option("--set-bucket", type=int, dest="set_bucket", metavar="CODE",
                      help="Set the type of rain bucket. "\
                          "Specify '0' for 0.01 inches; '1' for 0.2 MM; '2' for 0.1 MM")
    parser.add_option("--set-rain-year-start", type=int, dest="set_rain_year_start", metavar="MM", 
                      help="Set the rain year start (1=Jan, 2=Feb, etc.).")
    parser.add_option("--set-time", action="store_true", dest="set_time", help="Set the onboard clock to the current time.")
    parser.add_option("--set-lamp", dest="set_lamp", help="Turn the console lamp 'ON' or 'OFF'", metavar="ON|OFF")
    parser.add_option("--start", action="store_true", help="Start the logger.")
    parser.add_option("--stop",  action="store_true", help="Stop the logger.")
    parser.add_option("--dump",  action="store_true", help="Dump all data to the archive. "\
                          "NB: This may result in many duplicate primary key errors.")
    parser.add_option("--logger-summary", type="string", dest="logger_summary", 
                      metavar="FILE", help="Save diagnostic summary to FILE (for debugging the logger).")
    
    # Now we are ready to parse the command line:
    (options, args) = parser.parse_args()
    config_fn, config_dict = weeutil.weeutil.read_config(options.cfgfn, args)
    print "Using configuration file %s." % config_fn
        
    if options.start and options.stop:
        parser.error("Cannot specify both --start and --stop")

    # Open up the weather station:
    station = weewx.drivers.vantage.Vantage(**config_dict['Vantage'])

    if options.info:
        info(station)
    if options.clear:
        clear(station)
    if options.set_interval is not None:
        set_interval(station, options.set_interval)
    if options.set_altitude is not None:
        set_altitude(station, options.set_altitude)
    if options.set_barometer is not None:
        set_barometer(station, options.set_barometer)
    if options.set_bucket is not None:
        set_bucket(station, options.set_bucket)
    if options.set_rain_year_start is not None:
        set_rain_year_start(station, options.set_rain_year_start)
    if options.set_time:
        set_time(station)
    if options.set_lamp:
        set_lamp(station, options.set_lamp)
    if options.start:
        start_logger(station)
    if options.stop:
        stop_logger(station)
    if options.dump:
        dump_logger(station, config_dict)
    if options.logger_summary:
        logger_summary(station, options.logger_summary)
           
def info(station, dest=sys.stdout):
    """Query the configuration of the Vantage, printing out status information"""
    
    print "Querying..."
    
    try:
        _firmware_date = station.getFirmwareDate()
    except Exception:
        _firmware_date = "<Unavailable>"
        
    try:
        _firmware_version = station.getFirmwareVersion()
    except Exception:
        _firmware_version = '<Unavailable>'
    
    console_time = station.getConsoleTime()
    
    print >>dest, """Davis Vantage EEPROM settings:
    
    CONSOLE TYPE:                   %s
    
    CONSOLE FIRMWARE:
      Date:                         %s
      Version:                      %s
    
    CONSOLE SETTINGS:
      Archive interval:             %d (seconds)
      Altitude:                     %d (%s)
      Wind cup type:                %s
      Rain bucket type:             %s
      Rain year start:              %d
      Onboard time:                 %s
      
    CONSOLE DISPLAY UNITS:
      Barometer:                    %s
      Temperature:                  %s
      Rain:                         %s
      Wind:                         %s
      """ % (station.hardware_name, _firmware_date, _firmware_version,
             station.archive_interval, station.altitude, station.altitude_unit,
             station.wind_cup_size, station.rain_bucket_size, station.rain_year_start, console_time,
             station.barometer_unit, station.temperature_unit, 
             station.rain_unit, station.wind_unit)
    try:
        (stnlat, stnlon, time_zone, man_or_auto, dst, gmt_offset, gmt_or_zone) = station.getStnInfo()
        print >>dest, """    CONSOLE STATION INFO:
      Latitude (onboard):           %0.1f
      Longitude (onboard):          %0.1f
      Time zone code:               %s
      Use manual or auto DST?       %s
      DST setting:                  %s
      GMT offset:                   %+.1f hours
      Use GMT offset or time zone?  %s
        """ % (stnlat, stnlon, time_zone, man_or_auto, dst, gmt_offset, gmt_or_zone)
    except:
        pass
    
    
    # Add reception statistics if we can:
    try:
        _rx_list = station.getRX()
        print >>dest, """    RECEPTION STATS:
      Total packets received:       %d
      Total packets missed:         %d
      Number of resynchronizations: %d
      Longest good stretch:         %d
      Number of CRC errors:         %d
      """ % _rx_list
    except:
        pass

    # Add barometer calibration data if we can.
    try:
        _bar_list = station.getBarData()
        print >>dest, """    BAROMETER CALIBRATION DATA:
      Current barometer reading:    %.3f inHg
      Altitude:                     %.0f feet
      Dew point:                    %.0f F
      Virtual temperature:          %.0f F
      Humidity correction factor:   %.0f
      Correction ratio:             %.3f
      Correction constant:          %+.3f inHg
      Gain:                         %.3f
      Offset:                       %.3f
      """   % _bar_list
    except:
        pass

def set_interval(station, new_interval_seconds):
    """Set the console archive interval."""
    
    print "Old archive interval is %d seconds, new one will be %d seconds." % (station.archive_interval, new_interval_seconds)
    if station.archive_interval == new_interval_seconds:
        print "Old and new archive intervals are the same. Nothing done."
    else:
        ans = None
        while ans not in ['y', 'n']:
            print "Proceeding will change the archive interval as well as erase all old archive records."
            ans = raw_input("Are you sure you want to proceed (y/n)? ")
            if ans == 'y' :
                try:
                    station.setArchiveInterval(new_interval_seconds)
                except StandardError, e:
                    print >>sys.stderr, "Unable to set new archive interval. Reason:\n\t****", e
                else:
                    print "Archive interval now set to %d seconds." % (station.archive_interval,)
                    # The Davis documentation implies that the log is cleared after
                    # changing the archive interval, but that doesn't seem to be the
                    # case. Clear it explicitly:
                    station.clearLog()
                    print "Archive records cleared."
            elif ans == 'n':
                print "Nothing done."
    
def set_altitude(station, altitude_ft):
    """Set the console station altitude"""
    # Hit the console to get the current barometer calibration data:
    _bardata = station.getBarData()

    ans = None
    while ans not in ['y', 'n']:    
        print "Proceeding will set the barometer value to %.3f and the station altitude to %.1f feet." % (_bardata[0], altitude_ft)
        ans = raw_input("Are you sure you wish to proceed (y/n)? ")
        if ans == 'y':
            station.setBarData(_bardata[0], altitude_ft)
        elif ans == 'n':
            print "Nothing done."

def set_barometer(station, barometer_inHg):
    """Set the barometer reading to a known correct value."""
    # Hit the console to get the current barometer calibration data:
    _bardata = station.getBarData()
    
    ans = None
    while ans not in ['y', 'n']:
        if barometer_inHg:
            print "Proceeding will set the barometer value to %.3f and the station altitude to %.1f feet." % (barometer_inHg, _bardata[1])
        else:
            print "Proceeding will have the console pick a sensible barometer calibration and set the station altitude to %.1f feet," % (_bardata[1],)
        ans = raw_input("Are you sure you wish to proceed (y/n)? ")
        if ans == 'y':
            station.setBarData(barometer_inHg, _bardata[1])
        elif ans == 'n':
            print "Nothing done."
    
def clear(station):
    """Clear the archive memory of a VantagePro"""
    
    ans = None
    while ans not in ['y', 'n']:
        print "Clearing the archive memory ..."
        print "Proceeding will erase old archive records."
        ans = raw_input("Are you sure you wish to proceed (y/n)? ")
        if ans == 'y':
            station.clearLog()
            print "Archive records cleared."
        elif ans == 'n':
            print "Nothing done."

def set_bucket(station, new_bucket_type):
    """Set the bucket type on the console."""

    print "Old rain bucket type is %d (%s), new one is %d (%s)." % (station.rain_bucket_type, 
                                                                    station.rain_bucket_size,
                                                                    new_bucket_type, 
                                                                    weewx.drivers.vantage.Vantage.rain_bucket_dict[new_bucket_type])
    if station.rain_bucket_type == new_bucket_type:
        print "Old and new bucket types are the same. Nothing done."
    else:
        ans = None
        while ans not in ['y', 'n']:
            print "Proceeding will change the rain bucket type."
            ans = raw_input("Are you sure you want to proceed (y/n)? ")
            if ans == 'y' :
                try:
                    station.setBucketType(new_bucket_type)
                except StandardError, e:
                    print >>sys.stderr, "Unable to set new bucket type. Reason:\n\t****", e
                else:
                    print "Bucket type now set to %d." % (station.rain_bucket_type,)
            elif ans == 'n':
                print "Nothing done."

def set_rain_year_start(station, rain_year_start):
        
    print "Old rain season start is %d, new one is %d." % (station.rain_year_start, rain_year_start)

    if station.rain_year_start == rain_year_start:
        print "Old and new rain season starts are the same. Nothing done."
    else:
        ans = None
        while ans not in ['y', 'n']:
            print "Proceeding will change the rain season start."
            ans = raw_input("Are you sure you want to proceed (y/n)? ")
            if ans == 'y' :
                try:
                    station.setRainYearStart(rain_year_start)
                except StandardError, e:
                    print >>sys.stderr, "Unable to set new rain year start. Reason:\n\t****", e
                else:
                    print "Rain year start now set to %d." % (station.rain_year_start,)
            elif ans == 'n':
                print "Nothing done."

def set_time(station):
    print "Setting time on console..."
    station.setTime(time.time())
    newtime_ts = station.getTime()
    print "Current console time is %s" % weeutil.weeutil.timestamp_to_string(newtime_ts)

def set_lamp(station, onoff):
    print "Setting lamp on console..."
    station.setLamp(onoff)
    
def start_logger(station):
    print "Starting logger ..."
    station.startLogger()
    print "Logger started"
    
def stop_logger(station):
    print "Stopping logger ..."
    station.stopLogger()
    print "Logger stopped"
    
def dump_logger(station, config_dict):
    import user.schemas
    import weedb
    import weewx.archive

    archive_db = config_dict['StdArchive']['archive_database']
    archive_db_dict = config_dict['Databases'][archive_db]
    try:
        archive = weewx.archive.Archive.open(archive_db_dict)
        print "Opened existing database '%s'" % (archive_db,)
    except weedb.OperationalError:
        # Database does not exist. Do an open_with_create:
        archive = weewx.archive.Archive.open_with_create(archive_db_dict, user.schemas.defaultArchiveSchema)
        print "Created database '%s'" % (archive_db,)

    print "Starting dump ..."
    nrecs = 0
    for record in station.genArchiveDump():
        archive.addRecord(record)
        nrecs += 1
        if nrecs%10 == 0:
            print >>sys.stdout, "Records processed: %d; Timestamp: %s\r" % (nrecs, weeutil.weeutil.timestamp_to_string(record['dateTime'])),
            sys.stdout.flush()
    print "\nFinished dump. %d records added" % (nrecs,)
    
def logger_summary(station, dest_path):
    dest = open(dest_path, mode="w")

    info(station, dest)
    
    print "Starting download of logger summary..."
    
    nrecs = 0
    for (page, index, y, mo, d, h, mn, time_ts) in station.genLoggerSummary():
        if time_ts:
            print >>dest, "%4d %4d %4d | %4d-%02d-%02d %02d:%02d | %s" % (nrecs, page, index, y+2000, mo, d, h, mn, weeutil.weeutil.timestamp_to_string(time_ts))
        else:
            print >>dest, "%4d %4d %4d [*** Unused index ***]" % (nrecs, page, index)
        nrecs += 1
        if nrecs%10 == 0:
            print >>sys.stdout, "Records processed: %d; Timestamp: %s\r" % (nrecs, weeutil.weeutil.timestamp_to_string(time_ts)),
            sys.stdout.flush()
    print "\nFinished download of logger summary to file '%s'. %d records processed." % (dest_path, nrecs)
    
if __name__=="__main__" :
    main()
