"""
Version 0
This program have the first steps to make one to control a MOT
"""

from labjack import ljm
import time
import sys
from datetime import datetime
import numpy as np


MAX_REQUESTS = 4000 # The number of eStreamRead calls that will be performed.

# Open first found LabJack
handle = ljm.openS("ANY", "ANY", "ANY")
#handle = ljm.open(ljm.constants.dtANY, ljm.constants.ctANY, "ANY")


info = ljm.getHandleInfo(handle)
print("Opened a LabJack with Device type: %i, Connection type: %i,\n" \
    "Serial number: %i, IP address: %s, Port: %i,\nMax bytes per MB: %i" % \
    (info[0], info[1], info[2], ljm.numberToIP(info[3]), info[4], info[5]))


# ### FIO LOW ### 
#set all FIO digital channels to low state (prevent malfunction of digital channels)
statelow=0
for i in range(0,8):
    num=str(i)
    chF="FIO"+num
    ljm.eWriteName(handle, chF, statelow)
# ### FIO LOW ###

# Setup Stream Out
OUT_NAMES = ["FIO_STATE", "DAC1", "DAC0"]
NUM_OUT_CHANNELS = len(OUT_NAMES)
outAddress = ljm.nameToAddress(OUT_NAMES[0])[0]

# FIO ###
# Allocate memory for the stream-out buffer
ljm.eWriteName(handle, "STREAM_OUT0_TARGET", outAddress)
ljm.eWriteName(handle, "STREAM_OUT0_BUFFER_SIZE", 512)
ljm.eWriteName(handle, "STREAM_OUT0_ENABLE", 1)

matriz = np.array([[1,1,1,1],[0,0,0,0],[1,1,1,1], [0,0,0,0],[1,1,1,1],[0,0,0,0]])
rowsdim=matriz.shape[0]
rows=[]
for i in range(0,rowsdim):
    rows.append(''.join(map(str,matriz[i,])))  #each row contain the state of all FIO at a given time

# Write values to the stream-out buffer
ljm.eWriteName(handle, "STREAM_OUT0_LOOP_SIZE", rowsdim)
for r in range(0,rowsdim):
    ljm.eWriteName(handle, "STREAM_OUT0_BUFFER_U16", int(rows[r],2)) 

ljm.eWriteName(handle, "STREAM_OUT0_SET_LOOP", 1)

print("STREAM_OUT0_BUFFER_STATUS = %f" % (ljm.eReadName(handle, "STREAM_OUT0_BUFFER_STATUS")))
# FIO ###    

# DAC1 ###    
outAddress = ljm.nameToAddress(OUT_NAMES[1])[0]
# Allocate memory for the stream-out buffer
ljm.eWriteName(handle, "STREAM_OUT1_TARGET", outAddress)
ljm.eWriteName(handle, "STREAM_OUT1_BUFFER_SIZE", 512)
ljm.eWriteName(handle, "STREAM_OUT1_ENABLE", 1)

vminD1=0
vmaxD1=5
stepsD1=3
volt_stepD1=(vmaxD1-vminD1)/(stepsD1-1)
volt_arrD1=np.linspace(vminD1,vmaxD1,stepsD1)

# Write values to the stream-out buffer 
NumBuf="1"
ljm.eWriteName(handle, "STREAM_OUT"+NumBuf+"_LOOP_SIZE", stepsD1)
for v1 in range(0,stepsD1):
    ljm.eWriteName(handle, "STREAM_OUT"+NumBuf+"_BUFFER_F32", volt_arrD1[v1]) 

ljm.eWriteName(handle, "STREAM_OUT2_SET_LOOP", 1)
print("STREAM_OUT1_BUFFER_STATUS = %f" % (ljm.eReadName(handle, "STREAM_OUT1_BUFFER_STATUS")))
# DAC1 ###

# DAC0 ###
outAddress = ljm.nameToAddress(OUT_NAMES[2])[0]

# Allocate memory for the stream-out buffer
ljm.eWriteName(handle, "STREAM_OUT2_TARGET", outAddress)
ljm.eWriteName(handle, "STREAM_OUT2_BUFFER_SIZE", 512)
ljm.eWriteName(handle, "STREAM_OUT2_ENABLE", 1)

vminD0=0
vmaxD0=5
stepsD0=3
volt_stepD0=(vmaxD0-vminD0)/(stepsD0-1)
volt_arrD0=np.linspace(vminD0,vmaxD0,stepsD0)

# Write values to the stream-out buffer 
NumBuf="2"
ljm.eWriteName(handle, "STREAM_OUT"+NumBuf+"_LOOP_SIZE", stepsD0)
for v1 in range(0,stepsD0):
    ljm.eWriteName(handle, "STREAM_OUT"+NumBuf+"_BUFFER_F32", volt_arrD0[v1]) 

ljm.eWriteName(handle, "STREAM_OUT2_SET_LOOP", 1)

print("STREAM_OUT2_BUFFER_STATUS = %f" % (ljm.eReadName(handle, "STREAM_OUT2_BUFFER_STATUS")))
# DAC0 ###


# Stream Configuration
POS_IN_NAMES = ["AIN0", "AIN1"]
NUM_IN_CHANNELS = len(POS_IN_NAMES)

TOTAL_NUM_CHANNELS = NUM_IN_CHANNELS + NUM_OUT_CHANNELS


#Add positive channels to scan list
aScanList = ljm.namesToAddresses(NUM_IN_CHANNELS, POS_IN_NAMES)[0]
scanRate = 5000
scansPerRead = 60

# Add the stream out token 4800 to the end
aScanList.extend([4800,4801,4802])

try:
    # Configure the analog inputs' negative channel, range, settling time and
    # resolution.
    # Note when streaming, negative channels and ranges can be configured for
    # individual analog inputs, but the stream has only one settling time and
    # resolution.
    aNames = ["AIN_ALL_NEGATIVE_CH", "AIN_ALL_RANGE", "STREAM_SETTLING_US",
              "STREAM_RESOLUTION_INDEX"]
    aValues = [ljm.constants.GND, 10.0, 0, 0] #single-ended, +/-10V, 0 (default),
                                              #0 (default)
    ljm.eWriteNames(handle, len(aNames), aNames, aValues)

    # Configure and start stream
    print(aScanList[0:TOTAL_NUM_CHANNELS])
    scanRate = ljm.eStreamStart(handle, scansPerRead, TOTAL_NUM_CHANNELS, aScanList, scanRate)
    print("\nStream started with a scan rate of %0.0f Hz." % scanRate)

    print("\nPerforming %i stream reads." % MAX_REQUESTS)
    start = datetime.now()
    totScans = 0
    totSkip = 0 # Total skipped samples

    i = 1
    while i <= MAX_REQUESTS:
        ret = ljm.eStreamRead(handle)

        # Note that the Python eStreamData will return a data list of size
        # scansPerRead*TOTAL_NUM_CHANNELS, but only the first
        # scansPerRead*NUM_IN_CHANNELS samples in the list are valid. Output
        # channels are not included in the eStreamRead's returned data.
        data = ret[0][0:scansPerRead*NUM_IN_CHANNELS]
        scans = len(data)/NUM_IN_CHANNELS
        totScans += scans
        
        # Count the skipped samples which are indicated by -9999 values. Missed
        # samples occur after a device's stream buffer overflows and are
        # reported after auto-recover mode ends.
        curSkip = data.count(-9999.0)
        totSkip += curSkip
        
        print("\neStreamRead #%i, %i scans" % (i, scans))
        for j in range(0, scansPerRead):
            ainStr = ""
            for k in range(0, NUM_IN_CHANNELS):
                ainStr += "%s: %0.5f, " % (POS_IN_NAMES[k], data[j*NUM_IN_CHANNELS + k])
            print("  %s" % (ainStr))
        print("  Scans Skipped = %0.0f, Scan Backlogs: Device = %i, LJM = " \
              "%i" % (curSkip/NUM_IN_CHANNELS, ret[1], ret[2]))
        i += 1

    end = datetime.now()

    print("\nTotal scans = %i" % (totScans))
    tt = (end-start).seconds + float((end-start).microseconds)/1000000
    print("Time taken = %f seconds" % (tt))
    print("LJM Scan Rate = %f scans/second" % (scanRate))
    print("Timed Scan Rate = %f scans/second" % (totScans/tt))
    print("Timed Sample Rate = %f samples/second" % (totScans*NUM_IN_CHANNELS/tt))
    print("Skipped scans = %0.0f" % (totSkip/NUM_IN_CHANNELS))
except ljm.LJMError:
    ljme = sys.exc_info()[1]
    print(ljme)
except Exception:
    e = sys.exc_info()[1]
    print(e)

print("\nStop Stream")
ljm.eStreamStop(handle)

# Close handle
ljm.close(handle)