#!   /usr/bin/env   python
#    coding: utf8

# Copyright CERN, 2011
# Author: Matthieu Cattin <matthieu.cattin@cern.ch>
# Licence: GPL v2 or later.
# Website: http://www.ohwr.org
# Last modifications: 16/5/2012

# Import system modules
import sys
import time
import datetime
import os

# Add common modules and libraries location to path
sys.path.append('../../../')
sys.path.append('../../../gnurabbit/python/')
sys.path.append('../../../common/')
sys.path.append('../../fmceeprom/python/')

# Import common modules
from ptsexcept import *
from fmc_eeprom import *
import rr

# Import specific modules
from fmc_adc_spec import *
from fmc_adc import *


"""
test23: Write IPMI information and calibration data to FMC EEPROM.
        This is done using gensdbfs tool.
        Serial number and calibration data are taken from text files.

Note: Requires test00.py to run first to load the firmware!
      Requires test19.py to run before to get calibration data.
"""

def main (default_directory='.'):

    # Constants declaration
    TEST_NB = 23
    EXPECTED_BITSTREAM_TYPE = 0x1
    PART_NUMBER = "EDA-02063-V5-0"
    SERIAL_FILENAME = "../../../serial.txt"
    SERIAL_FILENAME = os.path.join(default_directory, SERIAL_FILENAME)
    CALIBR_FILENAME = "calibration_data.txt"
    CALIBR_FILENAME = os.path.join(default_directory, CALIBR_FILENAME)
    SDBFS_DIR = "sdbfs/"
    SDBFS_DIR = os.path.join(default_directory, SDBFS_DIR)
    IPMI_BIN_FILENAME = SDBFS_DIR + "ipmi.sdb"
    CALIBR_BIN_FILENAME = SDBFS_DIR + "calibration.sdb"
    EEPROM_BIN_FILENAME = SDBFS_DIR + "eeprom_content.out"
    EEPROM_SIZE = 8192 # in Bytes


    start_test_time = time.time()
    print "================================================================================"
    print "Test%02d start\n" % TEST_NB

    # SPEC object declaration
    print "Loading hardware access library and opening device.\n"
    spec = rr.Gennum()

    # Carrier object declaration (SPEC board specific part)
    try:
        carrier = CFmcAdc100mSpec(spec, EXPECTED_BITSTREAM_TYPE)
    except FmcAdc100mSpecOperationError as e:
        raise PtsCritical("Carrier init failed, test stopped: %s" % e)

    # Mezzanine object declaration (FmcAdc100m14b4cha board specific part)
    try:
        fmc = CFmcAdc100m(spec)
    except FmcAdc100mOperationError as e:
        raise PtsCritical("Mezzanine init failed, test stopped: %s" % e)


    ###########################################################################
    # Real test stuff here
    try:
        #==================================================
        # Read serial number from a file
        # This file must be written by the shell script launching pts
        f_serial = open(SERIAL_FILENAME, 'r+')
        serial = f_serial.readline()
        f_serial.close()
        serial = serial[:-1] # remove EOL char
        print "Board's serial number: %s\n" % serial

        #==================================================
        # Calculate number of minutes since 0:00 1/1/96
        now_date = datetime.datetime.now()
        ref_date = datetime.datetime(1996, 1, 1)
        diff_date = now_date - ref_date
        current_date = int(diff_date.total_seconds()//60)
        print "Current date/time: %d minutes (since 0:00 1/1/96)\n" % current_date

        #==================================================
        # Read calibration data from text file
        # This file must be written by the test performing the calibration
        f_calibr = open(CALIBR_FILENAME, 'r+')
        calibr_data = []
        for line in iter(f_calibr):
            calibr_data.append(0xFF & int(line, 16))
            calibr_data.append(0xFF & (int(line, 16) >> 8))
        f_calibr.close()

        #print "Raw calibration data:"
        #for data in calibr_data:
        #    print "0x%02X" % (data)

        #==================================================
        # Write calibration data to binary file
        # This file will be feed to gensdbfs
        f_bin_calibr = open(CALIBR_BIN_FILENAME, 'wb')
        for byte in calibr_data:
            f_bin_calibr.write(chr(byte))
        f_bin_calibr.close()

        ################################################################################
        # Check if a manufacturing date is present in the EEPROM.
        # If not, put the current date (it means it's the first time the test is run).
        # If a date is present, keep it.

        # EEPROM clear code used to test the test!
        #eeprom_data = [0x0] * EEPROM_SIZE
        #fmc.sys_i2c_eeprom_write(eeprom_data)

        # Read entire EEPROM
        #print "Read EEPROM content."
        eeprom_data_read = fmc.sys_i2c_eeprom_read(0, EEPROM_SIZE)

        # Write EEPROM data to binary file
        #print "Write EEPROM content to file (binary)."
        f_eeprom = open(EEPROM_BIN_FILENAME, "wb")
        for byte in eeprom_data_read:
            f_eeprom.write(chr(byte))
        f_eeprom.close()

        # Get manufacturing date from EEPROM data, if exists
        print "Get manufacturing date from EEPROM."
        eeprom_data = open(EEPROM_BIN_FILENAME, "rb").read()
        mfg_date = ipmi_get_mfg_date(eeprom_data)

        # No manufacturing date present in EEPROM, put the current date
        if(mfg_date == 0):
            print "No manufacturing date found in the EEPROM, taking current date: %d" % current_date
            mfg_date = current_date
        else:
            print "Manufacturing date found in EEPROM: %d (will be preserved)\n" % mfg_date

        #==================================================
        # Create Board Info Area
        # FRU field is used to store the date of generation of the eeprom content
        # This could be used later to determine if the content has to be udated (bug fix, ...)
        print "EEPROM content generated: %s\n" % now_date
        fru = "%s" % now_date
        bia = BoardInfoArea(mfg_date, "CERN", "FmcAdc100m14b4cha", serial, PART_NUMBER, fru)

        #==================================================
        # Multirecords Area

        # output number, vnom, vmin, vmax, ripple, imin, imax
        dcload0 = DCLoadRecord(0, 2.5, 2.375, 2.625, 0.0, 0, 4000) # VADJ
        dcload1 = DCLoadRecord(1, 3.3, 3.135, 3.465, 0.0, 0, 3000) # P3V3
        dcload2 = DCLoadRecord(2, 12.0, 11.4, 12.6, 0.0, 0, 1000)  # P12V
        dcload = [ dcload0, dcload1, dcload2 ]

        # output number, vnom, vmin, vmax, ripple, imin, imax
        dcout0 = DCOutputRecord(3, 0.0, 0.0, 0.0, 0.0, 0, 0) # VIO_B_M2C
        dcout1 = DCOutputRecord(4, 0.0, 0.0, 0.0, 0.0, 0, 0) # VREF_A_M2C
        dcout2 = DCOutputRecord(5, 0.0, 0.0, 0.0, 0.0, 0, 0) # VREF_B_M2C
        dcout = [ dcout0, dcout1, dcout2 ]

        # module size  : 0=single width, 1=double width
        # P1 size      : 0=LPC, 1=HPC
        # P2 size      : 0=LPC, 1=HPC, 3=not fitted
        # clock dir    : 0=M2C, 1=C2M
        # nb sig P1 A  : number
        # nb sig P1 B  : number
        # nb sig P2 A  : number
        # nb sig P2 B  : number
        # nb GBT P1    : number
        # nb GBT P2    : number
        # max TCK freq : frequency in MHz
        oem = OEMRecord(0, 0, 3, 0, 68, 0, 0, 0, 0, 0, 0)

        #==================================================
        # Write ipmi content to a binary file
        ipmi_open_file(IPMI_BIN_FILENAME)
        ipmi_set(bia, dcload, dcout, oem)
        ipmi_write()
        ipmi_close_file()

        #==================================================
        # Read impi content from binary file
        f_ipmi = open(IPMI_BIN_FILENAME, "rb")
        ipmi_data = []
        byte = f_ipmi.read(1) # reads one byte
        while byte:
            ipmi_data.append(ord(byte))
            byte = f_ipmi.read(1) # reads one byte
        f_ipmi.close()

        #print "Raw IPMI data:"
        #for add, data in enumerate(ipmi_data):
        #    print "0x%02X(%3d): 0x%02X" % (add, add, data)

        #==================================================
        # Generate eeprom image with gensdbfs
        cmd = "gensdbfs " + SDBFS_DIR + " " + EEPROM_BIN_FILENAME
        print("Generate eeprom image, cmd: %s"%(cmd))
        os.system(cmd)

        #==================================================
        # Erase EEPROM content
        print "Erase EEPROM content.\n"
        eeprom_data = [0x0] * EEPROM_SIZE
        fmc.sys_i2c_eeprom_write(eeprom_data)

        #==================================================
        # Read eeprom content from binary file
        f_bin_eeprom = open(EEPROM_BIN_FILENAME, "rb")
        eeprom_data = []
        byte = f_bin_eeprom.read(1) # reads one byte
        while byte:
            eeprom_data.append(ord(byte))
            byte = f_bin_eeprom.read(1) # reads one byte
        f_bin_eeprom.close()

        #==================================================
        # Write content to EEPROM via I2C
        print "Write EEPROM content.\n"
        if eeprom_data != []:
            fmc.sys_i2c_eeprom_write(eeprom_data)

        #==================================================
        # Read back EEPROM content via I2C
        eeprom_data_read = fmc.sys_i2c_eeprom_read(0, len(eeprom_data))
        mismatch = 0
        for i in range(len(eeprom_data)):
            wr_data = eeprom_data[i]
            rd_data = eeprom_data_read[i]
            if wr_data == rd_data:
                check = "OK"
            else:
                check = "FAILED"
                mismatch += 1
                print "0x%02X 0x%02X => %s" % (wr_data, rd_data, check)
            #print "0x%02X 0x%02X => %s" % (wr_data, rd_data, check)
        print "EEPROM content comparision => ",
        if(mismatch == 0):
            print "OK"
        else:
            print "FAILED"

    except FmcAdc100mOperationError as e:
        raise PtsError("Test failed: %s" % e)

    ###########################################################################

    print ""
    print "==> End of test%02d" % TEST_NB
    print "================================================================================"
    end_test_time = time.time()
    print "Test%02d elapsed time: %.2f seconds\n" % (TEST_NB, end_test_time-start_test_time)

    # Check if an error occured during EEPROM verification
    if(mismatch != 0):
        raise PtsError("EEPROM comparison failed: %d mismatch found. Check log for details." % mismatch)


if __name__ == '__main__' :
    main()
