diff --git a/workspace/src/application_pkg/application_pkg/web_node.py b/workspace/src/application_pkg/application_pkg/web_node.py
index 7cea9b7..24a13f8 100755
--- a/workspace/src/application_pkg/application_pkg/web_node.py
+++ b/workspace/src/application_pkg/application_pkg/web_node.py
@@ -1,138 +1,88 @@
#!/usr/bin/env python3
import json
+import asyncio
+import threading
import socket
-import select
-import socketserver
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
-from http.server import HTTPServer, BaseHTTPRequestHandler
-
-_mode_publisher = None
-
-
-class _APIHandler(BaseHTTPRequestHandler):
- def do_GET(self):
- self.send_response(404)
- self.end_headers()
-
- def do_POST(self):
- path = self.path.split('?')[0]
- if path == '/api/mode/switch':
- self._handle_mode_switch()
- elif path == '/api/laser':
- self._handle_laser()
- else:
- self.send_response(404)
- self.end_headers()
-
- def _handle_mode_switch(self):
- content_length = int(self.headers.get('Content-Length', 0))
- body = self.rfile.read(content_length) if content_length > 0 else b'{}'
- try:
- data = json.loads(body)
- mode = data.get('mode', '')
- except json.JSONDecodeError:
- self._json_response(400, {'code': -1, 'message': 'invalid json'})
- return
-
- valid_modes = ('auto', 'manual', 'debug')
- if mode not in valid_modes:
- self._json_response(400, {
- 'code': -1,
- 'message': f'unknown mode: {mode}',
- })
- return
-
- if _mode_publisher is not None:
- msg = String()
- msg.data = mode
- _mode_publisher.publish(msg)
-
- self._json_response(200, {'code': 0, 'message': 'ok'})
-
- def _handle_laser(self):
- content_length = int(self.headers.get('Content-Length', 0))
- body = self.rfile.read(content_length) if content_length > 0 else b'{}'
- try:
- data = json.loads(body)
- action = data.get('action', '')
- except json.JSONDecodeError:
- self._json_response(400, {'code': -1, 'message': 'invalid json'})
- return
-
- if action == 'on':
- mode = 'laser_on'
- elif action == 'off':
- mode = 'laser_off'
- else:
- self._json_response(400, {
- 'code': -1,
- 'message': 'action must be on or off',
- })
- return
-
- if _mode_publisher is not None:
- msg = String()
- msg.data = mode
- _mode_publisher.publish(msg)
-
- self._json_response(200, {'code': 0, 'message': 'ok'})
-
- def _json_response(self, status, data):
- self.send_response(status)
- self.send_header('Content-type', 'application/json')
- self.end_headers()
- self.wfile.write(json.dumps(data).encode())
-
- def handle_error(self, request, client_address):
- pass
-
- def log_message(self, format, *args):
- pass
-
-
-class _ThreadingHTTPServer(socketserver.ThreadingMixIn, HTTPServer):
- daemon_threads = True
+from microdot import Microdot
class WebNode(Node):
def __init__(self):
super().__init__('web_node')
- global _mode_publisher
self.declare_parameter('port', 8080)
- port = self.get_parameter('port').value
+ self._port = self.get_parameter('port').value
self._mode_publisher = self.create_publisher(
String, '/gimbal/mode', 10
)
- _mode_publisher = self._mode_publisher
- self._server = _ThreadingHTTPServer(('', port), _APIHandler)
- self._server.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- self._server.socket.setblocking(False)
- self._server.timeout = 0
+ self._app = Microdot()
+ self._setup_routes()
- self._timer = self.create_timer(0.01, self._handle_http_requests)
+ self._thread = threading.Thread(target=self._run_server, daemon=True)
+ self._thread.start()
hostname = socket.gethostname()
ip = socket.gethostbyname(hostname)
- self.get_logger().info(
- f'API server on http://{ip}:{port}'
- )
+ self.get_logger().info(f'API server on http://{ip}:{self._port}')
- def _handle_http_requests(self):
- for _ in range(20):
+ def _setup_routes(self):
+ app = self._app
+
+ @app.route('/api/mode/switch', methods=['POST'])
+ async def mode_switch(request):
try:
- self._server.handle_request()
+ data = request.json
except Exception:
- break
+ return {'code': -1, 'message': 'invalid json'}, 400
+
+ mode = data.get('mode', '')
+ valid_modes = ('auto', 'manual', 'debug')
+ if mode not in valid_modes:
+ return {'code': -1, 'message': f'unknown mode: {mode}'}, 400
+
+ msg = String()
+ msg.data = mode
+ self._mode_publisher.publish(msg)
+ return {'code': 0, 'message': 'ok'}
+
+ @app.route('/api/laser', methods=['POST'])
+ async def laser(request):
+ try:
+ data = request.json
+ except Exception:
+ return {'code': -1, 'message': 'invalid json'}, 400
+
+ action = data.get('action', '')
+ if action == 'on':
+ mode = 'laser_on'
+ elif action == 'off':
+ mode = 'laser_off'
+ else:
+ return {'code': -1, 'message': 'action must be on or off'}, 400
+
+ msg = String()
+ msg.data = mode
+ self._mode_publisher.publish(msg)
+ return {'code': 0, 'message': 'ok'}
+
+ def _run_server(self):
+ try:
+ self._app.run(host='0.0.0.0', port=self._port, debug=False)
+ except RuntimeError:
+ pass
def destroy_node(self):
- self._server.server_close()
+ try:
+ self._app.shutdown()
+ except Exception:
+ pass
super().destroy_node()
diff --git a/workspace/src/application_pkg/package.xml b/workspace/src/application_pkg/package.xml
index e56ce9e..8f71e81 100755
--- a/workspace/src/application_pkg/package.xml
+++ b/workspace/src/application_pkg/package.xml
@@ -9,6 +9,7 @@
rclpy
std_msgs
+ python3-microdot
ament_copyright
ament_flake8
diff --git a/workspace/src/application_pkg/setup.py b/workspace/src/application_pkg/setup.py
index 587e4a2..d85eb3e 100755
--- a/workspace/src/application_pkg/setup.py
+++ b/workspace/src/application_pkg/setup.py
@@ -11,7 +11,7 @@ setup(
['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
],
- install_requires=['setuptools'],
+ install_requires=['setuptools', 'microdot'],
zip_safe=True,
maintainer='dev01',
maintainer_email='dev01@todo.todo',