B
    ñŽi^{\  ã               @   sŒ   d Z ddlZddlZddlZddlZeZG dd„ dƒZe	dkrˆddl
Z
dZeejƒdkrdejd ZneZeedd	d
Ze
 e¡ e ¡  dS )z‰A ZCAM E2 camera.

Communicates with a TCP/IP-connected ZCAM E2 camera.
Original author: Mark Zvilius, 20-July-2019
For: The Sexton Corp
é    Nc               @   s8  e Zd ZdddddœZdddd	d
gZdZdZdZd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#d$„Zd%d&„ Zd'd(„ Zd)d*„ Zd+d,„ Zd-d.„ Zd/d0„ Zd1d2„ Zd3d4„ Zd5d6„ Zd7d8„ Zd9d:„ Zd;d<„ Zi fd=d>„Z d?d@„ Z!dAdB„ Z"e#e$e#fdCœdDdE„Z%e#e$e#fdFœdGdH„Z&e#e$e#fdFœdIdJ„Z'e$e$e#fdKœdLdM„Z(dNdO„ Z)dPdQ„ Z*d}dSdT„Z+dUdV„ Z,dWdX„ Z-dYdZ„ Z.d[d\„ Z/d]d^„ Z0d_d`„ Z1dadb„ Z2dcdd„ Z3e#e4dCœdedf„Z5e#e4dFœdgdh„Z6e#e4dFœdidj„Z7e$e4dKœdkdl„Z8dmdn„ Z9dodp„ Z:dqdr„ Z;i fdsdt„Z<dudv„ Z=e>dRefdwdx„ƒZ?dydz„ Z@dRS )~ÚE2CameraZ960Z540Z400000Ú30)ÚwidthÚheightZbitrateZfpsz--avdictzrts_transport:tcpz--livez--aidxz-1g      ð?é   é)   é*   é+   é,   Fc             C   s   || _ || _|| _|| _dS )zïConstructor.

        ip -- camera ip address
        id -- (string) unique camera id, e.g. '1', '2', '3', etc.
        master -- (bool) True if master camera, False if slave
        no_lens -- if camera has no lens (testing only)
        N)ÚipÚidÚmasterÚno_lens)Úselfr   r   r   r   © r   ú*/home/pi/sexton-multicamera/e2/E2Camera.pyÚ__init__3   s    zE2Camera.__init__c             C   sº   |   ¡ \}}|dkr||fS |  ¡ \}}|dkr8||fS | jrZ|  ¡ \}}|dkrZ||fS |  ¡ \}}|dkrv||fS |  |¡\}}|dkr”||fS |  dtj¡\}}|dkr¶||fS dS )a—  Initialize camera.

        Start with a ping to verify connectivity.
        Establish a session.
        Set various parameters appropriate for this application.
        Initialize dynamic settings (e.g. white balance).

        settings -- (dictionary) dynamic settings saved from last run
        returns -- (status,errmsg) status 0 on success
                                   errmsg on error
        r   Ústream1)r   Ú )	Úcommand_infoÚcommand_get_sessionr   Úmaster_initsÚcamera_initsÚcamera_settingsÚcommand_stream_settingsr   ÚSTREAM1_SETTINGS)r   ÚsettingsÚstatusÚerrmsgr   r   r   Ústart_cameraA   s(    zE2Camera.start_camerac             C   s(   |   d¡\}}|dkr||fS |  d¡S )zsIntializations for the master camera.

        Disable union exposure (iso and shutter) and white balance.
        ÚDisabler   )Úcommand_set_union_aeÚcommand_set_union_awb)r   r   r   r   r   r   r   n   s    zE2Camera.master_initsc             C   sˆ   |   d¡\}}|dkr||fS |  d¡\}}|  d¡\}}|dkrJ||fS |  ¡ \}}|dkrf||fS |  d¡\}}|dkr„||fS dS )a  Various camera initializations.

        Set camera to record mode.
        Turn off recording if it is on for some reason.
        WB set to Manual to allow setting directly in Kelvin.
        Configure autofocus.

        returns -- (status,errmsg) from the last command
        Zto_recr   ÚstopÚrecZManual)r   r   )Úcommand_record_modeÚcommand_recordÚcheck_record_modeÚverify_unions_disabledÚcommand_set_wb_mode)r   r   r   Úreplyr   r   r   r   z   s    zE2Camera.camera_initsc             C   sl   xP|  ¡ D ]D\}}|  d||i¡}t |¡\}}|dkr
|dkrL| jrLd}q
P q
W |  ||fd |||¡¡S )aF  Dynamic camera settings.

        These are parameters that are changed by the user on the fly,
        and are persisted to a file and restored at initialization time.

        The keys and values are persisted using the camera command format,
        so they can be used directly by build_ctrl_command().

        Examples:
        'mwb' -- white balance
        'iso' -- iso
        'shutter_time' -- shutter speed
        'video_encoder' -- video encoder
        'iris' -- f-stop

        settings -- (dictionary)
        returns -- (status,reply) from the last command
        Úsetr   Úirisz{0}={1} {2})ÚitemsÚbuild_ctrl_commandr   Úhttp_requestr   Úmake_errmsgÚformat)r   r   ÚkÚvÚurlr   r*   r   r   r   r   ¬   s    zE2Camera.camera_settingsc             C   sP   |   d¡\}}|d }|dks&||kr.||fS d ||¡}|  tj|f|¡S dS )z Check that the camera record mode is as expected.

        expected_mode -- 'rec', 'rec_ing', etc.
        returns -- (status,reply) or (status,errmsg)
        ÚqueryÚmsgr   zrecord mode: {0} expected {1}N)r%   r1   r0   r   ÚERROR_CAMERA)r   Zexpected_moder   r*   Zrecord_moder6   r   r   r   r'   Ë   s    zE2Camera.check_record_modec             C   s4   |   tjtj¡\}}|dkr$||fS |   tjtj¡S )a[  Check that union_ae and union_awb are disabled.

        These keys are set on the master. It can take some time for them to propagate
        to the slave cameras: in testing I have seen as much as 7 seconds. So this
        method polls, with a 10 second timeout, for the values to read as 'Disable'.

        returns -- (status,errmsg)
        r   )Úwait_for_settingÚE2ÚKEY_UNION_AEÚVAL_DISABLEÚKEY_UNION_AWB)r   r   r   r   r   r   r(   Ú   s    	zE2Camera.verify_unions_disabledc             C   s"   |   dddi¡}| j|ddddS )Nr   Úactionr5   r   Úidleg      4@)ÚkeyÚvalueÚtimeout)Úbuild_stream_settings_commandÚ_wait_for_value)r   r4   r   r   r   Úwait_for_stream_idleî   s    zE2Camera.wait_for_stream_idleç      $@c             C   s"   |   dd|i¡}| j|d||dS )NÚgetr2   r@   )r?   r@   rA   )r.   rC   )r   r?   ÚvalrA   r4   r   r   r   r8   û   s    zE2Camera.wait_for_settingc             C   sÚ   d}d}d}i }x¢t t|| ƒƒD ]Ž}	t |¡\}
}|
dkrD|
|fS || }||kr||	dkrxt| j› d|› d|d›ƒ dS |	dkržt| j› d	|› d
|› ƒ t |¡ ||7 }q"W |› d|› d|› }|  tj	|f|¡S )Ngš™™™™™¹?g        r   r   z: got z after z.1f)r   r   z: wait z got z secs waiting for )
ÚrangeÚroundr   r/   Úprintr   ÚtimeZsleepr0   r7   )r   r4   r?   r@   rA   ZTICKÚelapsedZgot_valr*   Úir   r6   r   r   r   rC   	  s&    
zE2Camera._wait_for_valuec             C   s(   |   ¡ }t |¡}|  |d |d ¡¡S )z©Send camera-info command, receive response.

        Useful as a ping.

        returns -- (status,reply) if success
                   (status,errmsg) if error
        zinfo {0}é   )Úbuild_info_commandr   r/   r0   r1   )r   r4   Úrr   r   r   r     s    
zE2Camera.command_infoc             C   sN   |   ¡ }t |¡\}}|dkr6|  tj|fd |¡¡S |  ||fd| ¡S dS )zoGet camera session.

        returns -- (status,errmsg)
        status 409 means could not get session
        i™  zcould not get session: {0}zget_sesssion %sN)Úbuild_session_commandr   r/   r0   ÚERROR_SESSIONr1   )r   r4   r   r*   r   r   r   r   +  s
    zE2Camera.command_get_sessionc             C   s*   |   d¡}t |¡}|  |d |d ¡¡S )zAQuit camera session.

        returns -- (status,errmsg)
        Úquitzquit_session {0}rN   )rQ   r   r/   r0   r1   )r   r4   rP   r   r   r   Úcommand_quit_session9  s    

zE2Camera.command_quit_sessionc             C   s,   |   |¡}t |¡}|  |d ||d ¡¡S )zrSet ISO value.

        val -- (string) iso setting
        returns -- (status,reply) from http_request()
        ziso {0} {1}rN   )Úbuild_iso_commandr   r/   r0   r1   )r   rG   r4   rP   r   r   r   Úcommand_set_isoC  s    

zE2Camera.command_set_isoc             C   s,   |   |¡}t |¡}|  |d ||d ¡¡S )z‡Set white balance mode: auto/manual.

        mode -- (string) wb mode ('Auto' or 'Manual')
        returns -- (status,errmsg)
        zwb_mode {0} {1}rN   )Úbuild_wb_mode_commandr   r/   r0   r1   )r   Úmoder4   rP   r   r   r   r)   N  s    

zE2Camera.command_set_wb_modec             C   s,   |   |¡}t |¡}|  |d ||d ¡¡S )zrSet white balance.

        val -- (string) wb value as degrees kelvin
        returns -- (status,errmsg)
        z
wb {0} {1}rN   )Úbuild_wb_commandr   r/   r0   r1   )r   rG   r4   rP   r   r   r   Úcommand_set_wbY  s    

zE2Camera.command_set_wbc             C   s,   |   |¡}t |¡}|  |d ||d ¡¡S )zeSet shutter speed.

        val -- (string) shutter speed
        returns -- (status,errmsg)
        zshutter {0} {1}rN   )Úbuild_shutter_commandr   r/   r0   r1   )r   rG   r4   rP   r   r   r   Úcommand_set_shutterd  s    

zE2Camera.command_set_shutterc             C   s,   |   |¡}t |¡}|  |d ||d ¡¡S )zjSet video encoder.

        val -- (string) video encoder name
        returns -- (status,errmsg)
        zencoder {0} {1}rN   )Úbuild_encoder_commandr   r/   r0   r1   )r   rG   r4   rP   r   r   r   Úcommand_set_encodero  s    

zE2Camera.command_set_encoderc             C   s6   |   |¡}t |¡}| jrdS |  |d ||d ¡¡S )zdSet f-stop (iris).

        val -- (string) f-stop value
        returns -- (status,errmsg)
        )r   r   zfstop {0} {1}rN   )Úbuild_iris_commandr   r/   r   r0   r1   )r   rG   r4   rP   r   r   r   Úcommand_set_fstopz  s
    

zE2Camera.command_set_fstopc             C   s0   |   |¡}t |¡}|  |d|› d|d › ¡S )zêSet union exposure: when enabled, shutter and iso are controlled by master.
        Should be set only on the master camera; slaves will return error.

        val -- (string) Enable/Disable
        returns -- (status,errmsg)
        z	union_ae ú rN   )Úbuild_union_ae_commandr   r/   r0   )r   rG   r4   rP   r   r   r   r!   ˆ  s    

zE2Camera.command_set_union_aec             C   s0   |   |¡}t |¡}|  |d|› d|d › ¡S )zìSet union white balance: when enabled, white balance is controlled by master.
        Should be set only on the master camera; slaves will return error.

        val -- (string) Enable/Disable
        returns -- (status,errmsg)
        z
union_awb ra   rN   )Úbuild_union_awb_commandr   r/   r0   )r   rG   r4   rP   r   r   r   r"   ”  s    

zE2Camera.command_set_union_awbc             C   s.   |   ||¡}t |¡}|  |d ||d ¡¡S )z±Set stream settings.

        index -- 'stream0' or 'stream1'
        settings -- e.g. width, height, bitrate, fps
        returns -- (status,reply) from http_request()
        zstream_settings {0} {1}rN   )rB   r   r/   r0   r1   )r   Úindexr   r4   rP   r   r   r   r      s    
z E2Camera.command_stream_settingsc             C   s,   |   |¡}t |¡}|  |d ||d ¡¡S )zControl camera recording mode.

        action -- 'to_rec', 'to_pb'
        returns -- (status,reply) or (status,errmsg)
        zrecord_mode {0} {1}rN   )Úbuild_record_mode_commandr   r/   r0   r1   )r   r=   r4   rP   r   r   r   r%   ¬  s    

zE2Camera.command_record_modec             C   s2   |   |¡}tj|tjd}|  |d ||d ¡¡S )z­Control recording.

        action -- 'start', 'stop', or 'remain'
        returns -- (status,reply) from http_request()
                   (status,errmsg) on error
        )rA   zrecord {0} {1}rN   )Úbuild_record_commandr   r/   ÚRECORD_TIMEOUTr0   r1   )r   r=   r4   rP   r   r   r   r&   ·  s    
zE2Camera.command_record)rX   Úreturnc             C   s0   |   |¡}t |¡}|  |d|› d|d › ¡S )Nzfocus ra   rN   )Úbuild_set_focus_commandr   r/   r0   )r   rX   r4   rP   r   r   r   Úcommand_set_focus_modeÉ  s    

zE2Camera.command_set_focus_mode)rG   rh   c             C   s0   |   |¡}t |¡}|  |d|› d|d › ¡S )Nzcaf ra   rN   )Úbuild_set_caf_commandr   r/   r0   )r   rG   r4   rP   r   r   r   Ú command_set_continuous_autofocusÕ  s    

z)E2Camera.command_set_continuous_autofocusc             C   s0   |   |¡}t |¡}|  |d|› d|d › ¡S )Nzrestore_lens_pos ra   rN   )Ú"build_set_restore_lens_pos_commandr   r/   r0   )r   rG   r4   rP   r   r   r   Úcommand_set_restore_lens_posá  s    

z%E2Camera.command_set_restore_lens_pos)Úposrh   c             C   s0   |   |¡}t |¡}|  |d|› d|d › ¡S )Nzrestore_lens_pos ra   rN   )Ú build_set_lens_focus_pos_commandr   r/   r0   )r   ro   r4   rP   r   r   r   Úcommand_set_lens_focus_posí  s    

z#E2Camera.command_set_lens_focus_posc             C   s   |   ¡ }t |¡}dS )z¾Get camera temperature in degC.

        returns -- (status,result)
                   success: status=0 and result=temperature
                   error: status!=0 and result=errmsg
        N)Zbuild_get_temperaturer   r/   )r   r4   rP   r   r   r   Úget_temperatureò  s    zE2Camera.get_temperaturec             C   s   |   ¡ d S )zBBuild camera-info command.

        returns -- url string
        Úinfo)Ú
url_prefix)r   r   r   r   rO   þ  s    zE2Camera.build_info_commandNc             C   s   i }|r||d< |   d|¡S )zgBuild camera session command.

        action -- None to get sesssion, 'quit' to quit session.
        r=   Zsession)r.   )r   r=   Úparamsr   r   r   rQ     s    zE2Camera.build_session_commandc             C   s   |   dtj|i¡S )z<Build set-iso command.

        val -- ISO setting.
        r+   )r.   r9   ÚKEY_ISO)r   rG   r   r   r   rU     s    zE2Camera.build_iso_commandc             C   s   |   dtj|i¡S )zgBuild set-white-balance-mode command.

        mode -- white-balance mode ('Auto' or 'Manual')
        r+   )r.   r9   Ú
KEY_WBMODE)r   rX   r   r   r   rW     s    zE2Camera.build_wb_mode_commandc             C   s   |   dtj|i¡S )zMBuild set-white-balance command.

        val -- white-balance value
        r+   )r.   r9   ÚKEY_WB)r   rG   r   r   r   rY   !  s    zE2Camera.build_wb_commandc             C   s   |   dtj|i¡S )zMBuild set-shutter-speed command.

        val -- shutter speed value
        r+   )r.   r9   ÚKEY_SHUTTER)r   rG   r   r   r   r[   )  s    zE2Camera.build_shutter_commandc             C   s   |   dtj|i¡S )zFBuild set-encoder command.

        val -- video encoder name
        r+   )r.   r9   ÚKEY_ENCODER)r   rG   r   r   r   r]   1  s    zE2Camera.build_encoder_commandc             C   s   |   dtj|i¡S )z7Build iris command.

        val -- iris value
        r+   )r.   r9   ÚKEY_IRIS)r   rG   r   r   r   r_   9  s    zE2Camera.build_iris_commandc             C   s   |   dtj|i¡S )z?Build union_ae command.

        val -- Enable/Disable
        r+   )r.   r9   r:   )r   rG   r   r   r   rb   A  s    zE2Camera.build_union_ae_commandc             C   s   |   dtj|i¡S )z@Build union_awb command.

        val -- Enable/Disable
        r+   )r.   r9   r<   )r   rG   r   r   r   rc   I  s    z E2Camera.build_union_awb_commandc             C   s   |   dtj|i¡S )Nr+   )r.   r9   Ú	KEY_FOCUS)r   rX   r   r   r   ri   W  s    z E2Camera.build_set_focus_commandc             C   s   |   dtj|i¡S )Nr+   )r.   r9   ÚKEY_CAF)r   rG   r   r   r   rk   a  s    zE2Camera.build_set_caf_commandc             C   s   |   dtj|i¡S )Nr+   )r.   r9   ÚKEY_RESTORE_LENS)r   rG   r   r   r   rm   k  s    z+E2Camera.build_set_restore_lens_pos_commandc             C   s   |   dtj|i¡S )Nr+   )r.   r9   ÚKEY_LENS_FOCUS_POS)r   ro   r   r   r   rp   u  s    z)E2Camera.build_set_lens_focus_pos_commandc             C   s   d|i|–}|   d|¡S )z„Build set-stream-settings command.

        index -- (string) stream index
        settings -- (dictionary) stream settings
        rd   Zstream_setting)r.   )r   rd   r   ru   r   r   r   rB   x  s    z&E2Camera.build_stream_settings_commandc             C   s   |   dd|i¡S )z?Build record-mode command.

        action -- (string)
        rX   r=   )r.   )r   r=   r   r   r   re   ‚  s    z"E2Camera.build_record_mode_commandc             C   s   |   dd|i¡S )zOBuild record command.

        action -- (string) 'start', 'stop', etc
        r$   r=   )r.   )r   r=   r   r   r   rf   Š  s    zE2Camera.build_record_commandc             C   sJ   |   ¡ › d|› }d}x.| ¡ D ]"\}}||› |› d|›  }d}q W |S )z¼Build a "/ctrl" command.

        Format: <prefix>/ctrl/<command>[?<key>=<value>[&<key>=<value>]...]

        command -- (string)
        params -- (dictionary) option-value pairs
        zctrl/ú?ú=ú&)rt   r-   )r   Úcommandru   r4   Z
query_charr2   r3   r   r   r   r.   ’  s    zE2Camera.build_ctrl_commandc             C   s   d| j › dS )zReturn prefix for URL.zhttp://ú/)r   )r   r   r   r   rt   ¢  s    zE2Camera.url_prefixc             C   s    yt j| ||d}W nJ t jjk
r:   tjd | |¡fS  t jjk
r^   tjd | |¡fS X |j	dkr||j	d |j	¡fS d}| 
¡ }d|kr˜|d }||fS )aO  Issue request and get reply.

        url -- (string)
        payload -- (string or None)
        timeout -- comm timeout in seconds (number)

        returns -- (status,reply)
            status: (integer) 0 success, nonzero ERROR_TIMEOUT or status_code
            reply: (dictionary) json portion of reply, or error message
        )ru   rA   z connection error: {0} ({1} secs)ztimeout: {0} ({1} secs)éÈ   zhttp status {0}r   Úcode)ÚrequestsrF   Ú
exceptionsÚConnectionErrorr   ÚERROR_CONNECTIONr1   ZTimeoutÚERROR_TIMEOUTZstatus_codeÚjson)r4   ZpayloadrA   rP   r   r*   r   r   r   r/   ¦  s    
zE2Camera.http_requestc             C   s*   |d }|dkr|S |d  | j|¡fS dS )zÎMake human-readable error message.

        sr -- (status, reply) tuple
        msg -- partial error message
        returns -- (status,reply) if success
                   (status,errmsg) if error
        r   zcam{0}: {1}N)r1   r   )r   Úsrr6   r   r   r   r   r0   Å  s    zE2Camera.make_errmsg)F)rE   )N)AÚ__name__Ú
__module__Ú__qualname__r   ZOMXPLAYER_ARGSZDEFAULT_TIMEOUTrg   rŠ   r‹   rR   r7   r   r   r   r   r   r'   r(   rD   r8   rC   r   r   rT   rV   r)   rZ   r\   r^   r`   r!   r"   r   r%   r&   ÚstrÚintrj   rl   rn   rq   rr   rO   rQ   rU   rW   rY   r[   r]   r_   rb   rc   ÚUrlri   rk   rm   rp   rB   re   rf   r.   rt   Ústaticmethodr/   r0   r   r   r   r   r      sz   
-2






r   Ú__main__z10.6.10.162rN   Ú1T)r   )Ú__doc__Úsysr‡   rK   Ú
E2Protocolr9   r‘   r“   r   rŽ   ZE2Camera_testsZDEFAULT_CAMERA_IPÚlenÚargvr   ÚcamZrun_unit_testsÚexitr   r   r   r   Ú<module>   s(        I
