This commit is contained in:
Devaev Maxim 2020-09-10 07:09:03 +03:00
parent 31fdcd2f3c
commit a6385cd20e
2 changed files with 42 additions and 27 deletions

View File

@ -52,8 +52,9 @@ class UserGpioApi:
async def __switch_handler(self, request: Request) -> Response: async def __switch_handler(self, request: Request) -> Response:
channel = valid_ugpio_channel(request.query.get("channel")) channel = valid_ugpio_channel(request.query.get("channel"))
state = valid_bool(request.query.get("state")) state = valid_bool(request.query.get("state"))
done = await self.__user_gpio.switch(channel, state) wait = valid_bool(request.query.get("wait", "0"))
return make_json_response({"done": done}) await self.__user_gpio.switch(channel, state, wait)
return make_json_response()
@exposed_http("POST", "/gpio/pulse") @exposed_http("POST", "/gpio/pulse")
async def __pulse_handler(self, request: Request) -> Response: async def __pulse_handler(self, request: Request) -> Response:

View File

@ -26,7 +26,9 @@ import operator
from typing import List from typing import List
from typing import Dict from typing import Dict
from typing import AsyncGenerator from typing import AsyncGenerator
from typing import Callable
from typing import Optional from typing import Optional
from typing import Any
from ...logging import get_logger from ...logging import get_logger
@ -120,9 +122,14 @@ class _GpioOutput: # pylint: disable=too-many-instance-attributes
self.__switch: bool = config.switch self.__switch: bool = config.switch
self.__pulse_delay: float = config.pulse.delay self.__pulse_delay = 0.0
self.__min_pulse_delay: float = config.pulse.min_delay self.__min_pulse_delay = 0.0
self.__max_pulse_delay: float = config.pulse.max_delay self.__max_pulse_delay = 0.0
if config.pulse.delay:
assert config.pulse.max_delay > 0
self.__pulse_delay = min(max(config.pulse.delay, config.pulse.min_delay), config.pulse.max_delay)
self.__min_pulse_delay = config.pulse.min_delay
self.__max_pulse_delay = config.pulse.max_delay
self.__busy_delay: float = config.busy_delay self.__busy_delay: float = config.busy_delay
@ -135,9 +142,9 @@ class _GpioOutput: # pylint: disable=too-many-instance-attributes
return { return {
"switch": self.__switch, "switch": self.__switch,
"pulse": { "pulse": {
"delay": min(max(self.__pulse_delay, self.__min_pulse_delay), self.__max_pulse_delay), "delay": self.__pulse_delay,
"min_delay": (self.__min_pulse_delay if self.__pulse_delay else 0), "min_delay": self.__min_pulse_delay,
"max_delay": (self.__max_pulse_delay if self.__pulse_delay else 0), "max_delay": self.__max_pulse_delay,
}, },
"hw": { "hw": {
"driver": self.__driver.get_instance_id(), "driver": self.__driver.get_instance_id(),
@ -159,39 +166,44 @@ class _GpioOutput: # pylint: disable=too-many-instance-attributes
"busy": busy, "busy": busy,
} }
async def switch(self, state: bool) -> bool: async def switch(self, state: bool, wait: bool) -> None:
if not self.__switch: if not self.__switch:
raise GpioSwitchNotSupported() raise GpioSwitchNotSupported()
async with self.__region: await self.__run_action(wait, "switch", self.__inner_switch, state)
if state != self.__read():
self.__write(state)
get_logger(0).info("Switched %s to state=%d", self, state)
await asyncio.sleep(self.__busy_delay)
return True
await asyncio.sleep(self.__busy_delay)
return False
@aiotools.atomic @aiotools.atomic
async def pulse(self, delay: float, wait: bool) -> None: async def pulse(self, delay: float, wait: bool) -> None:
if not self.__pulse_delay: if not self.__pulse_delay:
raise GpioPulseNotSupported() raise GpioPulseNotSupported()
delay = min(max((delay or self.__pulse_delay), self.__min_pulse_delay), self.__max_pulse_delay) delay = min(max((delay or self.__pulse_delay), self.__min_pulse_delay), self.__max_pulse_delay)
await self.__run_action(wait, "pulse", self.__inner_pulse, delay)
# =====
@aiotools.atomic
async def __run_action(self, wait: bool, name: str, method: Callable, *args: Any) -> None:
if wait: if wait:
async with self.__region: async with self.__region:
await self.__inner_pulse(delay) await method(*args)
else: else:
await aiotools.run_region_task( await aiotools.run_region_task(
f"Can't perform pulse of {self} or operation was not completed", f"Can't perform {name} of {self} or operation was not completed",
self.__region, self.__inner_pulse_tasked, delay, self.__region, self.__action_task_wrapper, name, method, *args,
) )
@aiotools.atomic @aiotools.atomic
async def __inner_pulse_tasked(self, delay: float) -> None: async def __action_task_wrapper(self, name: str, method: Callable, *args: Any) -> None:
try: try:
await self.__inner_pulse(delay) return (await method(*args))
except GpioDriverOfflineError: except GpioDriverOfflineError:
get_logger(0).error("Can't perform pulse of %s or operation was not completed" get_logger(0).error("Can't perform %s of %s or operation was not completed: driver offline", name, self)
" because the driver is offline", self)
@aiotools.atomic
async def __inner_switch(self, state: bool) -> None:
if state != self.__read():
self.__write(state)
get_logger(0).info("Switched %s to state=%d", self, state)
await asyncio.sleep(self.__busy_delay)
@aiotools.atomic @aiotools.atomic
async def __inner_pulse(self, delay: float) -> None: async def __inner_pulse(self, delay: float) -> None:
@ -203,6 +215,8 @@ class _GpioOutput: # pylint: disable=too-many-instance-attributes
await asyncio.sleep(self.__busy_delay) await asyncio.sleep(self.__busy_delay)
get_logger(0).info("Pulsed %s with delay=%.2f", self, delay) get_logger(0).info("Pulsed %s with delay=%.2f", self, delay)
# =====
def __read(self) -> bool: def __read(self) -> bool:
return (self.__driver.read(self.__pin) ^ self.__inverted) return (self.__driver.read(self.__pin) ^ self.__inverted)
@ -228,7 +242,7 @@ class UserGpio:
notifier=self.__notifier, notifier=self.__notifier,
**drv_config._unpack(ignore=["instance_name", "notifier", "type"]), **drv_config._unpack(ignore=["instance_name", "notifier", "type"]),
) )
for (driver, drv_config) in config.drivers.items() for (driver, drv_config) in sorted(config.drivers.items(), key=operator.itemgetter(0))
} }
self.__inputs: Dict[str, _GpioInput] = {} self.__inputs: Dict[str, _GpioInput] = {}
@ -284,11 +298,11 @@ class UserGpio:
except Exception: except Exception:
get_logger().exception("Can't cleanup driver %s", driver) get_logger().exception("Can't cleanup driver %s", driver)
async def switch(self, channel: str, state: bool) -> bool: async def switch(self, channel: str, state: bool, wait: bool) -> None:
gout = self.__outputs.get(channel) gout = self.__outputs.get(channel)
if gout is None: if gout is None:
raise GpioChannelNotFoundError() raise GpioChannelNotFoundError()
return (await gout.switch(state)) await gout.switch(state, wait)
async def pulse(self, channel: str, delay: float, wait: bool) -> None: async def pulse(self, channel: str, delay: float, wait: bool) -> None:
gout = self.__outputs.get(channel) gout = self.__outputs.get(channel)