Skip to content

Commit 4ad0d5e

Browse files
authored
Merge pull request #3716 from JdeRobot/ros2-direct-car-junction
Ros2 direct car junction
2 parents 3c2d3bc + 25692c2 commit 4ad0d5e

1 file changed

Lines changed: 76 additions & 28 deletions

File tree

  • exercises/car_junction/python_template

exercises/car_junction/python_template/WebGUI.py

Lines changed: 76 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,60 +2,108 @@
22
import cv2
33
import base64
44
import threading
5-
import time
6-
import numpy as np
5+
6+
import rclpy
7+
from rclpy.node import Node
8+
from rclpy.qos import QoSProfile, DurabilityPolicy
9+
from sensor_msgs.msg import Image
10+
from cv_bridge import CvBridge
11+
from rclpy.executors import SingleThreadedExecutor
12+
713
from gui_interfaces.general.measuring_threading_gui_harmonic import (
814
MeasuringThreadingGUI,
915
)
1016
from console_interfaces.general.console import start_console
1117

1218

19+
class WebGUINode(Node):
20+
def __init__(self, gui_instance):
21+
super().__init__("webgui_car_junction")
22+
self.gui = gui_instance
23+
self.bridge = CvBridge()
24+
25+
qos_profile = QoSProfile(depth=1, durability=DurabilityPolicy.TRANSIENT_LOCAL)
26+
self.create_subscription(Image, "/webgui/image", self.img_cb, qos_profile)
27+
28+
def img_cb(self, msg):
29+
try:
30+
cv_image = self.bridge.imgmsg_to_cv2(msg, desired_encoding="passthrough")
31+
self.gui.set_incoming_image(cv_image)
32+
except Exception:
33+
pass
34+
35+
1336
class WebGUI(MeasuringThreadingGUI):
1437
def __init__(self, host="ws://127.0.0.1:2303"):
1538
super().__init__(host)
1639

17-
self.image_to_be_shown = None
18-
self.image_to_be_shown_updated = False
19-
self.image_show_lock = threading.Lock()
40+
if not rclpy.ok():
41+
rclpy.init()
2042

43+
self.incoming_image = None
44+
self.incoming_lock = threading.Lock()
45+
46+
self.encoded_payload = ""
47+
self.payload_lock = threading.Lock()
2148
self.payload = {"image": ""}
2249

23-
self.start()
50+
self.ros_node = WebGUINode(self)
51+
self.executor = SingleThreadedExecutor()
52+
self.executor.add_node(self.ros_node)
53+
54+
self.executor_thread = threading.Thread(target=self.executor.spin, daemon=True)
55+
self.executor_thread.start()
2456

25-
def payloadImage(self):
26-
with self.image_show_lock:
27-
image_to_be_shown_updated = self.image_to_be_shown_updated
28-
image_to_be_shown = self.image_to_be_shown
57+
self.unified_thread = threading.Thread(
58+
target=self._unified_image_loop, daemon=True
59+
)
60+
self.unified_thread.start()
2961

30-
payload = {"image": "", "shape": ""}
62+
self.start()
3163

32-
if not image_to_be_shown_updated or image_to_be_shown is None:
33-
return payload
64+
def set_incoming_image(self, image):
65+
with self.incoming_lock:
66+
self.incoming_image = image
3467

35-
shape = image_to_be_shown.shape
36-
frame = cv2.imencode(".JPEG", image_to_be_shown)[1]
37-
encoded_image = base64.b64encode(frame)
68+
def _unified_image_loop(self):
69+
while True:
70+
try:
71+
with self.incoming_lock:
72+
image = self.incoming_image
73+
self.incoming_image = None
3874

39-
payload["image"] = encoded_image.decode("utf-8")
40-
payload["shape"] = shape
75+
if image is not None:
76+
h, w = image.shape[:2]
77+
if w > 640 or h > 480:
78+
image = cv2.resize(image, (640, 480))
4179

42-
with self.image_show_lock:
43-
self.image_to_be_shown_updated = False
80+
_, frame = cv2.imencode(
81+
".JPEG", image, [int(cv2.IMWRITE_JPEG_QUALITY), 60]
82+
)
83+
encoded = base64.b64encode(frame).decode("utf-8")
4484

45-
return payload
85+
with self.payload_lock:
86+
self.encoded_payload = encoded
4687

47-
def update_gui(self):
48-
payload = self.payloadImage()
49-
self.payload["image"] = json.dumps(payload)
88+
threading.Event().wait(0.033)
89+
except Exception:
90+
threading.Event().wait(1.0)
5091

92+
def update_gui(self):
93+
with self.payload_lock:
94+
if not self.encoded_payload:
95+
return
96+
current_payload = self.encoded_payload
97+
self.encoded_payload = ""
98+
99+
self.payload["image"] = json.dumps(
100+
{"image": current_payload, "shape": [480, 640, 3]}
101+
)
51102
message = json.dumps(self.payload)
52103
self.send_to_client(message)
53104

54105
def setImage(self, image):
55-
"""Single point of entry for all images"""
56-
with self.image_show_lock:
57-
self.image_to_be_shown = image
58-
self.image_to_be_shown_updated = True
106+
self.set_incoming_image(image)
59107

60108

61109
host = "ws://127.0.0.1:2303"

0 commit comments

Comments
 (0)