from typing import Tuple,List
from DevicePackages.defaultConfigForDevices import LOGGER
from DevicePackages.ControlBoardPackage.ControlBoardConnectionModule import ConnectionClass
from DevicePackages.TestWidgedDataClass import TestWidgedDataClass


class SingleRegisterClass():
    def __init__(self,initial_value:int = 0, number_of_bits:int = 40, endianity:str = "MSB_FIRST", register_name:str = "",
                 ):
        self._register_value = initial_value
        self._number_of_bits = number_of_bits
        self._endianity = endianity #CAN BE MSB_FIRST or LSB_FIRST
        self.register_name = register_name


    def getPinStateFromRegisterValue(self,pin_number: int) -> bool:
        """This function returns the state of the pin from the register value. pins are numbered from 1 to 40
        INPUTS: pin_number:int
        OUTPUTS: pin_state:bool"""
        pin_number = pin_number - 1 #pin number is 0 based and in app 1 based ...
        if self._endianity == "LSB_FIRST":
            return bool(self._register_value & (1 << pin_number))

        elif self._endianity == "MSB_FIRST":
            return bool(self._register_value & (1 << (self._number_of_bits - pin_number - 1)))
        
    def setRegisterLength(self,length:int):
        self._number_of_bits = length

    def setRegisterEndianity(self,endianity:str):
        self._endianity = endianity
    
    def setRegisterName(self,register_name:str):
        self.register_name = register_name

    def getNewRegisterValueFromPinState(self,pin_number: int, value:bool) -> int:
        """This function updates the register accordingly to selected pin and value.
        This function keep other pins unchanged.
        INPUTS: pin_number:int, value:bool
        OUTPUTS: register_value:int"""

        pin_number = pin_number - 1 #pin number is 0 based and in app 1 based ...
        temp_register_value = 0
        if self._endianity == "LSB_FIRST":
            if value == True:
                temp_register_value = self._register_value | (1 << pin_number)

            elif value == False:
                temp_register_value = self._register_value & ~(1 << pin_number)

        elif self._endianity == "MSB_FIRST":
            if value == True:
                temp_register_value = self._register_value | (1 << (self._number_of_bits - pin_number - 1))

            elif value == False:
                temp_register_value = self._register_value & ~(1 << (self._number_of_bits - pin_number - 1))

        return temp_register_value
                   
    def setRegisterFromValue(self,value:int) -> int:
        """This function updates the register with the value.
        INPUTS: value:int
        OUTPUTS: register_value:int"""
        self._register_value = value
        return self._register_value
           
class Registers():
    """
    At the board there are 2 types of registers, one for setting outputs and one for setting impedances.
    There are 2 sets of 5x8 bit registers for each type. But first 40 pins are MSB and second 40 pins are LSB.
    """
    IO1TO40_OUTPUTS = SingleRegisterClass(initial_value= 0x0 ,endianity="MSB_FIRST",number_of_bits=40,register_name="IO1TO40_OUTPUTS")
    IO41TO80_OUTPUTS = SingleRegisterClass(initial_value= 0x0 ,endianity="LSB_FIRST",number_of_bits=40, register_name="IO41TO80_OUTPUTS")
    IO1TO40_IMPEDANCES = SingleRegisterClass(initial_value= 0x0 ,endianity="MSB_FIRST",number_of_bits=40, register_name="IO1TO40_IMPEDANCES")
    IO41TO80_IMPEDANCES = SingleRegisterClass(initial_value= 0x0 ,endianity="LSB_FIRST",number_of_bits=40, register_name="IO41TO80_IMPEDANCES") 

    def _getRegisterFromName(self,register_name:str) -> SingleRegisterClass:
        """This function returns the register object by its name.
        INPUTS: register_name:str
        OUTPUTS: register_object:CardRegistersClass"""
        match register_name:
            case "IO1TO40_OUTPUTS":
                return self.IO1TO40_OUTPUTS
            case "IO41TO80_OUTPUTS":
                return self.IO41TO80_OUTPUTS
            case "IO1TO40_IMPEDANCES":
                return self.IO1TO40_IMPEDANCES
            case "IO41TO80_IMPEDANCES":
                return self.IO41TO80_IMPEDANCES
            case _: #in case of wrong command
                return False
            

    def parseConfigurationMessage(self,message:str) -> bool:
        """This function parses the configuration message and sets the registers accordingly.
        MESSAGE SHOULD LOOK LIKE THIS:
        "OK;IO1TO40_OUTPUTS:(length:40,endianity:1,value:0);IO41TO80_OUTPUTS:(length:40,endianity:0,value:0);IO1TO40_IMPEDANCES:(length:40,endianity:1,value:0);IO41TO80_IMPEDANCES:(length:40,endianity:0,value:0);"
        """

        message = message.split(";")[1:-1]
        for register_data in message:
            register_name = register_data.split(":")[0]
            register_to_update = self._getRegisterFromName(register_name)
            register_parameters = register_data.split("(")[1].replace(")","").split(",")

            for string_parameters in register_parameters:
                parameter = string_parameters.split(":")[0]
                data = string_parameters.split(":")[1]

                if parameter == "length":
                    register_to_update.setRegisterLength(int(data))
                elif parameter == "endianity":
                    register_to_update.setRegisterEndianity("MSB_FIRST" if int(data) == 1 else "LSB_FIRST")
                elif parameter == "value":
                    register_to_update.setRegisterFromValue(int(data))
        return True

class SingleMultiplexerClass():
    def __init__(self,number_of_bits:int = 40, endianity:str = "MSB_FIRST", multiplexer_name:str = "", initial_value:int = 0,
                 ):
        self._number_of_bits = number_of_bits
        self._endianity = endianity #CAN BE MSB_FIRST or LSB_FIRST
        self.multiplexer_name = multiplexer_name
        self._multiplexer_value = initial_value

    def getNewMultiplexerValueFromPinState(self,pin_number: int, value:bool) -> int:
        """This function updates the register accordingly to selected pin and value.
        This function keep other pins unchanged.
        INPUTS: pin_number:int, value:bool
        OUTPUTS: register_value:int"""

        pin_number = pin_number - 1 #pin number is 0 based and in app 1 based ...
        temp_multiplexer_value = 0
        if self._endianity == "LSB_FIRST":
            if value == True:
                temp_multiplexer_value = self._multiplexer_value | (1 << pin_number)

            elif value == False:
                temp_multiplexer_value = self._multiplexer_value & ~(1 << pin_number)

        elif self._endianity == "MSB_FIRST":
            if value == True:
                temp_multiplexer_value = self._multiplexer_value | (1 << (self._number_of_bits - pin_number - 1))

            elif value == False:
                temp_multiplexer_value = self._multiplexer_value & ~(1 << (self._number_of_bits - pin_number - 1))

        else:
            print("Wrong endianity")

        return temp_multiplexer_value

    def getPinStateFromMultiplexerValue(self,pin_number: int) -> bool:
        """This function returns the state of the pin from the register value.
        INPUTS: pin_number:int
        OUTPUTS: pin_state:bool"""
        pin_number = pin_number - 1 #pin number is 0 based and in app 1 based ...
        if self._endianity == "LSB_FIRST":
            return bool(self._multiplexer_value & (1 << pin_number))
        
        elif self._endianity == "MSB_FIRST":
            return bool(self._multiplexer_value & (1 << (self._number_of_bits - pin_number - 1)))
        
    def setMultiplexerLength(self,length:int):
        self._number_of_bits = length

    def setMultiplexerEndianity(self,endianity:str):
        self._endianity = endianity

    def setMultiplexerName(self,multiplexer_name:str):
        self.multiplexer_name = multiplexer_name

    def setMultiplexerValue(self,value: int):
        self._multiplexer_value = value
        
class Multiplexers():
    MUXES_1TO40 = SingleMultiplexerClass(initial_value= 0x0 ,endianity="MSB_FIRST",number_of_bits=40,multiplexer_name="MUXES_1TO40")
    MUXES_41TO80 = SingleMultiplexerClass(initial_value= 0x0 ,endianity="LSB_FIRST",number_of_bits=40,multiplexer_name="MUXES_41TO80")

    def _getMultiplexerFromName(self,multiplexer_name:str) -> SingleMultiplexerClass:
        """This function returns the register object by its name.
        INPUTS: register_name:str
        OUTPUTS: register_object:CardRegistersClass"""
        match multiplexer_name:
            case "MUXES_1TO40":
                return self.MUXES_1TO40
            case "MUXES_41TO80":
                return self.MUXES_41TO80
            case _: #in case of wrong command
                return False
            

    def parseConfigurationMessage(self,message:str) -> bool:
        """This function parses the configuration message and sets the registers accordingly.
        MESSAGE SHOULD LOOK LIKE THIS:
        "OK;MUXES_1TO40:(bits:8,paralel:5,endianity:1,value:0);MUXES_41TO80:(bits:8,paralel:5,endianity:0,value:0);"
        """

        message = message.split(";")[1:-1]
        for multiplexer_data in message:
            register_name = multiplexer_data.split(":")[0]
            multiplexer_to_update = self._getMultiplexerFromName(register_name)
            multiplexer_parameters = multiplexer_data.split("(")[1].replace(")","").split(",")

            for string_parameters in multiplexer_parameters:
                parameter = string_parameters.split(":")[0]
                data = string_parameters.split(":")[1]

                if parameter == "bits":
                    number_of_bits = int(data)
                elif parameter == "endianity":
                    multiplexer_to_update.setMultiplexerEndianity("MSB_FIRST" if int(data) == 1 else "LSB_FIRST")
                elif parameter == "paralel":
                    number_of_paralel_multiplexers = int(data)
                elif parameter == "value":
                    multiplexer_to_update.setMultiplexerValue(int(data))

            multiplexer_to_update.setMultiplexerLength(number_of_bits * number_of_paralel_multiplexers)
        return True

class CardSettings():
    """THIS CLASS IS USED TO STORE TE SETTINGS OF THE CARD
    but only stores data. It does not send any commands to real hardware"""
    DAC_RAMP_DIRECTION = "UP"
    DAC_RAMP_STEP = 1
    DAC_RAMP_DELAY = 0.1
    DAC_RAMP_MIN_VALUE = 0
    DAC_RAMP_MAX_VALUE = 4095
    VOLTAGE_REFERENCE = 3.3
    VOLTAGE_READ_AVERAGES = 0
    DAC_VALUE = 0
    ADC_VALUE = 0

    def parseConfigurationMessage(self,message:str) -> bool:
        message = message.split(";")[1:-1]
        for parameters in message:
            parameter = parameters.split(":")[0]
            data = parameters.split(":")[1]
            match parameter:
                case "DAC_RAMP_DIRECTION":
                    self.setRampDirection(data)
                case "DAC_RAMP_STEP":
                    self.setRampStep(int(data))
                case "DAC_RAMP_DELAY_2500_NS":
                    self.setRampDelay(int(data))
                case "DAC_RAMP_MIN_VALUE":
                    self.setRampMinValue(int(data))
                case "DAC_RAMP_MAX_VALUE":
                    self.setRampMaxValue(int(data))
                case "VOLTAGE_REFERENCE":
                    self.setVoltageReference(float(data))
                case "VOLTAGE_READ_AVERAGES":
                    self.setVoltageReadAverages(int(data))
                case "DAC_VALUE":
                    self.setDACValue(int(data))
                case "ADC_VALUE":
                    self.setADCValue(int(data))


    def setRampDirection(self,direction:str):
        self.DAC_RAMP_DIRECTION = direction
    
    def setRampStep(self,step:int):
        self.DAC_RAMP_STEP = step

    def setRampDelay(self,delay:float):
        self.DAC_RAMP_DELAY = delay
    
    def setRampMinValue(self,min_value:int):
        self.DAC_RAMP_MIN_VALUE = min_value
    
    def setRampMaxValue(self,max_value:int):
        self.DAC_RAMP_MAX_VALUE = max_value
    
    def setVoltageReference(self,voltage_reference:float):
        self.VOLTAGE_REFERENCE = voltage_reference

    def setVoltageReadAverages(self,voltage_read_averages:int):
        self.VOLTAGE_READ_AVERAGES = voltage_read_averages

    def setDACValue(self,dac_value:int):
        self.DAC_VALUE = dac_value
    
    def setADCValue(self,adc_value:int):
        self.ADC_VALUE = adc_value

class PinState():
    IMPEDANCE = "LOW" #HIGH/LOW
    OUTPUT = "LOW" #HIGH/LOW
    INPUT_FROM_COMPARATOR = "LOW" #HIGH/LOW
    VOLTAGE = 0.0

    def fetchImpedance(self,impedance:str):
        self.IMPEDANCE = impedance
    
    def fetchOutput(self,output:str):
        self.OUTPUT = output
    
    def fetchInputFromComparator(self,input_from_comparator:str):
        self.INPUT_FROM_COMPARATOR = input_from_comparator

    def fetchVoltage(self,voltage:float):
        self.VOLTAGE = voltage

    def fetchAll(self,impedance:str,output:str,input_from_comparator:str,voltage:float = -2): 
        if type(impedance) == bool:
            impedance = "HIGH" if impedance else "LOW"

        if type(output) == bool:
            output = "HIGH" if output else "LOW"
        
        if type(input_from_comparator) == bool:
            input_from_comparator = "HIGH" if input_from_comparator else "LOW"
            
        self.fetchImpedance(impedance)
        self.fetchOutput(output)
        self.fetchInputFromComparator(input_from_comparator)
        self.fetchVoltage(voltage)

class IO80pinCardTestClass():
    """ Tests for 80 pin IO card.
    """
    ################ INIT Function ##################
    def __init__(self,log:LOGGER("ALL"),ConnectionClass :ConnectionClass):
        self.log = log
        self.REGISTERS = Registers()
        self.MULTIPLEXERS = Multiplexers()
        self.CARDSETTINGS = CardSettings()
        self.ConnectionClass = ConnectionClass
        self._PASS_STRING = "PASS"
        self._FAIL_STRING = "FAIL"
        
    def doTest(self, test_command:List[str], widget_data:TestWidgedDataClass = TestWidgedDataClass()) -> Tuple[bool,str,List[float]|str]:
        """This function parses the command and calls the appropriate function to do the test.
        INPUTS: test_command:list or str -> EX: ["SET","PIN","11","OUTPUT","HIGH"] or "SET PIN OUTPUT HIGH"]
        OUTPUTS: status:bool, response:str"""

        match test_command[0].upper():
            case "SET":
                print(test_command[1:])
                status,response = self._setCommands(test_command[1:])
                return status,response
            case "READ":
                status,response = self._readCommands(test_command[1:])
                return status,response
            case "MEAS" | "MEASURE":
                status,response = self._measureCommands(test_command[1:])
                return status,response
            
            case _: #in case of wrong command
                return False,"Wrong command"
    
    def getDataFromNonReadResponse(self,status,response) -> str:
        if status == True and str(response).startswith("OK;"):
            return self._PASS_STRING
        else:
            return self._FAIL_STRING

    def _setCommands(self,commands:List[str]) -> Tuple[bool,str]:
        match commands[0].upper():
            case "PIN": # "PIN","25","OUTPUT|IMPEDANCE","HIGH"
                pin_number = int(commands[1])
                if pin_number > 80:
                    return False,"Pin number must be between 1 and 80"
                pin_level = commands[3].upper().replace("HIGH","1").replace("LOW","0").replace("ON","1").replace("OFF","0")
                pin_level = int(pin_level)
                status,response = self._setPin(pin_number,commands[2].upper(),pin_level) #25 OUTPUT 1 | 25 IMPEDANCE 1
                return status,response
            
            case "REGISTER": #REGISTER IO1TO40_OUTPUTS VALUE 0xf|15
                selected_register = self.REGISTERS._getRegisterFromName(register_name = commands[1].upper())
                if selected_register == False:
                    return False,"Wrong register name"
                
                if commands[2].upper() == "VALUE":
                    if "0X" in commands[3].upper():
                        register_value = int(commands[3],16)
                    else:
                        register_value = int(commands[3])
                
                    to_send_string = f"80_IO_CARD SET SHIFT_REGISTER {selected_register.register_name} VALUE {register_value}"
                    status,response = self.ConnectionClass.queryMessage(to_send_string)
                    if status == True:
                        selected_register.setRegisterFromValue(register_value)
                    return status,response
                
            case "DAC":
                status,response = self.ConnectionClass.queryMessage(f"80_IO_CARD SET DAC {commands[1].replace('V','')}")
                if status == True:
                    self.CARDSETTINGS.setDACValue(response.split(":")[1])
                return status,response
            
            case _: #in case of wrong command
                return False,"Wrong command"
                    
            #case "DAC":
            #    status,response = self._setLedCommands(commands[1:])
            #    return status,response

    def _readCommands(self,commands:List[str]) -> Tuple[bool,str,int|str]:
        match commands[0].upper():
            case "MUX" | "MULTIPLEXER": # "MUX","MUXES_1TO40 ALL|PINNUMBER"
                multiplexer = self.MULTIPLEXERS._getMultiplexerFromName(multiplexer_name = commands[1].upper())
                if multiplexer == False:
                    return False,"Wrong multiplexer name"
                
                if commands[2].upper() == "ALL":
                    status, response = self.ConnectionClass.queryMessage(f"80_IO_CARD READ MUX {multiplexer.multiplexer_name} ALL")
                    if status == True:
                        multiplexer_value = int(response.split(":")[-1])
                        multiplexer.setMultiplexerValue(multiplexer_value)
                    return status,response
                
                else:
                    pin_number = int(commands[2])

                if pin_number > 80:
                    return False,"Pin number must be between 1 and 80"
                
                if pin_number > 40:
                    pin_number = pin_number - 40
            
                status, response = self.ConnectionClass.queryMessage(f"80_IO_CARD READ MUX {multiplexer.multiplexer_name} {pin_number - 1}")
                if status == True:
                    multiplexer_value = int(response.split(":")[-1])
                    multiplexer.getNewMultiplexerValueFromPinState(pin_number = pin_number, value = multiplexer_value)
                return status,response
            
            case "PIN_INPUT": # "READ PIN_INPUT 25"
                pin_number = int(commands[1])
                if pin_number < 40:
                    status,response = self._readCommands(["MUX",self.MULTIPLEXERS.MUXES_1TO40.multiplexer_name,str(pin_number)])
                    return status,response
                elif pin_number < 80:
                    status,response = self._readCommands(["MUX",self.MULTIPLEXERS.MUXES_41TO80.multiplexer_name,str(pin_number)])
                    return status,response
                else:
                    return False,"Pin number must be between 1 and 80"
                
            case "PIN_IMPEDANCE": # "READ PIN_IMPEDANCE 25"
                pin_number = int(commands[1])
                if pin_number < 40:
                    status,response = self._readCommands(["REGISTER",self.REGISTERS.IO1TO40_IMPEDANCES.register_name,str(pin_number)])
                    return status,response
                elif pin_number < 80:
                    status,response = self._readCommands(["REGISTER",self.REGISTERS.IO41TO80_IMPEDANCES.register_name,str(pin_number)])
                    return status,response
                else:
                    return False,"Pin number must be between 1 and 80"
                
            case "PIN_OUTPUT": # "READ OUTPUT 25"
                pin_number = int(commands[1])
                if pin_number < 40:
                    status,response = self._readCommands(["REGISTER",self.REGISTERS.IO1TO40_OUTPUTS.register_name,str(pin_number)])
                    return status,response
                elif pin_number < 80:
                    status,response = self._readCommands(["REGISTER",self.REGISTERS.IO41TO80_OUTPUTS.register_name,str(pin_number)])
                    return status,response
                else:
                    return False,"Pin number must be between 1 and 80" 
                
            case "REGISTER" | "SHIFT_REGISTER": #REGISTER IO1TO40_OUTPUTS
                selected_register = self.REGISTERS._getRegisterFromName(register_name = commands[1].upper())
                if selected_register == False:
                    return False,"Wrong register name"
                
                to_send_string = f"80_IO_CARD READ SHIFT_REGISTER {selected_register.register_name}"
                status,response = self.ConnectionClass.queryMessage(to_send_string)
                if status == True and str(response).startswith("OK;"):
                    register_value = int(response.split(":")[-1])
                    selected_register.setRegisterFromValue(register_value)
                    return status,response
                else:
                    return status,response
            
            case "HARDWARE_SETTINGS":
                status,response = self.ConnectionClass.queryMessage(f"80_IO_CARD READ CONFIGURATION SHIFT_REGISTERS")
                if status == True and str(response).startswith("OK;"):
                    if self.REGISTERS.parseConfigurationMessage(response) == 0:
                        return False,f"Could not parse shift registers configuration message: {response}"
                else:#FAULT
                    return status,response


                status,response = self.ConnectionClass.queryMessage(f"80_IO_CARD READ CONFIGURATION MULTIPLEXERS")
                if status == True and str(response).startswith("OK;"):
                    if self.MULTIPLEXERS.parseConfigurationMessage(response) == 0:
                        return False,f"Could not parse multiplexers configuration message: {response}"
                else:#FAULT
                    return status,response

                status,response = self.ConnectionClass.queryMessage(f"80_IO_CARD READ CONFIGURATION VOLTAGE_READINGS")
                if status == True and str(response).startswith("OK;"):
                    if self.CARDSETTINGS.parseConfigurationMessage(response) == 0:
                        return False,f"Could not parse Card configuration message: {response}"
                else:#FAULT
                    return status,response
            
                return True, f"Configuration loaded succesfully"

            case _:
                return False,"Wrong command"

    def _measureCommands(self,commands:List[str]) -> Tuple[bool,str,List[float]|int|str]:
        match commands[0].upper():
            case "ADC":
                status,response = self.ConnectionClass.queryMessage(f"80_IO_CARD MEASURE ADC")
                if status == True and str(response).startswith("OK;"):
                    ADC_value = int(response.split(":")[-1])
                    return status,response
                else:
                    return status,response
            
            case "VOLTAGE":
                if commands[1].upper() == "ALL":
                    status,response = self.ConnectionClass.queryMessage(f"80_IO_CARD MEASURE VOLTAGE ALL")
                    if status == True and str(response).startswith("OK;"):
                        list_of_voltages = response.split(";")[1:-1]         
                        return status,response
                    return status,response
                
                else:
                    pin_number = int(commands[1])
                    status,response = self.ConnectionClass.queryMessage(f"80_IO_CARD MEASURE VOLTAGE {pin_number-1}")
                    if status == True and str(response).startswith("OK;"):
                        voltage = float(response.split(":")[-1])
                        return status,response
                    return status,response
            
            case _:
                return False,"Wrong command"
    
    def _setPin(self,pin_number:int,pin_type:str,pin_level:int) -> Tuple[bool,str,int]:
        pin_level = bool(pin_level)
        if pin_type == "OUTPUT":
            if pin_number <= 40:
                selected_register = self.REGISTERS.IO1TO40_OUTPUTS
            else:
                selected_register = self.REGISTERS.IO41TO80_OUTPUTS
                pin_number = pin_number - 40
            
        elif pin_type == "IMPEDANCE":
            if pin_number <= 40:
                selected_register = self.REGISTERS.IO1TO40_IMPEDANCES
            else:
                selected_register = self.REGISTERS.IO41TO80_IMPEDANCES
                pin_number = pin_number - 40
        
        #register_value 1 sets output to 0V and 0 sets output to 3.3V, and also
        #register_value 1 sets impedance to LOW and 0 sets impedance to HIGH
        #this may leads to to confusion, so logic is reversed here
        register_value = selected_register.getNewRegisterValueFromPinState(pin_number,not pin_level)
        to_send_string = f"80_IO_CARD SET SHIFT_REGISTER {selected_register.register_name} VALUE {register_value}"
        status,response = self.ConnectionClass.queryMessage(to_send_string)
        if status == True and str(response).startswith("OK;"):
            selected_register.setRegisterFromValue(register_value)
            return status,response
        
        return status,response
    
class AllPins(IO80pinCardTestClass):
    """This class stores the state of all IOs"""
    list_of_All_pins = [PinState() for _ in range(80)]
    def updateSingleIoState(self,pin:int):
        """This function updates the state of a single IO"""
        
        if pin < 40:
            ask_for_pin_value = pin
            impedance_register = self.REGISTERS.IO1TO40_IMPEDANCES
            output_register = self.REGISTERS.IO1TO40_OUTPUTS
            mux = self.MULTIPLEXERS.MUXES_1TO40
        
        elif pin < 80:
            ask_for_pin_value= pin - 40
            impedance_register = self.REGISTERS.IO41TO80_IMPEDANCES
            output_register = self.REGISTERS.IO41TO80_OUTPUTS
            mux = self.MULTIPLEXERS.MUXES_41TO80
        
        else:
            return False, "Pin number is out of range"

        impedance = impedance_register.getPinStateFromRegisterValue(ask_for_pin_value+1)#pins are numbered from 1 to 80 in that function...
        output = output_register.getPinStateFromRegisterValue(ask_for_pin_value+1)
        input_from_comparator = mux.getPinStateFromMultiplexerValue(ask_for_pin_value+1)
        self.list_of_All_pins[pin].fetchAll(impedance,output,input_from_comparator)
        return True,"Pin updated succesfully"
    
    def askForAllVoltages(self):
        """This function updates the voltage of all IOs"""
        status,response = self._measureCommands(["VOLTAGE","ALL"])
        if status == False:
            return status,response

        #response should look like this: OK;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;
        #-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;
        # -1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;
        # -1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;
        # -1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;
        # -1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;-1.000000;
        # -1.000000;-1.000000;-1.000000;-1.000000;-1.000000;

        list_of_voltages = response.split(";")[1:-1]
        for pin in range(0,80):
            self.list_of_All_pins[pin].fetchVoltage(float(list_of_voltages[pin]))            
        return True,"Voltages updated succesfully"
            
    def askForPinStatesFromHardware(self):
        """This function updates the state of all IOs"""
        self._readCommands(["HARDWARE_SETTINGS"])
        for pin in range(0,80):
            status,message = self.updateSingleIoState(pin)
            if status == False:
                return status,message
            
        return True,"All pins updated succesfully"