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:
channel = valid_ugpio_channel(request.query.get("channel"))
state = valid_bool(request.query.get("state"))
done = await self.__user_gpio.switch(channel, state)
return make_json_response({"done": done})
wait = valid_bool(request.query.get("wait", "0"))
await self.__user_gpio.switch(channel, state, wait)
return make_json_response()
@exposed_http("POST", "/gpio/pulse")
async def __pulse_handler(self, request: Request) -> Response:

View File

@ -26,7 +26,9 @@ import operator
from typing import List
from typing import Dict
from typing import AsyncGenerator
from typing import Callable
from typing import Optional
from typing import Any
from ...logging import get_logger
@ -120,9 +122,14 @@ class _GpioOutput: # pylint: disable=too-many-instance-attributes
self.__switch: bool = config.switch
self.__pulse_delay: float = config.pulse.delay
self.__min_pulse_delay: float = config.pulse.min_delay
self.__max_pulse_delay: float = config.pulse.max_delay
self.__pulse_delay = 0.0
self.__min_pulse_delay = 0.0
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
@ -135,9 +142,9 @@ class _GpioOutput: # pylint: disable=too-many-instance-attributes
return {
"switch": self.__switch,
"pulse": {
"delay": min(max(self.__pulse_delay, self.__min_pulse_delay), self.__max_pulse_delay),
"min_delay": (self.__min_pulse_delay if self.__pulse_delay else 0),
"max_delay": (self.__max_pulse_delay if self.__pulse_delay else 0),
"delay": self.__pulse_delay,
"min_delay": self.__min_pulse_delay,
"max_delay": self.__max_pulse_delay,
},
"hw": {
"driver": self.__driver.get_instance_id(),
@ -159,39 +166,44 @@ class _GpioOutput: # pylint: disable=too-many-instance-attributes
"busy": busy,
}
async def switch(self, state: bool) -> bool:
async def switch(self, state: bool, wait: bool) -> None:
if not self.__switch:
raise GpioSwitchNotSupported()
async with self.__region:
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
await self.__run_action(wait, "switch", self.__inner_switch, state)
@aiotools.atomic
async def pulse(self, delay: float, wait: bool) -> None:
if not self.__pulse_delay:
raise GpioPulseNotSupported()
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:
async with self.__region:
await self.__inner_pulse(delay)
await method(*args)
else:
await aiotools.run_region_task(
f"Can't perform pulse of {self} or operation was not completed",
self.__region, self.__inner_pulse_tasked, delay,
f"Can't perform {name} of {self} or operation was not completed",
self.__region, self.__action_task_wrapper, name, method, *args,
)
@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:
await self.__inner_pulse(delay)
return (await method(*args))
except GpioDriverOfflineError:
get_logger(0).error("Can't perform pulse of %s or operation was not completed"
" because the driver is offline", self)
get_logger(0).error("Can't perform %s of %s or operation was not completed: driver offline", name, 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
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)
get_logger(0).info("Pulsed %s with delay=%.2f", self, delay)
# =====
def __read(self) -> bool:
return (self.__driver.read(self.__pin) ^ self.__inverted)
@ -228,7 +242,7 @@ class UserGpio:
notifier=self.__notifier,
**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] = {}
@ -284,11 +298,11 @@ class UserGpio:
except Exception:
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)
if gout is None:
raise GpioChannelNotFoundError()
return (await gout.switch(state))
await gout.switch(state, wait)
async def pulse(self, channel: str, delay: float, wait: bool) -> None:
gout = self.__outputs.get(channel)