#!   /usr/bin/python
#    coding: utf8
##________________________________________________________________________________________________
##
##                                        CONV-TTL-RS485 PTS
##
##                                         CERN,BE/CO-HT
##________________________________________________________________________________________________
##
##------------------------------------------------------------------------------------------------
##
##                                  CONV-TTL-RS485 test sequencer
##
##------------------------------------------------------------------------------------------------
##
## Description  This module gets called by the main PTS module (pts.py) to run the tests defined
##              via the make-links.bat script. The tests are listed within this script and symlinks
##              are created to point to the actual Pyton programs running the tests.
##
##              The task of this module is to run these Python programs in a sequence and print the
##              test results to output files.
##
##              Two types of output files exist for this purpose. The first is the log file (.log),
##              which contains brief information, PASS/FAIL for each test. The second, the info
##              file (.inf) contains more detailed information, listing the steps the test takes
##              and the reasons for which the test failed (if it failed).
##
## Authors      Julian Lewis (Julian.Lewis@cern.ch)
##              Theodor Stana (t.stana@cern.ch)
## Website      http://www.ohwr.org/projects/conv-ttl-rs485
## Date         30/10/2014
##-------------------------------------------------------------------------------------------------
##
##-------------------------------------------------------------------------------------------------
##                               GNU LESSER GENERAL PUBLIC LICENSE
##                              ------------------------------------
## This source file is free software; you can redistribute it and/or modify it under the terms of
## the GNU Lesser General Public License as published by the Free Software Foundation; either
## version 2.1 of the License, or (at your option) any later version.
## This source is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
## without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
## See the GNU Lesser General Public License for more details.
## You should have received a copy of the GNU Lesser General Public License along with this
## source; if not, download it from http://www.gnu.org/licenses/lgpl-2.1.html
##-------------------------------------------------------------------------------------------------

##------------------------------------------------------------------------------------------------
##                                            Import
##------------------------------------------------------------------------------------------------
import sys
sys.path.append('.')
sys.path.append("pyts")
import ptsexcept
import ptsdefine
import time
import traceback
import socket
from optparse import OptionParser
from vv_pts import *
from os import path, access, R_OK, readlink

##-------------------------------------------------------------------------------------------------
## Method to get the test program a symlink points to
##-------------------------------------------------------------------------------------------------
def lnk_ptr(tname):

    lname = "./%s.py" % (tname)
    if path.islink(lname):
        return readlink(lname)
    else:
        return tname

##-------------------------------------------------------------------------------------------------
## Method to run the test
##-------------------------------------------------------------------------------------------------
def run_test(tname,bus,inf,log):

    # Each test should have a main function in it running the necessary steps
    # to run. Here we import the name of the test and run the main function,
    # printing the docstring at the beginning of this function.
    #
    # The number of errors returned by the test's main function is returned
    # by this function to the JPTS program.
    try:
        mod = __import__(tname)
        msg = mod.main.__doc__
        print msg
        log.write("%s\n" % (msg))
        inf.write("%s\n" % (msg))
        time.sleep(1)
        return mod.main(bus,tname,inf,log)

    except PtsError, e:
        msg = "%s %s->%s\n" % (e, tname, lnk_ptr(tname))
        print "ABORT: %s" % (msg)
        inf.write(msg)
        log.write(msg)
        return 1

##-------------------------------------------------------------------------------------------------
## Main "method" of JPTS
##-------------------------------------------------------------------------------------------------
if __name__ == '__main__':

    # Print usage information
    use = "Usage: %prog [--lun=0 --snum=1234 5678 --debug] test_num,run_count test_num,run_count..."
    parser = OptionParser(usage=use, version="pts version 2.0")

    parser.add_option("-l", "--lun", help="Logical Unit Number", type="int", default=ELMASLOT, dest="lun")
    parser.add_option("-s", "--snum", help="Serial number(s)", dest="snum", action="store", nargs=2, default=("0","0"))
    parser.add_option("-n", "--name", help="Board name", dest="board_name", default=BOARD)
    parser.add_option("-b", "--bus", help="Bus VME/PCI/SKT", dest="bus_type", default="SKT")
    parser.add_option("-o", "--outdir", help="Path to log directory", dest="dir_name", default=".")
    parser.add_option("-d", "--debug", help="Debug Option", dest="debug", default=False, action="store_true")

    options, args = parser.parse_args()

    # Print further information if in debug mode
    if options.debug:
        print "\n"
        print "options (Logical Unit Number) lun   :%d"    % options.lun
        print "options (Serial numbers pair) snum  :%s %s" % options.snum
        print "options (Board name         ) name  :%s"    % options.board_name
        print "options (Bus VME PCI or SKT ) bus   :%s"    % options.bus_type
        print "options (Location of log dir) outdir:%s"    % options.dir_name
        print "options (Debug printing flag) debug :%d"    % options.debug
        print "arglist (List: Test,RunCount)       :%s"    % (args)

    # Create test array from existing test links
    tns = []
    if len(args):
        for a in args:
            nstr = a.split(',')
            tn = int(nstr[0])
            if len(nstr) == 2:
                tc = int(nstr[1])
            else:
                tc = 1
            tns.append((tn,tc))
    else:
        for a in range(19):
            tns.append((a,1))

    # Get logical unit number, serial numbers, board name, log file dir from command line args
    # and create path strings for log and info files
    lun = options.lun
    sno, xno = options.snum
    bnm = options.board_name
    dir = options.dir_name

    now = time.asctime(time.localtime(time.time()))
    now = now.replace(" ","-")
    now = now.replace(":","-")

    lgf = "%s/log/%s-%s-%s-%s-%1d.log" % (dir,bnm,sno,xno,now,lun)
    inf = "%s/log/%s-%s-%s-%s-%1d.inf" % (dir,bnm,sno,xno,now,lun)

    # Open the log and info files
    try:
        log = open(lgf, "w")
        inf = open(inf, "w")

    except Exception, e:
        msg = "Exception: %s" % (e)
        print "Fatal: %s" % (msg)
        sys.exit(1)

    # and print them to console if in debug mode
    if options.debug:
        print "\n"
        print "Log file is: %s" % lgf
        print "Inf file is: %s" % inf

    print "\n"

    # Open up a bus object, can be one of following (depending on tested device):
    # * VME -- VME board (SVEC, etc.)
    # * PCI -- PCI board (SPEC, etc.)
    # * SKT -- board connecting through I2C lines on VME backplane (CONV-TTL-BLO, etc.)
    try:
        if options.bus_type == "VME":
            bus = VME(lun)
        elif options.bus_type == "PCI":
            bus = PCI(lun)
        else:
            bus = SKT(lun)

        bus.vv_init()

    except BusException, e:
        print "Fatal:BUS Exception: %s" % (e)

    except BusWarning, e:
        print "Warning:Bus Exception: %s" % (e)

    # Start running the tests.
    for t in tns:

        tname = "test%02d" % t[0]
        pyt = "%s/%s.py"  % (dir, tname)

        if path.exists(pyt) and path.isfile(pyt) and access(pyt, R_OK):

            for n in range(t[1]):

                if n == 10:
                    msg = "Printing suppresses after 10 runs"
                    print msg
                    log.write('\n' + msg + '\n')
                    inf.write('\n' + msg + '\n')

                if n < 10:
                    msg = "Run:%d Begin:%s" % (n+1,tname)
                    print msg
                    log.write('\n' + msg + '\n')
                    inf.write('\n' + msg + '\n')

                try:
                    # First load a firmware and read back a register to confirm correct load.
                    if bus.vv_load() == 0:
                        msg = "INFO: Found CONV-TTL-RS485 board in slot %d\n" % (lun)
                        inf.write(msg + '\n')

                    # Each test is passed the test name, the log and info files and the
                    # bus object. The test program is expected to return the number of
                    # errors that occured. If no errors occur, the test PASSes, otherwise
                    # it FAILs.
                    cc = run_test(tname,bus,inf,log)
                    if cc == 0:
                        msg = "PASS: %s" % (tname)
                        log.write(msg + '\n')
                        inf.write(msg + '\n')
                        print msg
                    else:
                        msg = "FAIL: %s->%s" % (tname, lnk_ptr(tname))
                        log.write(msg + '\n')
                        inf.write(msg + '\n')
                        print msg

                except Exception, e:
                    if options.debug:
                        print e
                        traceback.print_exc()

                    msg = "FAIL: %s->%s (%s)" % (tname, lnk_ptr(tname), e)
                    log.write(msg + '\n')
                    inf.write(msg + '\n')
                    print msg

                if n < 10:
                    msg = "Run:%d End:%s\n" % (n+1,tname)
                    print msg
                    log.write(msg + '\n')
                    inf.write(msg + '\n')

    # Close the bus and the files
    bus.vv_close()
    log.close()
    inf.close()

    # Print info file to console if in debug mode
    if options.debug:
        print "Debug: printing:%s" % (inf)
        inf = open(inf, "r")
        try:
            for line in inf:
                l = line.split("\n")
                print "%s" % l[0]

        finally:
            inf.close()

    sys.exit(0)
