# This script will go through the polaris recal values on all polaris chips
# for both the Position and vector antenna. We first do a check for valid temp range,
# Wafer ID and previous calibration (A5 pattern) before doing a recal.
# Parameters can be passed to ignore all the checks, set an IP and tell the script to 
# either use the Polaris Mux or ignore it.(-i, -IP <IPADDR>, -PM)
# The default parameters are IP: localhost:8080 and NOT setting the Polaris Multiplexer
# This script may have to be updated to support firmware changes to include GET requests
# for Polaris register reads.
# Python Version : 3.x
# packages used: time, requests, argparse, xml
# Copyright Trimble 2018
import time
import requests
import argparse
import xml.etree.ElementTree as ET

#These are all the program sequence lists for the requests sent
#calibration sequence form data
form_data =[('0x0D','0x5C'),('0x1E','0x01'),('0x34','0x02'),('0x34','0x00'),('0x01','0x48'),('0x1E','0x01'),('0x34','0x01'),('0x34','0x00'),('0x0D','0x01')]
#set A5 pattern form data
scratch_data = [('0x0D','0x5C'),('0x1E','0x0F'),('0x1F','0xA5'),('0x34','0x01'),('0x34','0x00'),('0x0D','0x01')]
#the 0x1F is a tuple for the read in the scratch register
scratch_check = [('0x0D','0x5C'),('0x1E','0x0F'),('0x34','0x02'),('0x34','0x00'),('0x1F','0x00'),('0x0D','0x01')]
wafer_check = [('0x0D','0x5C'),('0x1E','0x0E'),('0x34','0x02'),('0x34','0x00'),('0x1F','READ'),('0x20','READ'),('0x21','READ'),('0x22','READ'),('0x23','READ'),('0x24','READ'),('0x25','READ'),('0x26','READ'),('0x0D','0x01')]
polaris_addr = ['0x30','0x31','0x32','0x33','0x34','0x35','0x36','0x37']
authorization = ('admin','password')

#Configuration variables
IP_Addr = 'localhost:8080'
polaris_url = 'http://'+IP_Addr+'/cgi-bin/polarisTest.xml?'
polaris_switch = 0
polarisMux = 0
ignoreCheck = 0

#constants for wafer ID check
POLARIS_RAW_ID_PRIMARY_MASK = 0xFFFFFFFFFFF80000
POLARIS_RAW_ID_SECONDARY_MASK = 0xFFFFFFFFFFFFC000
POLARIS_SMALL_BATCH01_PRIMARY_COMPARE = 0x315BB366C5B00000
POLARIS_SMALL_BATCH01_SECONDARY_COMPARE = 0x315BB366C5B04000
POLARIS_LARGE_BATCH02_COMPARE = 0x3198B26CC5C00000
POLARIS_LARGE_BATCH03_COMPARE = 0x31993170C5A80000

def sendRequestData(url,request_data):
    r = requests.post(url=polaris_url,auth=authorization,params=request_data,timeout=3)
    time.sleep(0.05)

def writePolarisMux(hasPolarisMux,MuxVal):
    """
    This function writes to the polaris multiplexer when we need to switch
    between the position and vector antenna
    """
    if hasPolarisMux:
        reg_data = {'i2cSource': MuxVal}
        sendRequestData(polaris_url,reg_data)

def getPolarisReadFromXML(register,chipAddr):
    """
    Because we don't have a direct GET request through the webpage for reading 
    the polaris register, we need to first do a POST and then read the register 
    data from the polaris log XML
    """
    #first have to do the read and then read it from the XML log
    reg_data = {'request':'1','i2cAddr':chipAddr,'regAddr':register,'data':'0x00'}
    sendRequestData(polaris_url,reg_data)
    
    #now setup the Elemtree and read the most recent XML log data
    dynamicLog_url = 'http://'+IP_Addr+'/xml/dynamic/polarisTest.xml'

    resp = requests.get(dynamicLog_url,auth=authorization)

    #gets the text(the XML string) from the GET XML response earlier
    msg = resp.content

    #create element tree
    tree = ET.fromstring(msg)

    #finds the first instance of the logs XML tag
    readLog = tree.find('logs')
    #gets the most recent data under the datalog tag
    scratchVal = readLog.find('dataLog').text
    
    #can't return an ascii value in format 0x with base 10, have to convert it to hex 
    return int(scratchVal,16)

def checkTempValue():
    #system stores temperature data in powerData.xml
    #use ElemTrees to parse the XML and get the celsius tag
    dynamicLog_url = 'http://'+IP_Addr+'/xml/dynamic/powerData.xml'

    resp = requests.get(dynamicLog_url,auth=authorization)

    #gets the text(the XML string) from the GET XML response earlier
    msg = resp.content

    #create element tree
    tree = ET.fromstring(msg)

    #finds the first instance of the logs XML tag
    readLog = tree.find('T1')
    #gets the most recent data under the datalog tag
    tempVal = readLog.find('celsius').text
    
    # Python does not allow directly converting a string representation of a float to an int
    # We are also checking to see if the temperature is the default value of 60 if there is no RF antenna connected
    return ( (int(float(tempVal)) > 0) and (int(float(tempVal)) < 50) ) or int(float(tempVal)) == 60

    
def checkCalibrationPattern(chipAddr):
    for x in scratch_check:
    
        if x[0] == '0x1F':
            #0x1F is the EE_DATA0 register
            scratchVal = getPolarisReadFromXML(x[0],chipAddr)
        else:
            reg_data = {'request':'0','i2cAddr':chipAddr,'regAddr':x[0],'data':x[1]}
            sendRequestData(polaris_url,reg_data)
    if scratchVal == 0xA5:
        #want to return false if pattern exists since this method is a check
        #to see if the A5 doesn't exist
        return False
    else:
        return True
    
def setCalibrationPattern(chipAddr):
    for x in scratch_data:
        reg_data = {'request':'0','i2cAddr':chipAddr,'regAddr':x[0],'data':x[1]}
        sendRequestData(polaris_url,reg_data)
    
def checkWaferID(chipAddr):
    """
    This whole sequence of Wafer ID check is copied directly from Avinit's implementation
    in our coreBuild. It will return True if any of the Large or small batch ID matches.
    """
    raw_id_dict = {'0x1F':0x00,'0x20':0x00,'0x21':0x00,'0x22':0x00,'0x23':0x00,'0x24':0x00,'0x25':0x00,'0x26':0x00}
    raw_id_shift = {'0x1F': 0,'0x20':8,'0x21':16,'0x22':24,'0x23':32,'0x24': 40,'0x25': 48,'0x26': 56}
    raw_id_mask = {'0x1F':0x00000000000000FF,'0x20':0x000000000000FF00,'0x21':0x0000000000FF0000,'0x22':0x00000000FF000000,'0x23':0x000000FF00000000,'0x24':0x0000FF0000000000,'0x25':0x00FF000000000000,'0x26':0xFF00000000000000}
    raw_pol_id = 0
    for x in wafer_check:
        
        if x[1] == 'READ':
            #dictionary keys match the register addresses for the data0 to data7 registers
            raw_id_dict[x[0]] = getPolarisReadFromXML(x[0],chipAddr)
        else:
            reg_data = {'request':'0','i2cAddr':chipAddr,'regAddr':x[0],'data':x[1]}
            sendRequestData(polaris_url,reg_data)
    
    #create the raw ID
    #utilizing dictionaries we are shifting and masking according to the data registers 
    for key,value in raw_id_dict.items():      
        
        raw_pol_id |= (value << raw_id_shift[key]) & raw_id_mask[key]
    
    pol_id = raw_pol_id & POLARIS_RAW_ID_PRIMARY_MASK
    
    if pol_id == POLARIS_LARGE_BATCH02_COMPARE or pol_id == POLARIS_LARGE_BATCH03_COMPARE:
        return True
    elif pol_id == POLARIS_SMALL_BATCH01_PRIMARY_COMPARE:
        
        pol_id = raw_pol_id & POLARIS_RAW_ID_SECONDARY_MASK
        
        if pol_id == POLARIS_SMALL_BATCH01_SECONDARY_COMPARE:
            return True
        else:
            return False
    else:
        return False

if __name__ == "__main__":
    
    """
    Starting point of execution. We first check for any arguments that were passed with 
    the python command and perform the full calibration sequence after
    """
    passedCheck = False
    
    parser = argparse.ArgumentParser(description='Extra arguments for the Polaris Recal script')
    parser.add_argument('-i','--ignoreCheck',help='ignore the checks for temperature and serial ID and program polaris',action='store_true')
    parser.add_argument('-IP','--IPAddress',type=str,default='localhost:8080',help='Change the IP address from the default localhost:8080')
    parser.add_argument('-PM','--polarisMux',help='Adds option to set polaris multiplexer for systems that have Dual antenna',action='store_true')
    parsed_args = parser.parse_args()

    IP_Addr = parsed_args.IPAddress
    polaris_url = 'http://'+IP_Addr+'/cgi-bin/polarisTest.xml?'
    
    if parsed_args.polarisMux:
        polarisMux = 1
    else:
        polarisMux = 0
        
    for pol_chip in polaris_addr:
        polaris_switch = 0
        passedCheck = False
        
        #check the ignore argument to see if any apply before doing the checks
        if parsed_args.ignoreCheck:
            passedCheck = True
        else:
            if checkTempValue():
                if checkWaferID(pol_chip) and checkCalibrationPattern(pol_chip):
                    passedCheck = True
                    print('Polaris Chip Addr {} passed temp,wafer and calibration pattern check'.format(pol_chip))
                else:
                    passedCheck = False
            else:
                passedCheck = False
        
        #If we passed all our checks run the whole calibration sequence , otherwise continue onto the next chip
        if passedCheck:
            if polarisMux:
                for y in range(2):
                    writePolarisMux(polarisMux,polaris_switch)

                    for x in form_data:
                        reg_data = {'request':'0','i2cAddr':pol_chip,'regAddr':x[0],'data':x[1]}
                        sendRequestData(polaris_url,reg_data)
                    #set the calibration A5 pattern
                    setCalibrationPattern(pol_chip)
                    #XOR to switch from 0->1 and 1->0 for antenna switch
                    polaris_switch ^= 1
            else:
                for x in form_data:
                    reg_data = {'request':'0','i2cAddr':pol_chip,'regAddr':x[0],'data':x[1]}
                    sendRequestData(polaris_url,reg_data)
                #set the calibration A5 pattern
                setCalibrationPattern(pol_chip)
        else:
            print('Polaris Chip Addr {} did not pass checks'.format(pol_chip))
    print('done with calibration sequence')