feat: added functional for led indicator, class instance for RobotControl and FormResponse moved to function start()

This commit is contained in:
Arduinum628
2025-10-20 16:07:29 +03:00
parent 72af2bc2f9
commit 16729812b6
3 changed files with 90 additions and 23 deletions

View File

@@ -2,7 +2,7 @@
WEBSOCKET_HOST=хост
WEBSOCKET_PORT=порт
# Команды
# Команды робота
FORWARD=вперёд
BACKWARD=назад
LEFT=влево

View File

@@ -1,5 +1,6 @@
from gpiod import request_lines, LineSettings
from gpiod import request_lines, LineSettings, RequestReleasedError
from gpiod.line import Direction, Value
import asyncio
from settings import settings
@@ -19,6 +20,7 @@ class LedLineGpio:
)
}
)
self._task: asyncio.Task | None = None
def on(self) -> None:
"""Метод для включения LED"""
@@ -27,9 +29,34 @@ class LedLineGpio:
def off(self) -> None:
"""Метод для выключения LED"""
self._request.set_value(self.line, Value.INACTIVE)
async def _blinking(self) -> None:
"""Метод для мигания LED"""
try:
while True:
self._request.set_value(self.line, Value.ACTIVE)
await asyncio.sleep(0.2)
self._request.set_value(self.line, Value.INACTIVE)
await asyncio.sleep(0.2)
except RequestReleasedError:
pass
def start_blinking(self) -> None:
"""Метод для добавления асинхронной задачи мигания LED"""
if not self._task or self._task.done():
self._task = asyncio.create_task(self._blinking())
def stop_blinking(self) -> None:
"""Метод для остановки мигания LED"""
if self._task:
self._task.cancel()
self._request.set_value(self.line, Value.INACTIVE)
def close(self) -> None:
"""Метод для освобождения ресурса"""
@@ -107,7 +134,29 @@ class RobotControl:
consumer=settings.gpio_lines.right_motor_consumer
)
self._led_indicator = LedLineGpio(
line=settings.gpio_lines.led_line,
gpio_path=settings.gpio_lines.gpio_path,
consumer=settings.gpio_lines.led_consumer
)
self.stop()
self.ready_to_connect()
def ready_to_connect(self) -> None:
"""Готовность робота к подключению (индикация)"""
self._led_indicator.start_blinking()
def connected(self) -> None:
"""Робот подключен (индикация)"""
self._led_indicator.on()
def blinking_off(self) -> None:
"""Выключение моргания LED"""
self._led_indicator.stop_blinking()
def forward(self) -> None:
"""Движение робота вперёд"""
@@ -124,15 +173,15 @@ class RobotControl:
def left(self) -> None:
"""Поворот робота налево"""
self._right_motor.forward_motor()
self._left_motor.backward_motor()
self._right_motor.backward_motor()
self._left_motor.forward_motor()
def right(self) -> None:
"""Поворот робота направо"""
self._right_motor.backward_motor()
self._left_motor.forward_motor()
self._right_motor.forward_motor()
self._left_motor.backward_motor()
def stop(self) -> None:
"""Остановка робота"""
@@ -144,3 +193,4 @@ class RobotControl:
self._left_motor.close()
self._right_motor.close()
self._led_indicator.close()

View File

@@ -1,4 +1,6 @@
import asyncio
from functools import partial
from gpiod import exception
from socket import gethostbyname
from websockets import serve, exceptions
from websockets.legacy.server import WebSocketServerProtocol
@@ -9,20 +11,25 @@ from gpio_control import RobotControl
from response_data import FormResponse
async def robot_control_gpio(websocket: WebSocketServerProtocol) -> None:
async def robot_control_gpio(
websocket: WebSocketServerProtocol,
robot_control: RobotControl,
commands_status: FormResponse
) -> None:
"""
Асинхронная функция для управлением gpio робота (через websocket)
"""
try:
robot_control = RobotControl()
commands_status = FormResponse
while True:
try:
command = await asyncio.wait_for(websocket.recv(), timeout=30.0)
data = loads(command)
command_name = data.get('command')
if command_name != settings.commands_robot.stop:
robot_control.blinking_off()
robot_control.connected()
except asyncio.TimeoutError as err:
print('Таймаут ожидания команды от клиента')
continue # продолжение цикла, чтобы не закрывать соединение
@@ -41,6 +48,7 @@ async def robot_control_gpio(websocket: WebSocketServerProtocol) -> None:
robot_control.right()
case settings.commands_robot.stop:
robot_control.stop()
robot_control.ready_to_connect()
case _:
data.update(commands_status.NOT_FOUND_COMMAND.response)
await websocket.send(message=dumps(data))
@@ -48,9 +56,9 @@ async def robot_control_gpio(websocket: WebSocketServerProtocol) -> None:
if settings.commands_robot.is_command(command=command_name):
data.update(commands_status.OK_COMMAND.response)
await websocket.send(message=dumps(data))
except exceptions.ConnectionClosed:
except (exceptions.ConnectionClosed, exception.RequestReleasedError):
pass
except (exceptions.ConnectionClosedOK, exceptions.InvalidMessage, exceptions.InvalidState) as err:
except (exceptions.ConnectionClosedOK, exceptions.InvalidMessage, exceptions.InvalidState, OSError) as err:
message_err = f'{err.__class__.__name__}: {err}'
print(message_err)
data.update(
@@ -60,8 +68,6 @@ async def robot_control_gpio(websocket: WebSocketServerProtocol) -> None:
)
)
await websocket.send(message=dumps(data))
finally:
robot_control.close()
async def start() -> None:
@@ -69,13 +75,24 @@ async def start() -> None:
print('Старт сервиса робота для приёма команд.')
async with serve(
handler=robot_control_gpio,
host=gethostbyname(settings.websocket_host),
port=settings.websocket_port
):
# бесконечный цикл
await asyncio.Future()
try:
robot_control: RobotControl = RobotControl()
commands_status: FormResponse = FormResponse
async with serve(
handler=partial(robot_control_gpio, robot_control=robot_control, commands_status=commands_status),
host=gethostbyname(settings.websocket_host),
port=settings.websocket_port
):
# бесконечный цикл
await asyncio.Future()
except asyncio.CancelledError:
if robot_control is not None:
robot_control.blinking_off()
robot_control.close()
# пробрасываем CancelledError, чтобы asyncio.run() всё корректно закрыл
raise
def run_app() -> None: