22import base64
33import json
44import threading
5+ import sys
56import rclpy
7+ from rclpy .node import Node
8+ from rclpy .executors import MultiThreadedExecutor
9+ from sensor_msgs .msg import Image
10+ from cv_bridge import CvBridge
11+
612from gui_interfaces .general .measuring_threading_gui import MeasuringThreadingGUI
713from console_interfaces .general .console import start_console
8- from hal_interfaces .general .odometry import OdometryNode
914from lap import Lap
15+ from hal_interfaces .general .odometry import OdometryNode
1016
11- import sys
1217
13- sys .path .insert (0 , "/RoboticsApplicationManager" )
18+ class WebGUINode (Node ):
19+ def __init__ (self , gui_instance ):
20+ super ().__init__ ("webgui_end_to_end" )
21+ self .gui = gui_instance
22+ self .bridge = CvBridge ()
1423
15- from robotics_application_manager import LogManager
24+ self . create_subscription ( Image , "/webgui/image" , self . img_cb , 10 )
1625
17- # Graphical User Interface Class
26+ def img_cb (self , msg ):
27+ try :
28+ cv_image = self .bridge .imgmsg_to_cv2 (msg , "bgr8" )
29+ with self .gui .image_show_lock :
30+ self .gui .image_to_be_shown = cv_image
31+ self .gui .image_to_be_shown_updated = True
32+ except Exception :
33+ pass
1834
1935
2036class WebGUI (MeasuringThreadingGUI ):
2137 def __init__ (self , host = "ws://127.0.0.1:2303" ):
2238 super ().__init__ (host )
2339
40+ if not rclpy .ok ():
41+ rclpy .init ()
42+
2443 self .image_to_be_shown = None
2544 self .image_to_be_shown_updated = False
2645 self .image_show_lock = threading .Lock ()
2746
28- # Payload vars
2947 self .payload = {"image" : "" , "lap" : "" , "map" : "" }
30- # TODO: maybe move this to HAL and have it be hybrid
31- self .pose3d_object = OdometryNode ("/odom" )
32- executor = rclpy .executors .MultiThreadedExecutor ()
33- executor .add_node (self .pose3d_object )
34- executor_thread = threading .Thread (target = executor .spin , daemon = True )
35- executor_thread .start ()
36- self .lap = Lap (self .pose3d_object )
48+
49+ self .ros_node = WebGUINode (self )
50+ self .odom_node = OdometryNode ("/odom" , "webgui_odometry" )
51+
52+ self .executor = MultiThreadedExecutor ()
53+ self .executor .add_node (self .ros_node )
54+ self .executor .add_node (self .odom_node )
55+
56+ self .executor_thread = threading .Thread (target = self .executor .spin , daemon = True )
57+ self .executor_thread .start ()
58+
59+ self .lap = Lap (self .odom_node )
3760
3861 self .start ()
3962
40- # Process incoming messages to the GUI
4163 def gui_in_thread (self , ws , message ):
42-
43- # In this case, incoming msgs can only be acks
4464 if "ack" in message :
4565 with self .ack_lock :
4666 self .ack = True
@@ -51,55 +71,46 @@ def gui_in_thread(self, ws, message):
5171 self .lap .unpause ()
5272 elif "pause" in message :
5373 self .lap .pause ()
54- else :
55- LogManager .logger .error ("Unsupported msg" )
5674
57- # Prepares and sends a map to the websocket server
5875 def update_gui (self ):
59-
6076 payload = self .payloadImage ()
6177 self .payload ["image" ] = json .dumps (payload )
6278
63- # Payload Lap Message
6479 lapped = self .lap .check_threshold ()
6580 self .payload ["lap" ] = ""
6681 if lapped is not None :
6782 self .payload ["lap" ] = str (lapped )
6883
69- # Payload Map Message
70- pose = self .pose3d_object .getPose3d ()
84+ pose = self .odom_node .getPose3d ()
7185 pos_message = str ((pose .x , pose .y ))
7286 self .payload ["map" ] = pos_message
7387
7488 message = json .dumps (self .payload )
7589 self .send_to_client (message )
7690
77- # Function to prepare image payload
78- # Encodes the image as a JSON string and sends through the WS
7991 def payloadImage (self ):
8092 with self .image_show_lock :
8193 image_to_be_shown_updated = self .image_to_be_shown_updated
8294 image_to_be_shown = self .image_to_be_shown
8395
84- image = image_to_be_shown
8596 payload = {"image" : "" , "shape" : "" }
8697
8798 if not image_to_be_shown_updated :
8899 return payload
89100
90- shape = image .shape
91- frame = cv2 .imencode (".JPEG" , image )[1 ]
92- encoded_image = base64 .b64encode (frame )
101+ if image_to_be_shown is not None :
102+ shape = image_to_be_shown .shape
103+ frame = cv2 .imencode (".JPEG" , image_to_be_shown )[1 ]
104+ encoded_image = base64 .b64encode (frame )
93105
94- payload ["image" ] = encoded_image .decode ("utf-8" )
95- payload ["shape" ] = shape
106+ payload ["image" ] = encoded_image .decode ("utf-8" )
107+ payload ["shape" ] = shape
96108
97109 with self .image_show_lock :
98110 self .image_to_be_shown_updated = False
99111
100112 return payload
101113
102- # Function for student to call
103114 def showImage (self , image ):
104115 with self .image_show_lock :
105116 self .image_to_be_shown = image
@@ -108,11 +119,8 @@ def showImage(self, image):
108119
109120host = "ws://127.0.0.1:2303"
110121gui = WebGUI (host )
111-
112- # Redirect the console
113122start_console ()
114123
115124
116- # Expose to the user
117125def showImage (image ):
118126 gui .showImage (image )
0 commit comments