a
    i2F                     @   sj   d Z ddlZddlZddlZddlmZmZ G dd dZee	dddZ
dd	d
dZedkrfe  dS )uC  
MCP23017 - 16-разрядный расширитель каналов ввода-вывода с последовательным интерфейсом. 
- I2C интерфейс
- I2C адрес: 0x20 - 0х27 (адрес устанавливается контактами A0, A1, A2). По умолчанию 0x20.
- Питание модуля: 3,3 В или 5 В
- Ток потребления:
	собственно микросхемы - 1mA при частоте шины 1MHz, 
	в режиме ожидания не более 3µA
- Количество каналов ввода-вывода: 16 шт.
- Datasheet: https://www.farnell.com/datasheets/2243622.pdf

	Назначение: 
		1. Определение состояния кналов микросхемы.
		2. Установка режима ввода/вывода и нового состояния каналов микросхемы.
	Возможности:
		1. Использование как класса, используемого приложением
		2. Использование как самостоятельного приложения
	Синтаксис:
		mcp23017.py [--uid] [--bus] [-a address] [-c channel [0 - 15]] [-m mode {input|output}] [-s state {0|1}] [-o output {text|json}]
		где:
			--uid       -   Уникальный идентификатор. 
							Параметр не обязательный. 
							В коде никак не обрабатывается.
							Предназначен скорее для источника, запросившего данные с датчика, может быть использован 
							при синхронном запросе в источнике и последующей обработке результата в источнике.
			--bus		-   Номер шины I2C на Raspberry Pi. 
							Параметр не обязательный.
							Допускается следующий формат:
								параметр не указан - по умолчанию номер шины = 0
								0|1 - номер шины. 
			-a address 	- I2C адрес на котором расположена микросхема mcp23017. 
							Параметр не обязательный.
							Допускается следующий формат:
								- параметр не указан - будет установлено значение по умолчанию, в соответствии 
								с установленным ADDRESS;
								- адрес в десятичном или шестнадцатеричном представлении.
			-c channel	- Список номеров каналов [от 0 до 15], для которых необходимо установить новое или прочесть текущее состояние порта. 
							Параметр не обязательный.
							Допускается следующий формат:
								- параметр не указан - будут установлены или прочитаны значения всех портов микросхемы
								- список каналов, разделенных пробелом - будут установлены или прочитаны значения для 
								перечисленных каналов. Допускается указание как в шестнадцатеричном, так и в десятичном представлении.
			-m mode		- Режим канала. 
							Параметр не обязательный.
							Допускается следующий формат:
								- input - канал будет установлен в режим ввода (режим считывания состояния источника)
								- output - канал будет установлен в ежим вывода (режим управления получателем)
			-s state	- Состояние канала. 
							Параметр не обязательный.
							Допускается следующий формат:
								- параметр не указан - будет считано состояние указанных каналов.
								- 0 - перечисленные каналы будут установлены в выключенное состояние LOW
								- 1 - перечисленные каналы будут установлены во включенное состояние HIGH 
			-o output	- Тип результата. 
							Параметр не обязательный.
							Допускается	следующий формат:
								параметр не указан - вывода не будет
								- text - тип text
								- json - тип json	

	Ответ в формате:
	json 
		{
			status: код ответа от сервиса (например 200 - ок|400 - не коррекнтый запрос|520 - неизвестная ошибка| и т.д.)
			message: описание ответа
			request: {
				uid: уникальный идентификатор
				bus: номер I2C шины 		
				address: адрес на котором расположена микросхема mcp23017.
				channel: [cписок каналов, для которых необходимо прочесть/установить состояние или установить режим]
				mode: режим канала
				state: состояние канала
				output: тип результата
			}
			response: [{
				channel: номер канала
				state: состояние канала {0|1}
			}]
		}                                                    
	text
		status<код ответа> - <описание ответа>
		uid<уникальный идентификатор> address<адрес> channel<cписок каналов, разделенный запятыми> mode<режим канала> state<состояние канала> output<тип результата>
		channel<номер канала> state<состояние канала>
		...
		channel<номер канала> state<состояние канала>
		
	Модуль написан в 2025 году.
	Основой для создания модуля послужила статья 
	https://github.com/sensorberg/MCP23017-python
    N)AnyListc                   @   s  e Zd ZdZdZdZdZdZdZdZ	dZ
eed	ZdZdZd
ZdZdefeeddddZd+eddddZefeddddZddddZddddZeeddddZeeddddZedd d!d"Zeed#d$d%Zee dd&d'Zeeedd(d)d*Z dS ),MCP23017z0.2    r                        N)i2caddressreturnc                 C   s   || _ || _d S N)i2cbusr   )selfr   r    r   1/var/aqua_controller/devices/mcp23017/mcp23017.py__init__z   s    zMCP23017.__init__)r   r   c                 C   s
   || _ d S r   )r   )r   r   r   r   r   set_i2c   s    zMCP23017.set_i2c)r   r   c                 C   s
   || _ d S r   )r   )r   r   r   r   r   set_address   s    zMCP23017.set_addressr   c              
   C   sd   z0| j | j| j| j | j | j| j| j W n. ty^ } ztd|W Y d }~n
d }~0 0 d S )NuI   Ошибка установки каналов в режим вывода)r   write_byte_datar   IODIRAOUTPUTIODIRB	ExceptionIOErrorr   er   r   r   set_all_output   s
    zMCP23017.set_all_outputc              
   C   sd   z0| j | j| j| j | j | j| j| j W n. ty^ } ztd|W Y d }~n
d }~0 0 d S )NuG   Ошибка установки каналов в режим ввода)r   r   r   r   INPUTr   r   r   r   r   r   r   set_all_input   s
    zMCP23017.set_all_input)gpiomoder   c              
   C   s   zjd|  krdks n t d|| j| jfvr8t d|dk rF| jn| j}|d }| |||| jk W n4 ty } ztd| |W Y d }~n
d }~0 0 d S )Nr      e   Номер канала может быть установлен в диапазоне от 0 до 15uA   Режим канала должен быть INPUT или OUTPUT   uB   Ошибка установки режима для канала #)
ValueErrorr"   r   r   r   _MCP23017__fn_set_register_bitr   r   )r   r$   r%   regbitr    r   r   r   set_mode   s    zMCP23017.set_mode)r$   stater   c              
   C   s   zjd|  krdks n t d|| j| jfvr8t d|dk rF| jn| j}|d }| |||| jk W n4 ty } ztd| |W Y d }~n
d }~0 0 d S )Nr   r&   r'   u>   Значение должно быть LOW (0) или HIGH (1)r(   uH   Ошибка установки состояния для канала #)r)   LOWHIGHOLATAOLATBr*   r   r   )r   r$   r.   r+   r,   r    r   r   r   	set_state   s    zMCP23017.set_state)r.   r   c                 C   s&   | j D ]}| || td qd S )N皙?)ALL_GPIOr3   timesleep)r   r.   Z
_item_gpior   r   r   set_all_state   s    
zMCP23017.set_all_state)r$   r   c              
   C   s   z`d|  krdks n t d|dk r.| jn| j}|d }| j| j|}|d|> @ r\dndW S  ty } ztd| |W Y d }~n
d }~0 0 d S )Nr   r&   r'   r(   r   u;   Ошибка чтения состояния канала #)r)   GPIOAGPIOBr   read_byte_datar   r   r   )r   r$   r+   r,   valuer    r   r   r   	get_state   s    zMCP23017.get_statec              
      s   zR| j | j| j | j | j| j fddtdD fddtdD  W S  ty } ztd|W Y d }~n
d }~0 0 d S )Nc                    s   g | ]} |? d @ qS r   r   .0i)ar   r   
<listcomp>       z*MCP23017.get_all_state.<locals>.<listcomp>r(   c                    s   g | ]} |? d @ qS r>   r   r?   )br   r   rC      rD   uK   Ошибка чтения состояния для всех каналов)r   r;   r   r9   r:   ranger   r   r   r   )rB   rE   r   get_all_state   s    .zMCP23017.get_all_state)r+   r,   enabler   c                 C   sF   | j | j|}|r"|d|> O }n|d|>  M }| j | j|| d S )Nr   )r   r;   r   r   )r   r+   r,   rH   valr   r   r   Z__fn_set_register_bit   s
    zMCP23017.__fn_set_register_bit)N)!__name__
__module____qualname__VERSIONZADDRESSr   r   r9   r:   r1   r2   listrF   r5   r0   r/   r"   r   r   intr   r   r   r!   r#   r-   r3   r8   r=   r   rG   boolr*   r   r   r   r   r   d   s0   
	r   )r<   r   c                 C   s6   zt | dW S  ty0   td|  dY n0 d S )Nr   u#   Неверное значение: u+   . Ожидается целое число.)rO   r)   argparseArgumentTypeError)r<   r   r   r   __fn_to_int   s    rS   r   c               
   C   s  d} d}i }zt jdd}|jdtdd dd |jd	tdd
dd |jddtdd dd |jddtddd dd |jddtdddgd dd |jddtdd
dgd dd |jddtdd d!gd d"d | }W n6 t jy } zd#} d$| }W Y d }~n
d }~0 0 zd
d l}||j	}W n< t
yZ } z"d%} d&|j	 d'| }W Y d }~n
d }~0 0 | dkrz,t }|| |jd ur||j W n4 t
y } zd(} d)| }W Y d }~n
d }~0 0 |jd u rttd*n|j}	|jdkrtjn|jdkr
tjnd }
|jdkr tjn|jd
kr2tjnd }| dkr|	d urzH|	D ]>}|tjv rP|
d urv|||
 |d urP||| qPW n4 t
y } zd(} d+| }W Y d }~n
d }~0 0 | dkrP|	d urPz>|	D ]4}|tjv r|d u r||||< td, qW n4 t
yN } zd(} d-| }W Y d }~n
d }~0 0 |d urz|  W n4 t
y } zd.} d/| }W Y d }~n
d }~0 0 z|jd ur"|j  d kr" }|d0t|  d1t| d27 }||j!d urd3t|j! d4nd57 }||j	d ur$d6t|j	 d4nd57 }||jd urJd7t"|j d4nd57 }||jd urxd8d9#t$t|j d4nd57 }||jd urd:|j d4nd57 }||jd urd;|j d4nd57 }||jd urd<t|j d4nd57 }|d=7 }|D ]$}|d8| d>t||  d27 }qt%| |jd ur\|j  d!kr\i }| |d?< t||d@< i |dA< |j!d ur|t|j!|dA dB< |j	d ur|j	|dA dC< |jd urt"|j|dA dD< |jd ur|j|dA dE< |jd ur|j|dA dF< |jd ur|j|dA dG< |jd ur|j|dA d< g |dH< |D ]}|dH &||| dI q(t%t'j(|ddJdK W n4 t
y } zd.} dL| }W Y d }~n
d }~0 0 d S )MN   oku-   Управление MCP23017 через I2C.)descriptionz--uidFu>   Уникальный идентификатор запроса)typerequireddefaulthelpz--busr   uE   Номер шины I2C на Raspberry Pi (по умолчанию 0)z-az	--addressu.   Адрес раширителя каналовz-cz	--channel*u;   Номер канала расширителя портов)rW   rX   nargsrY   rZ   z-mz--modeinputoutputu6   Режим ввода/вывода для канала)rW   rX   choicesrY   rZ   z-sz--stater   u.   Состояние канала {0-LOW|1-HIGH}z-oz--outputtextjsonu+   Формат вывода: text или jsoni  uA   ошибка обработки командной строки: i  u   i2c шина u    недоступна: i  u>   Ошибка инициализации устройства: r   uC   Ошибка изменения состояния каналов: r4   uG   Ошибка определения состояния каналов: i  u1   Не удалось закрыть шину i2c: zstatus<z> - <z>
zuid<z>  zbus<zaddress<zchannel<,zmode<zstate<zoutput<
z> state<statusmessagerequestuidbusr   channelr%   r.   response)rj   r.      )ensure_asciiindentu4   ошибка формирования отчета: ))rQ   ArgumentParseradd_argumentstrrS   
parse_argsArgumentErrorsmbusSMBusri   r   r   r   r   r   rj   rN   rF   r%   r"   r   r.   r0   r/   r5   r-   r3   r=   r6   r7   closer^   lowerrh   hexjoinmapprintappendra   dumps)_status_status_message_resultparserargsr    rt   Zmy_i2cZmy_MCP23017Z_channel_mode_stateZ_item_channel	_response_item_resultr   r   r   main   s     (

 ((

 
 
 &&&.""&"r   __main__)__doc__rQ   r6   ra   typingr   r   r   rq   rO   rS   r   rJ   r   r   r   r   <module>   s   [  