1414__email__ = "amir@avisengine.com"
1515
1616
17- class Car () :
17+ class Car :
1818 '''
1919 AVIS Engine Main Car class
20-
21- Attributes
22- ----------
23-
24- Public:
25- steering_value
26- speed_value
27- sensor_status
28- image_mode
29- get_Speed
30- data_arr
31- data_str
32- sock
33- image
34- sensors
35- current_speed
36- sensor_angle
3720 '''
38-
39- #Attributes to kind of replicate a Pub-sub pattern messaging to request data
40- steering_value = 0
41- speed_value = 0
42- sensor_status = 1
43- image_mode = 1
44- get_Speed = 1
45- sensor_angle = 30
46-
47- sock = socket .socket (socket .AF_INET , socket .SOCK_STREAM )
48-
49- #Data format for request
50- data_arr = [speed_value , steering_value , image_mode , sensor_status , get_Speed , sensor_angle ]
51- _data_format = "Speed:{},Steering:{},ImageStatus:{},SensorStatus:{},GetSpeed:{},SensorAngle:{}"
52- data_str = _data_format .format (data_arr [0 ], data_arr [1 ], data_arr [2 ], data_arr [3 ], data_arr [4 ], data_arr [5 ])
53-
54- image = None
55- sensors = None
56- current_speed = None
57-
58- def connect (self ,server ,port ):
21+ def __init__ (self ) -> None :
22+ # Instance variables for car state
23+ self .steering_value : int = 0
24+ self .speed_value : int = 0
25+ self .sensor_status : int = 1
26+ self .image_mode : int = 1
27+ self .get_Speed : int = 1
28+ self .sensor_angle : int = 30
29+ self .sock : socket .socket = socket .socket (socket .AF_INET , socket .SOCK_STREAM )
30+ self ._data_format : str = "Speed:{},Steering:{},ImageStatus:{},SensorStatus:{},GetSpeed:{},SensorAngle:{}"
31+ self .data_str : str = self ._data_format .format (
32+ self .speed_value , self .steering_value , self .image_mode , self .sensor_status , self .get_Speed , self .sensor_angle
33+ )
34+ self .image = None
35+ self .sensors = None
36+ self .current_speed = None
37+ self ._is_initialized = False
38+ self ._init_frames_needed = 4 # Number of frames needed for stabilization
39+
40+ def connect (self , server : str , port : int ) -> bool :
5941 '''
6042 Connecting to the simulator (server)
6143 '''
6244 try :
6345 self .sock .connect ((server , port ))
6446 self .sock .settimeout (5.0 )
65-
66- print ("connected to " , server , port )
47+ print (f"connected to { server } { port } " )
48+ # Give simulator time to initialize
49+ time .sleep (3 )
50+ # Request initial data frames for stabilization
51+ for _ in range (self ._init_frames_needed ):
52+ self .getData ()
53+ self ._is_initialized = True
6754 return True
68- except :
69- print ("Failed to connect to " , server , port )
55+ except Exception as e :
56+ print (f "Failed to connect to { server } { port } : { e } " )
7057 return False
7158
72-
73- def recvall (self , socket ):
59+ def recvall (self , sock : socket .socket ) -> str :
7460 '''
7561 Function to receive all the data chunks
7662 '''
7763 BUFFER_SIZE = 131072 # Increased buffer size for better performance
78- data = bytearray () # Use a bytearray for better performance
79-
64+ data = bytearray ()
8065 while True :
81- part = socket .recv (BUFFER_SIZE )
66+ part = sock .recv (BUFFER_SIZE )
8267 data .extend (part )
83-
84- # Use KMP search to find the <EOF>, KMPSearch() returns -1 if the pattern was not found
85- # It is 9 times faster than the simple python search
86- if utils .KMPSearch (b"<EOF>" , data ) > - 1 : # Convert "<EOF>" to bytes
68+ if utils .KMPSearch (b"<EOF>" , data ) > - 1 :
8769 break
88-
8970 return data .decode ("utf-8" )
9071
91-
92- def setSteering (self ,steering ):
72+ def setSteering (self , steering : int ) -> None :
9373 '''
9474 Setting the steering of the car
95-
96- Parameters
97- ----------
98- steering : int
99- Steering value in degree
10075 '''
10176 self .steering_value = steering
10277 self .image_mode = 0
@@ -105,13 +80,9 @@ def setSteering(self,steering):
10580 self .sock .sendall (self .data_str .encode ("utf-8" ))
10681 time .sleep (0.01 )
10782
108- def setSpeed (self ,speed ) :
83+ def setSpeed (self , speed : int ) -> None :
10984 '''
11085 Setting the speed of the car
111-
112- Parameters
113- ----------
114- speed : int
11586 '''
11687 self .speed_value = speed
11788 self .image_mode = 0
@@ -120,61 +91,49 @@ def setSpeed(self,speed):
12091 self .sock .sendall (self .data_str .encode ("utf-8" ))
12192 time .sleep (0.01 )
12293
123- def setSensorAngle (self , angle ) :
94+ def setSensorAngle (self , angle : int ) -> None :
12495 '''
12596 Setting the angle between each sensor ray
126-
127- Parameters
128- ----------
129- angle : int
130- In degrees
13197 '''
132-
13398 self .image_mode = 0
13499 self .sensor_status = 0
135100 self .sensor_angle = angle
136101 self .updateData ()
137102 self .sock .sendall (self .data_str .encode ("utf-8" ))
138-
139- def getData (self ):
103+
104+ def getData (self ) -> None :
140105 '''
141106 Requesting for the data from the simulator
142107 '''
143108 self .image_mode = 1
144109 self .sensor_status = 1
145110 self .updateData ()
146111 self .sock .sendall (self .data_str .encode ("utf-8" ))
147-
148112 receive = self .recvall (self .sock )
149-
150113 imageTagCheck = re .search ('<image>(.*?)<\/image>' , receive )
151114 sensorTagCheck = re .search ('<sensor>(.*?)<\/sensor>' , receive )
152- speedTagCheck = re .search ('<speed>(.*?)<\/speed>' , receive )
153-
115+ speedTagCheck = re .search ('<speed>(.*?)<\/speed>' , receive )
154116 try :
155- if ( imageTagCheck ) :
117+ if imageTagCheck :
156118 imageData = imageTagCheck .group (1 )
157119 im_bytes = base64 .b64decode (imageData )
158- im_arr = np .frombuffer (im_bytes , dtype = np .uint8 ) # im_arr is one-dim Numpy array
120+ im_arr = np .frombuffer (im_bytes , dtype = np .uint8 )
159121 imageOpenCV = cv2 .imdecode (im_arr , flags = cv2 .IMREAD_COLOR )
160122 self .image = imageOpenCV
161-
162- if (sensorTagCheck ):
123+ if sensorTagCheck :
163124 sensorData = sensorTagCheck .group (1 )
164125 sensor_arr = re .findall ("\d+" , sensorData )
165- sensor_int_arr = list (map (int , sensor_arr ))
126+ sensor_int_arr = list (map (int , sensor_arr ))
166127 self .sensors = sensor_int_arr
167128 else :
168- self .sensors = [1500 ,1500 ,1500 ]
169-
170- if (speedTagCheck ):
129+ self .sensors = [1500 , 1500 , 1500 ]
130+ if speedTagCheck :
171131 current_sp = speedTagCheck .group (1 )
172132 self .current_speed = int (current_sp )
173133 else :
174134 self .current_speed = 0
175- except :
176- print ("Failed to receive data" )
177-
135+ except Exception as e :
136+ print (f"Failed to receive data: { e } " )
178137
179138 def getImage (self ):
180139 '''
@@ -185,36 +144,59 @@ def getImage(self):
185144 def getSensors (self ):
186145 '''
187146 Returns the sensor data
188- A List:
189- [Left Sensor: int, Middle Sensor: int, Right Sensor: int]
190147 '''
191148 return self .sensors
192-
149+
193150 def getSpeed (self ):
194151 '''
195152 Returns the speed of the car
196153 '''
197154 return self .current_speed
198-
199- def updateData (self ):
155+
156+ def updateData (self ) -> None :
200157 '''
201158 Updating the request data array and data string
202159 '''
203- data = [self .speed_value ,self .steering_value ,self .image_mode ,self .sensor_status ,self .get_Speed , self .sensor_angle ]
204- self .data_str = self ._data_format .format (data [ 0 ], data [ 1 ], data [ 2 ], data [ 3 ], data [ 4 ], data [ 5 ] )
205-
206- def stop (self ):
160+ data = [self .speed_value , self .steering_value , self .image_mode , self .sensor_status , self .get_Speed , self .sensor_angle ]
161+ self .data_str = self ._data_format .format (* data )
162+
163+ def stop (self ) -> None :
207164 '''
208- Stoping the car and closing the socket
165+ Stopping the car and closing the socket
209166 '''
210- self .setSpeed (0 )
211- self .setSteering (0 )
212- self .sock .sendall ("stop" .encode ("utf-8" ))
213- self .sock .close ()
214- print ("Process stopped successfully!" )
215-
216- def __del__ (self ):
217- self .stop ()
218-
219-
167+ try :
168+ self .setSpeed (0 )
169+ self .setSteering (0 )
170+ self .sock .sendall ("stop" .encode ("utf-8" ))
171+ self .sock .close ()
172+ print ("Process stopped successfully!" )
173+ except Exception as e :
174+ print (f"Error during stop: { e } " )
175+
176+ def is_ready (self ) -> bool :
177+ '''
178+ Check if the car is initialized and ready for operation
220179
180+ Returns
181+ -------
182+ bool: True if the car is initialized and ready for operation
183+ '''
184+ return self ._is_initialized
185+
186+ def __enter__ (self ):
187+ '''Enable use as a context manager.'''
188+ return self
189+
190+ def __exit__ (self , exc_type , exc_val , exc_tb ):
191+ '''Ensure resources are cleaned up when exiting context.'''
192+ self .stop ()
193+ return False # Do not suppress exceptions
194+
195+ def __del__ (self ):
196+ try :
197+ self .stop ()
198+ except Exception :
199+ pass
200+
201+
202+
0 commit comments