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

# Copyright CERN, 2012 (Seven Solutions S.L.)
# Author: Richard Carrillo <rcarrillo(AT)sevensols.com>
# Licence: GPL v2 or later.
# Website: http://www.ohwr.org
# Website: http://www.sevensols.com
# Version: 1.1 (Last modifications: 5/5/2012)


from ptsexcept import *
from dio_fmc import *
from keyb import *

import rr
import os

"""
test02: Check fmc-dio-5chttl-board ports as output (port driver working, connector connectivity and LVDS to LVCMOS IC working)
Conditions: Mezzanine presence line asserted (test00 passed)
User intervention required: Yes*
Procedure details:
- Load firmware.
- Test mezzanine presence line.
- Check that ports work as output
  - Using the port interconnection cables: Check that all ports are interconnected. 
  - If all the communications fail: Using the test cable: Oscillate state of fmc-dio-5chttl-board ports
"""

class test05:
   def __init__(self, spec):
      """ check FMC PRESENT-pin state """
      try:
         self.dio = CFmcDio(spec, 0x80000, False, False, True)
      except CFmcDioError as e:
         err_msg="While fmc-dio-5chttla initialization: "+str(e)
         if e.bus_name == "FMC":
            raise PtsCritical(err_msg) # Critical error: the fmc-dio-5chttl-board is apparently not present
         else:
            print err_msg # Non-critical error during devices initialization
            print "Trying to continue..."
            self.dio = e.dio_obj # Recover the created CFmcDio object before the exception was raised so that we can continue

   def osc_ports(self):
      """ Oscillate state of fmc-dio-5chttl-board ports """
      for lemon in range(5): # Disable output of all ports and set them to 1
         self.dio.set_dir(lemon,0)
         self.dio.set_out(lemon,1)
      ans=""
      lemon=0
      while ans <> "Y" and ans <> "N" and ans <> "S":
         time.sleep(0.05)
         self.dio.set_dir((lemon-1)%5,0)
         self.dio.set_dir(lemon,1)
         lemon=(lemon+1)%5
         if kbhit():
            ans=raw_input().upper()
            if ans <> "Y" and ans <> "N" and ans <> "S":
               print "The valid inputs are only Y, N and S and Enter"
      for lemon in range(5): # Disable output of all ports
         self.dio.set_out(lemon, 0)
         self.dio.set_dir(lemon, 0)
      return ans

   def check_port_couple(self,inp,outp):
      """ check if a given port couple works as input (inp) and output (outp) """
      self.dio.set_out(outp,0) # Set port output value to 0
      self.dio.set_dir(outp,1) # Enable port output

      self.dio.set_in_threshold(inp,self.dio.DACvoltage2value(0.8))
      time.sleep(0.010) # wait for the DAC output to stabilize

      if not self.dio.get_in(inp): # Low value detected in the input port
         self.dio.set_out(outp,1) # Set port output value to 1

         self.dio.set_in_threshold(inp,self.dio.DACvoltage2value(1.9))
         time.sleep(0.010) # wait for the DAC output to stabilize

         if self.dio.get_in(inp): # High value detected in the output port
            ports_working=True
         else:
            ports_working=False
      else:
         ports_working=False

      self.dio.set_out(outp,0) # Set port output value to 0
      self.dio.set_dir(outp,0) # Disable port output
      return ports_working

   def look_for_working_out_in_port_couple(self):
      """ look for a port which works as output and another which works as input """
      in_out_port_couple=None
      try:
         for lemoin, lemoout in ((inp,outp) for inp in range(5) for outp in range(5)):
            if lemoout <> lemoin: # Check interconnectivity of different ports only
               if self.check_port_couple(lemoin,lemoout): # these ports are connected
                  in_out_port_couple=[lemoin,lemoout]
                  break
      except CFmcDioError as e:
         print "While using CDAC5578 to check interport communication: "+str(e)
         print "Trying to continue..."
      return in_out_port_couple

   def test_port_inter(self):
      """ Port (LEMO 00 connectors) interconnection check """
      for lemon in range(5):
         self.dio.set_term(lemon, 0) # Disable termination resistor
         self.dio.set_out(lemon,0) # Set port output value to 0
         self.dio.set_dir(lemon,0) # Disable port output
      tmp_stdout = sys.stdout
      sys.stdout = sys.__stdout__
      tmp_stdin = sys.stdin
      sys.stdin = sys.__stdin__
      raw_input("Ensure that the interconnection cables are plugged in the five LEMO-00 connectors of the fmc-dio-5chttla board and press Enter")

      in_out_port_couple=self.look_for_working_out_in_port_couple()
      if not in_out_port_couple:
         init_error_msg="No communication could be performed between any two ports"
         print init_error_msg
         user_ans=self.test_port_out()
         if user_ans == "Y" or user_ans == "N": # manual test was performed
            msg="The user reported {} operation of board-port outputs".format("a correct" if user_ans=="Y" else "an incorrect") #  manual test procedure info message to be printed
         else:
            msg="The user skipped the manual port-output measurement"
         if user_ans == "N":
            emsg="TSTERR01: Operation of fmc-dio-5chttla-board ports as output failed" # set the new error message caused by the manual procedure
         elif user_ans == "Y":
            emsg=None # No error in the end
         else:
            emsg="TSTERR05:"+init_error_msg # manual test procedure skipped, report initial error
      else:
         msg=None # No additional message to be printed
         unchecked_ports=list(range(5)) # create a list containing the ports to be checked
         # remove the already-checked ports in_out_port_couple
         lemoin=in_out_port_couple[0]
         lemoout=in_out_port_couple[1]
         unchecked_ports.remove(lemoin)
         unchecked_ports.remove(lemoout)
         faulty_ports=list()
         for cport in unchecked_ports:
            if not self.check_port_couple(lemoin,cport): # no communication in this way
               if not self.check_port_couple(cport,lemoout): # cport is not connected
                  faulty_ports.append(cport)
         #print in_out_port_couple
         #print unchecked_ports
         #print faulty_ports
         if faulty_ports: # Some ports are not correctly interconnected
            emsg="TSTERR06: Ports {} do not appear to be correctly connected".format(faulty_ports)
         else:
            emsg=None

      raw_input("Disconnect all the cables from the fmc-dio-5chttla-board ports (in order to perform further tests) and press Enter")
      sys.stdout = tmp_stdout
      sys.stdin = tmp_stdin
      if msg:
         print msg #  print manual procedure message in case it was performed
      if emsg:
         print emsg #  print result error message
      return emsg

   def test_port_out(self):
      """ Port (LEMO 00 connectors) oscillation check """
      print "Connect the testing LEDs (or other measuring instrument) to the LEMO 00 connectors of the fmc-dio-5chttla board (120ohm serial resistor included)"
      print "¿Are all the connected testing LEDs blinking alternately?"
      print "Press Y/N/S (yes/no/skip test) and Enter"
      for lemon in range(5): # Disable all termination resistors
         self.dio.set_term(lemon, 0)
      ans=self.osc_ports()
      return ans

def main(default_directory="."):
   # Configure the FPGA using the program fpga_loader
   path_fpga_loader = '../firmwares/fpga_loader'
   path_firmware = '../firmwares/spec_top.bin'
   firmware_loader = os.path.join(default_directory, path_fpga_loader)
   bitstream = os.path.join(default_directory, path_firmware)
   print "Loading firmware: %s" % (firmware_loader + ' ' + bitstream)
   os.system( firmware_loader + ' ' + bitstream )

   # Load board library and open the corresponding device
   print "Loading hardware access library and opening device\n"
   spec = rr.Gennum()

   print "Test Start05"

   print "Checking fmc-dio-5chttla board presence"
   test=test05(spec)

   print "\nChecking connectivity of board ports"
   emsg=test.test_port_inter()

   print "\nEnd of Test05"
   print "RESULT: [{}]".format("FAIL" if emsg else "OK")
   if emsg:
      raise PtsError(emsg)

if __name__ == "__main__":
    main(".")

