Skip to content

Commit

Permalink
Merge pull request #21 from poppy-project/api_python_scp
Browse files Browse the repository at this point in the history
Exécution d'un programme python via `scp` 
Voir #11 pour le scénario
  • Loading branch information
EtienneSchmitz authored Oct 18, 2023
2 parents 084adac + 835b17f commit 60c481c
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 25 deletions.
2 changes: 1 addition & 1 deletion api/python/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

description="Rosa robot Python API",
author="Poppy Station",
url="https://github.com/poppy-project",
url="https://github.com/poppy-project/ucia/",

include_package_data=True,
packages=find_packages(),
Expand Down
12 changes: 10 additions & 2 deletions rpi/manager/thymio/manager.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import logging
import time

from tasks.thymio.follow_line import FollowLine
from tasks.thymio.object_collector import ObjectCollector
# from tasks.thymio.follow_line import FollowLine
# from tasks.thymio.object_collector import ObjectCollector
from tasks.thymio.photographer import Photographer
from tasks.api import API

from manager.base import BaseManager
from controller.thymio.controller import ThymioController
import settings

class ThymioManager(BaseManager):
current_mode = 0
Expand All @@ -17,6 +19,8 @@ def __init__(self):
self.controller = ThymioController()
self.logger = logging.getLogger(__name__)

self.api = API(self.controller)

self.tasks = [
# ObjectCollector(self.controller),
# FollowLine(self.controller),
Expand Down Expand Up @@ -48,6 +52,10 @@ def change_mode(self):
self.logger.info(f"Mode changed (BACK) - Previous mode was {previous_mode}, new mode is {self.current_mode}.")

def run(self):
self.logger.debug(f"Actual mode {settings.status}")
if settings.status != settings.RobotState.MODE:
return

self.change_mode()

self.tasks[self.current_mode].run()
Expand Down
11 changes: 11 additions & 0 deletions rpi/settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from enum import Enum

class RobotState(Enum):
API=0
MODE=1

status: RobotState = RobotState.MODE

def set_status(new_status: RobotState):
global status
status = new_status
41 changes: 27 additions & 14 deletions rpi/sockets/input_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,49 +2,62 @@
import asyncio
import websockets
import json
import settings

from controller.base import BaseController

class InputOuputServer():
def __init__(self, controller: BaseController):
self.robot = controller
self.connected = set()
self.client = None
self.logger = logging.getLogger(__name__)


async def send_to_all_clients(self):
async def send_state(self):
while True:
await asyncio.sleep(1) # Send a message every 5 seconds
self.logger.debug("Sending state to all client.")
for websocket in self.connected:
await asyncio.sleep(1) # Send a message every seconds
if self.client:
self.logger.debug("Sending state to client.")
try:
await websocket.send(json.dumps(self.robot.get_all_state()))
await self.client.send(json.dumps(self.robot.get_all_state()))
except:
pass # The client may have disconnected, ignore the exception
self.logger.debug("Failed to send state to client.")
self.client = None


async def handler(self, websocket, path):
if self.client:
self.logger.debug("Another client is already connected. Closing the connection.")
return

settings.status = settings.RobotState.API
self.client = websocket
self.logger.debug("New connection added.")
self.connected.add(websocket)
try:
async for message in websocket:
self.logger.debug(f"Receive commands :", message)
while True: # Loop to handle multiple messages and timeouts
try:
message = await asyncio.wait_for(websocket.recv(), timeout=10.0) # Wait for a message for up to 10 seconds
except asyncio.TimeoutError:
self.logger.debug("Timeout, no message received for 10 seconds.")
break # Break the loop and remove the connection on timeout

self.logger.debug(f"Receive commands: {message}")
self.robot.process_incoming_commands(json.loads(message))
except websockets.ConnectionClosedError:
self.logger.debug("Connection closed without close frame.")
except Exception as e:
self.logger.error(f"An error occurred: {e}")
finally:
self.connected.remove(websocket)
self.client = None
self.logger.debug("Connection removed.")
if len(self.connected) == 0:
self.robot.reset_robot_state()
settings.status = settings.RobotState.MODE
self.robot.reset_robot_state()

def run(self):
server = websockets.serve(self.handler, '0.0.0.0', 1234)
self.loop = asyncio.get_event_loop()
self.loop.run_until_complete(server)
self.loop.create_task(self.send_to_all_clients())
self.loop.create_task(self.send_state())

def close(self):
self.loop.close()
35 changes: 27 additions & 8 deletions rpi/tasks/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,43 @@

from controller.base import BaseController
from tasks.base import Task
# from sockets.input_output import InputOuputServer

from sockets.input_output import InputOuputServer
from sockets.camera import CameraServer
import asyncio
import threading


class API(Task):
def __init__(self, controller: BaseController):
self.controller = controller
self.logger = logging.getLogger(__name__)

# self.input_output = InputOuputServer(self.controller)
# self.input_output.run()
# Création d'un thread pour la boucle d'événements asyncio.
self.loop_thread = threading.Thread(target=self.start_asyncio_loop, daemon=True)
self.loop_thread.start()

def start_asyncio_loop(self):
self.loop = asyncio.new_event_loop()
asyncio.set_event_loop(self.loop)

self.input_output = InputOuputServer(self.controller)
self.input_output.run()

# self.camera_server = CameraServer()
# self.camera_server.run()
self.camera_server = CameraServer()
self.camera_server.run()

# asyncio.get_event_loop().run_forever()
self.loop.run_forever()

def run(self):
self.logger.info("API")

def close(self):
# self.input_output.close()
pass
# Arrêtez la boucle d'événements asyncio depuis le thread principal.
self.loop.call_soon_threadsafe(self.loop.stop)

# Attendez la fin du thread.
self.loop_thread.join()

def __exit__(self):
self.close()

0 comments on commit 60c481c

Please sign in to comment.