Skip to content

Commit 0c640c5

Browse files
refac: code refactor and example improvements
1 parent 6d390f2 commit 0c640c5

File tree

2 files changed

+165
-174
lines changed

2 files changed

+165
-174
lines changed

avisengine.py

Lines changed: 91 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -14,89 +14,64 @@
1414
__email__ = "[email protected]"
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

Comments
 (0)