B
    ª/^&  ã               @   s˜   d Z ddlZddlZddlZddlZddlZddlZdZdZ	dZ
dZG dd„ dƒZd	d
„ Zdd„ Zedkr”eƒ Ze ¡ Ze ejd ¡Zeedd dS )zÚ
.. module:: laser
    :platform: unix
    :synopsis: This module is for communicating with the laser.

.. codeauthor:: Nick Mondrik
Date: 01/2020

Control code for an Ekspla NT-252 tunable laser
Serial Number PGD 217
é    Né   iPÃ  iO  i(
  c               @   s’   e Zd ZdZd$dd„Zdd„ Zdd„ Zd	d
„ Zdd„ Zdd„ Z	dd„ Z
dd„ Zdd„ Zdd„ Zdd„ Zdd„ Zdd„ Zdd„ Zdd „ Zd!d"„ Zd#S )%ÚLaserSerialInterfacez›
    This class is for communicating with the NT252 laser through the RS232 serial interface.
    TODO: Add code to check error states in all modules

    ú/dev/ttyACM.LASERc             C   sD   d| _ dddddddœ| _tj|d	d
d| _d| _d| _|  ¡  d S )NzNot Connectedz/M_CPU800/18/PowerzD/M_CPU800/18/Continuous%20%2F%20Burst%20mode%20%2F%20Trigger%20burstz/M_CPU800/18/Burst lengthz/MidiOPG/31/WaveLengthz /M_CPU800/18/Output Energy levelz%/M_CPU800/18/External interlock state)Ústate_queryÚmode_selectÚnpulsesÚ
wavelengthÚenergy_levelÚext_interlock_statei K  é   )ÚportÚbaudrateÚtimeoutó   ú)ÚstateÚcommandsÚserialÚSerialÚ_read_terminationÚ_write_terminationÚcheck_state)Úselfr   © r   ú/home/cbp/cbp/cbp/nt252.pyÚ__init__    s    
zLaserSerialInterface.__init__c             C   s&   | j  | j¡}| d¡}| d¡}|S )zF
        Mid-level function to read from the laser registers.
        Úasciiz
)r   Ú
read_untilr   ÚdecodeÚstrip)r   Úresponser   r   r   Úread.   s    

zLaserSerialInterface.readc             C   s2   |d | j kr|| j 7 }| d¡}| j |¡ dS )zÚ
        Mid-level function to write a command to the laser.  Wraps the serial.write low level command.
        Before writing, the command string must first be encoded to ASCII bytes -- so no unicode allowed.
        éÿÿÿÿr   N)r   Úencoder   Úwrite)r   Úcmdr   r   r   r$   8   s
    

zLaserSerialInterface.writec             C   sd   | j d }|  |¡ |  ¡  ¡ | _| jdkr<td | j¡ƒ‚| jdkrNtdƒ t 	d | j¡¡ dS )z†
        Queries the "Power" register in the laser controler to determine the state of the system. Should be [ON, OFF, FAULT]
        r   )ÚonÚoffÚfaultz0check_state: Laser reports unrecognized state {}r(   z1check_state: Warning!  Laser reports fault state.zcheck_state: State: {}N)
r   r$   r!   Úlowerr   ÚRuntimeErrorÚformatÚprintÚloggingÚinfo)r   r%   r   r   r   r   D   s    



z LaserSerialInterface.check_statec             C   s$   |   ¡ }|dkr td ||¡ƒ‚dS )a^  
        After some set requests, the laser responds with an empty string.  If that doesn't happen, we should know.
        Needs the name of the calling function to specify where the error occurred.

        :param calling_function: String specifying the name of the calling function.  This is used to help localize the source of the error.
        Ú z,{}: Expected empty response, got {} instead.N)r!   r*   r+   )r   Zcalling_functionr    r   r   r   Úcheck_empty_responseS   s    z)LaserSerialInterface.check_empty_responsec             C   s‚   t  |¡}| jd d |¡ }t|  kr2tksZn t d |tt¡¡ td |tt¡ƒ‚|  	|¡ |  
d¡ t d |¡¡ dS )z·
        This method changes the wavelength of the laser.

        :param wavelength: This is the value of the wavelength to be set. Units are in nanometers.
        :return:
        r   z/{:6.2f}z4set_wavelength: Wavelength {} out of bounds {} to {}Úset_wavelengthz<set_wavelength: Sent change wavelength change request for {}N)ÚnpÚfloatr   r+   ÚWAVELENGTH_MINÚWAVELENGTH_MAXr-   r.   Ú
ValueErrorr$   r0   )r   r   Zwavelength_change_msgr   r   r   r1   a   s    


z#LaserSerialInterface.set_wavelengthc             C   s<   |   | jd ¡ |  ¡ }|dkr(tdƒ‚t | d¡¡}|S )z9
        :return wavelength: wavelength of laser
        r   r/   z0get_wavelength: No response to wavelength check.Únm)r$   r   r!   r*   r2   r3   r   )r   r    r   r   r   r   Úget_wavelengthu   s    z#LaserSerialInterface.get_wavelengthc             C   s^   |  ¡ dkrd}n |  ¡ dkr$d}ntd |¡ƒ‚| jd d |¡ }|  |¡ |  d¡ d	S )
a=  
        Change the laser to either continuous or burst mode.  If in burst mode, you will have to explicity call
        self.trigger_burst to trigger a burst.  You should also use self.set_npulses to select the number of pulses

        :param mode: Mode to change to. Must be either 'Continuous' or 'Burst'
        Z
continuousZ
ContinuousZburstZBurstz set_mode: mode {} not recognizedr   z/{}Úset_modeN)r)   r6   r+   r   r$   r0   )r   ÚmodeZ
cmd_suffixr%   r   r   r   r9      s    

zLaserSerialInterface.set_modec             C   s    | j d }|  |¡ |  ¡ }|S )z
        Query the laser to determine the current operating mode.
        TODO: Add logic to ensure mode returned is a recognized value
        r   )r   r$   r!   )r   r%   r    r   r   r   Úget_mode—   s    

zLaserSerialInterface.get_modec             C   s,   | j d d d¡ }|  |¡ |  d¡ dS )z9
        Fire a burst of npulses from the laser.
        r   z/{}ZTriggerÚtrigger_burstN)r   r+   r$   r0   )r   r%   r   r   r   r<   ¢   s    

z"LaserSerialInterface.trigger_burstc             C   sv   t |ƒt dƒkr"td t |ƒ¡ƒ‚t|  kr6tksJn td tt|¡ƒ‚| jd d |¡ }|  |¡ |  d¡ dS )zü
        Sets number of pulses to be fired by the laser.  The rep rate of the laser is generally 1kHz, so total exposure time provided
        by npulses is (npulses / 1000) seconds.

        :param npulses: Int.  Number of pulses to be fired.
        r   z:set_npulses: npulses must be an int.  You provided type {}zBset_npulses: npulses must be between {} and {}.  You requested {}.r   z/{:5d}Úset_npulsesN)	ÚtypeÚ	TypeErrorr+   ÚNPULSES_MINÚNPULSES_MAXr6   r   r$   r0   )r   r   r%   r   r   r   r=   ®   s    

z LaserSerialInterface.set_npulsesc             C   s$   | j d }|  |¡ |  ¡ }t|ƒS )z¡
        Read and return the current burst length stored in the laser.
        
        TODO: Add logic to make sure response from the laser makes sense
        r   )r   r$   r!   Úint)r   r%   r    r   r   r   Úget_npulsesÂ   s    

z LaserSerialInterface.get_npulsesc             C   sp   |  ¡ dkrd}n2|  ¡ dkr$d}n |  ¡ dkr6d}ntd |¡ƒ‚| jd d	 |¡ }|  |¡ |  d
¡ dS )z™
        Sets the energy output level of the laser.  Allowed values are [OFF, Adjust, MAX].
        :param energy_level: Requested energy level.
        r'   ÚOFFZadjustÚAdjustÚmaxÚMAXz1set_energy_level: energy level {} not recognized.r	   z/{}Úset_energy_levelN)r)   r6   r+   r   r$   r0   )r   r	   Zelevelr%   r   r   r   rH   Î   s    

z%LaserSerialInterface.set_energy_levelc             C   s6   | j d }|  |¡ |  ¡ }|dkr2td |¡ƒ‚|S )zL
        Fetch current output energy level from the laser register.
        r	   )rD   rE   rG   zBget_energy_level: Response {} not in expected energy level states.)r   r$   r!   r*   r+   )r   r%   r    r   r   r   Úget_energy_levelá   s    

z%LaserSerialInterface.get_energy_levelc             C   s6   | j d }|  |¡ |  ¡ }|dkr2td |¡ƒ‚|S )zL
        Find out whether the external interlock is closed or open.
        r
   )ÚOKzNOT OKZDefeatedzIget_ext_interlock_state: Response {} not in expected ext interlock states)r   r$   r!   r*   r+   )r   r%   r    r   r   r   Úget_ext_interlock_stateì   s    

z,LaserSerialInterface.get_ext_interlock_statec             C   s   d}dS )ag  
        Turns laser on and off. WARNING: if the laser is in "Continuous" mode, IT WILL BEGIN EMITTING.
        ENSURE PEOPLE AND EQUIPMENT IN THE ROOM ARE APPROPRIATELY PREPARED.
        ALSO ENSURE THAT THE WAVELENGTH SETTING IS APPROPRIATE FOR THE PROTECTIVE EYEWEAR.

        :param state: String specifying power state.  Should be "on" or "off".
        Nr   )r   r   r%   r   r   r   Ú	set_power÷   s    zLaserSerialInterface.set_powerN)r   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   r!   r$   r   r0   r1   r8   r9   r;   r<   r=   rC   rH   rI   rK   rL   r   r   r   r   r      s"   

r   c              C   s"   t jdd} | jdddtd | S )Nz:Changes the wavelength of the laser using rs232 interface.)Údescriptionr   r   z!Sets the wavelength of the laser.)ÚnargsÚhelpr>   )ÚargparseÚArgumentParserÚadd_argumentr3   )Úparserr   r   r   Úcreate_parser  s    rX   c             C   s   t |d}| | ¡ dS )zV
    This creates a command line and arguments for the script.

    :return: None
    )r   N)r   r1   )r   r   Zlaser_interfacer   r   r   Úmain  s    
rY   Ú__main__z/dev/ttyACM.LASER)r   )rP   r   rT   ÚstringÚnumpyr2   r-   Útimer@   rA   r4   r5   r   rX   rY   rM   rW   Ú
parse_argsÚargsr3   r   r   r   r   r   Ú<module>   s&    j