#! /usr/bin/python
"""
Author: Maciej Suminski <maciej.suminski@cern.ch>
Copyright: CERN 2017
License: GPLv3

Tests CONV-TTL-BLO pulse rejection module.
"""

import os
import sys
import time

sys.path.append("../PyBECO")
from PyBECO.boards import PyConvTTLBLO, PyConvTTLException
from PyBECO.fec import Elma, ElmaException

# crate credentials
sys.path.append("../ei2c")
import ei2cdefine as cfg

# settings
address = cfg.HNAME if cfg.ENABLED and cfg.HNAME else raw_input("Hostname: ")
user = cfg.USER if cfg.ENABLED and cfg.USER else raw_input("User: ")
password = cfg.PWD if cfg.ENABLED and cfg.PWD else raw_input("Password: ")
slot = cfg.SLOT if cfg.ENABLED and cfg.SLOT else int(input("Slot number: "))
cooldown_time = 8	# number seconds of cool down time between tests

elma = Elma(address, user, password)
convttl = PyConvTTLBLO(slot, elma)
convttl.reset()

# display status
print(convttl.status)

# verify gateware and hardware number
if convttl.status.hw_version[0] != 4:
    print("ERROR: Expected hardware version 4.x, but it is %d.%d" % convttl.status.hw_version)
    # sys.exit(1) # TODO uncomment

if convttl.status.gw_version[0] != 4:
    print("ERROR: Expected gateware version 4.x, but it is %d.%d" % convttl.status.gw_version)
    sys.exit(1)

# selectt the test data set depending on the pulse duration switch
if(convttl.status.switch[1]):
    # short pulse
    print("Running short pulse tests")

    tests = (
        {'freq': 2000000,   'pulses': 2e6},
        {'freq': 1333330,   'pulses': 8.65e6},
        {'freq': 1000000,   'pulses': 1e7},
        {'freq':  800000,   'pulses': 2.08e7},
        {'freq':  666670,   'pulses': 2.439e7},
        {'freq':  571000,   'pulses': 571000 * 120, 'continuous': True}
    )

    max_freq = 2e6
else:
    # long pulse
    print("Running long pulse tests")

    tests = (
        # {'freq':  104166, 'pulses': 104166},
        # {'freq':   92592, 'pulses': 277000},
        {'freq':   83333, 'pulses': 541000},
        {'freq':   75757, 'pulses':  75757 * 10},
        {'freq':   69444, 'pulses':  69444 * 20},
        {'freq':   55555, 'pulses':  55555 * 60},
        {'freq':   52083, 'pulses':  52083 * 120, 'continuous': True},
    )

    max_freq = 105e3

# run tests
for test in tests:
    old_counters = None
    freq = test['freq']
    pulses = int(test['pulses'])
    continuous = True if 'continuous' in test and test['continuous'] else False

#############################################################################################
# test if the guaranteed number of pulses is repeated

    # clear counters
    for ch in convttl.CHANNELS:
        convttl.pulse_counter_set(ch, (0, 0))

    # clear errors (including missed pulse flags)
    convttl.error.error_reg = convttl.error.ERRORS_MASK

    print("------")
    print("TEST: repeat %d pulses at %d Hz" % (pulses, freq))
    print("Start the pulse burst generator (%d pulses)" % int(pulses))
    print("	./pulsegen.py -c %d -f %F" % (pulses, freq))
    print("")
    print("Pulse counters:")

    test_run = True

    # test if all pulses pass through
    while(test_run):
        errors = convttl.error.error_reg
        counters = convttl.pulse_counters

        if(old_counters != counters):
            for ch in convttl.CHANNELS:
                print("CH%d" % ch),
                print("[TTL:%7d BLO:%7d]   " % counters[ch-1]),
            print("")
            old_counters = counters

        for ch in convttl.CHANNELS:
            # set of missed pulse error flags for this channel
            pulse_missed_errors = (convttl.error.ERRORS_MASK_FLIM_MISSED(ch),
                    convttl.error.ERRORS_MASK_FWDG_MISSED(ch))

            if counters[ch - 1][0] != 0 and counters[ch - 1][1] != 0:
                print("WARNING: Both pulse counters are increasing, check if it is ok")

            pulse_count = max(old_counters[ch - 1])

            # check if there were any pulses missed
            if any(error_bit & errors for error_bit in pulse_missed_errors):
                if(pulse_count <= pulses):
                    print("TEST FAILED: channel %d missed a pulse (%d)" % (ch, pulse_count))
                    sys.exit(2)
                test_run = False
                break

            # check if we have reached the number of guaranteed pulses
            if pulse_count >= pulses:
                print("TEST PASSED: repeated %d pulses at frequency %d Hz" % (pulse_count, freq))
                test_run = False
                break

    print("waiting %d seconds of cool-down period" % cooldown_time)
    time.sleep(cooldown_time)

#############################################################################################
# test if pulses are rejected after the counter threshold has been reached

    if continuous:  # we cannot perform this test for frequencies that
        continue    # are meant to repeat pulses without rejecting any

    # clear counters
    for ch in convttl.CHANNELS:
        convttl.pulse_counter_set(ch, (0, 0))

    # clear errors (including missed pulse flags)
    convttl.error.error_reg = convttl.error.ERRORS_MASK

    print("")
    print("------")
    print("")
    print("TEST: reject pulses after repeating %d pulses at %d Hz" % (pulses, freq))
    print("Start the pulse burst generator (%d pulses)" % (pulses * 2))
    print("	./pulsegen.py -c %d -f %F" % (pulses * 2, freq))
    print("")
    print("Pulse counters:")

    test_run = True
    passed = [False for _ in convttl.CHANNELS]  # passed test flags for each channel

    # test if pulses are rejected after a certain threshold
    while(test_run):
        errors = convttl.error.error_reg
        counters = convttl.pulse_counters

        if(old_counters != counters):
            for ch in convttl.CHANNELS:
                print("CH%d" % ch),
                print("[TTL:%7d BLO:%7d]   " % counters[ch-1]),
            print("")
            old_counters = counters

        for ch in convttl.CHANNELS:
            # set of missed pulse error flags for this channel
            pulse_missed_errors = (convttl.error.ERRORS_MASK_FLIM_MISSED(ch),
                    convttl.error.ERRORS_MASK_FWDG_MISSED(ch))

            if old_counters[ch - 1][0] != 0 and old_counters[ch - 1][1] != 0:
                print("WARNING: Both pulse counters are increasing, check if it is ok")

            pulse_count = max(old_counters[ch - 1])

            # check if there were any pulses missed
            if any(error_bit & errors for error_bit in pulse_missed_errors):
                if(pulse_count <= pulses):
                    print("TEST FAILED: channel %d missed a pulse too early (%d)" % (ch, pulse_count))
                    sys.exit(2)
                elif not passed[ch - 1]:
                    if convttl.error.ERRORS_MASK_FLIM_MISSED(ch) & errors:
                        print("TEST FAILED: pulse rejected on channel %d, but FLIM_MISSED bit is set" % (ch))
                        sys.exit(2)
                    else:
                        print("TEST PASSED: rejected %d pulse on channel %d" % (pulse_count, ch))
                        passed[ch - 1] = True

            # even if the test was passed, wait for the end of current pulse burst
            if(pulse_count >= pulses * 2):
                if not any(error & errors for error in pulse_missed_errors):
                    print("TEST FAILED: repeated %d pulses, none rejected" % (pulse_count))
                    sys.exit(2)
                test_run = False

    if any(passed):
        print("TEST PASSED")

    print("waiting %d seconds of cool-down period" % cooldown_time)
    time.sleep(cooldown_time)

###############################################################################
# test FLIM_MISSED bit (pulses with too high frequency)

pulses = 10000
freq = max_freq * 1.2

# clear counters
for ch in convttl.CHANNELS:
    convttl.pulse_counter_set(ch, (0, 0))

# clear errors (including missed pulse flags)
convttl.error.error_reg = convttl.error.ERRORS_MASK

print("")
print("------")
print("")
print("TEST: reject pulses at too high frequency (FLIM_MISSED bit)")
print("Start the pulse burst generator (%d pulses at %d Hz" % (pulses, freq))
print("	./pulsegen.py -c %d -f %F" % (pulses, freq))
print("")

test_run = True

while(test_run):
    counters = convttl.pulse_counters
    errors = convttl.error.error_reg

    if(old_counters != counters):
        print(counters)
        old_counters = counters

    for ch in convttl.CHANNELS:
        if old_counters[ch - 1][0] != 0 and old_counters[ch - 1][1] != 0:
            print("WARNING: Both pulse counters are increasing, check if it is ok")

        pulse_count = max(old_counters[ch - 1])

        # check if there were any pulses missed
        if convttl.error.ERRORS_MASK_FLIM_MISSED(ch) & errors:
            if convttl.error.ERRORS_MASK_FWDG_MISSED(ch) & errors:
                print("WARNING: FLIM_WDG bit is set for channel %d" % (ch))

            print("TEST PASSED: FLIM_MISSED flag set on channel %d" % (ch))
            test_run = False
            break

        if(pulse_count >= pulses):
            print("TEST FAILED: repeated %d pulses, FLIM_MISSED flag has not been set" % (pulse_count))
            sys.exit(2)

print("ALL TESTS PASSED!")
