-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMainer.py
More file actions
361 lines (288 loc) · 24.9 KB
/
Mainer.py
File metadata and controls
361 lines (288 loc) · 24.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
import time #An import that is Used for calculating time
import numpy as np #An import for calculating The camera Speed(Saved as np)
import cv2 #An import for controling the camera feed
import pyrealsense2 as rs #An import for calibrating the realsense camera and using the feed (Saved as rs)
import socket #Used for communicating back and forth with the Esp32
import threading #The import is used for Preforming multiple tasks in the same time
from flask import Flask, Response, render_template_string #It takes from the flask import oarts to controll the Self hosted flask website
# ==-- Initial Wifi + Socket --==
host_ip = '0.0.0.0' #The Ras-Pi 4b Ip(Our IP)
EspIP = '192.168.13.100' # Will be updated from user input
EspInputPort = EIP = 6010 #It Asighnes An input port for the Esp32 (In the Ras-Pi)
EspOutputPort = EOP = 6020 #IT assinghes An Output Port for the Esp32 (In the Ras-Pi)
GetConnectionFromEsp = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #Establishes an Connection socket input and Output socket using the AF_init protocol(Ipv4), and socket stream, to start and stop
OutputToEsp = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #Establishes with the ESP32 an 2 way socket using (Ipv4) throguh AF_init protocol, and socket stream to comunicate with the esp
InputFromEsp = None #Declares that there isn't any input from the ESP32 socket
# ==-- Variable Decleration --==
CALIB = 0.01 #It shrinks the Value by 1/100(to make the pixle motion more realistic)
ROI = (100, 100, 200, 200) #Declares the futer Reagon of itrest to be dimentions (x,y,width,height)(x,y,רוחב,גובה) Pixels/Image
fb_params = dict(pyr_scale=0.5, levels=3, winsize=15, iterations=3, poly_n=5, poly_sigma=1.2, flags=0) #Calib Paramaters- Only half of the omage,levals is for levals of manitude of zoom ,WindSide is 15x15 pixels ,Iteration is for it to have 3 tries ,poly + polysigma is for smothing ,defaulte = 0
TimeInc = 0.25 #Time increment (place Holder) in seconds
StopExper = False #An Var why To stop the exerminet
KillSwitch = False #Imedatly stops/Kills the experiment
ExperRequested = None #To start the Experiment
Duration = None #How much time will the expirament have
MaxExp = 60*30 #Maxuimum Duration
LAT = [] #List of Avarege Tempeturee
LPR = [] #List of PRessure
LWSPd = [] #The Delata Speed
LWSPB = [] #a List for the Speed Of Before
LWSC = [] #a List of the Speed that the Camera detecs
LAH = [] # A List for the Avarege Humidity
Stable = [] #It Asigns when will the first time that i would detect that the Wind is Stable
TimeIncCalcL = [] #It will be used to calc the Time Incrments, and the deltas in the time
# ==-- Camera Class --==
class SharedRealSenseStream: #זה יוצר מחלקה שלהיא מגדירה את הפרמטרים של המצלמה
def __init__(self): #זה הבנאי של המצלמה שמגדיר משתנים של המחלקה
self.Frame = None #A var to Save the Last frame
self.Lock = threading.Lock() #Locds that cirten thred so only 1 thred could acess the camera feed
self.Pipeline = rs.pipeline() #It Creates a Pipe Line(אובייקט שהספריה של המצלמה יכולה לעשות שינויים)
self.Config = rs.config() #It will be used to connfigure the stuf later
self.Config.enable_stream(rs.stream.color, 640, 480, rs.format.bgr8, 60) #Connfigers the camera to be with couleer, 640x480 ratio (frames in 480p), in a format for OpenCv, and with 60 FPS
self.Pipeline.start(self.Config) #It starts the pipeline With the config from earlier
self.Running = True #A Var that Can Stop/start the Camera
def StartCaptureLoop(self): #הפונקציה קולטת את התצלומים
try: #It is like an if statment just it is for getting data from frames & if it fails, it will be caught as e
while self.Running == True: #While the Camera Is Running
frames = self.Pipeline.wait_for_frames() #A comand to suspend the Camera So it could load another frame
color_frame = frames.get_color_frame() #It gets the colur Data from the frame
if color_frame != None: #An if statment to varify that it got colour from the last frame
frame = np.asanyarray(color_frame.get_data()) #Turens that in to a Numpy array data struckert(פורמט) so other lybareris could work with the data
with self.Lock: #It Locks the thred (Gives it asingle thred) (לסדר את זה לדבר יחיד בלי בלגן)
self.Frame = frame #Updates to the latest Frame
time.sleep(0.01) #It gives it alitle bit of time to refresh
except Exception as e: #Finds the Exepetion and stors the Exeption as e
print(f"Error RealSense camera thread failed: {e}") #It presents a Camera Thred Error + the reason
global StopExper, KillSwitch #It Makes the Variables listed Global(Being able to read and Write)
StopExper = True #The var is True, and it stops the experiment
KillSwitch = True #Asginigning True to the kill switch Variable(To kill the experimaent)
finally: #It Just works after the try and excepet like they were not there
self.Pipeline.stop() #It stops the pipeline compleatly
def GetLatestFrame(self): #הפונקציה מעדכנת את התמונה\פריים אחרון
with self.Lock: #It Locks the thred (Gives it asingle thred) (לסדר את זה לדבר יחיד בלי בלגן)
if self.Frame is not None: #If there is data in the farme
return self.Frame.copy() #It returns a coppy of the same frame
else: #If there isn't any data in the frame
return None # Returns None (Null/nothing)
def Stop(self): #הפונקציה מפיסקה את התוכנה
self.Running = False #It stops the Camera
self.Pipeline.stop() #It stops the Pipeline
SharedStream = SharedRealSenseStream() #It starts the Camera Class
# ==-- Site Block --==
app = Flask(__name__) #It starts the Flask Part and interaction with the website
#It Creates an HTML Page
Site= """
<!DOCTYPE html>
<html lang="en"> <!-- It conifguirs the html site's language as English -->
<head> <!--Configurs data so the site could work properly, and have Better in the Head Part -->
<meta charset="UTF-8"> <!-- It Configurs the Chares(Each LEtter) to be apart of the UTF-8 Format -->
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- -->
<title>Roy and Nivs Camera Feed </title> <!-- It gives it Internal Data And the web name of the site-->
</head> <!-- It closes the Head Devider -->
<body> <!-- It Starts the Body of the file and the stuff that would be shown -->
<div> <!-- Opens a divider For The shown File Name -->
<h1> <!-- It defines the bigest text size -->
<pr> Roy & Niv's Final Project Camera Site </pr> <!-- The projects name, and aparagrath -->
</h1> <!-- Closes the font size -->
</div> <!-- Closes the divider -->
<br> </br> <!-- Small brake line so there will be space between the title tand the camera feed ahead -->
<div> <!-- Main Divider -->
<h3> <!-- Smaller font For less emphesis -->
<br> <The Live Feed of our Camera:> </br> <!-- Text explaining what is hapaning -->
<img src="{{ url_for('video_feed') }}" alt="Video Feed"> <!-- Gives the sourcre of the image that will be displaied -->
</h3> <!-- Closes the font size -->
</div> <!-- Closes the divider -->
</body> <!-- End of the Body part -->
</html> <!-- The end of the site -->
"""
def generate_frames(): #הפונקציה ממירה את הפריים האחרון לאתר
while True: #While not stoped
frame = SharedStream.GetLatestFrame() #It asgings The latest frame from the fuction inside the class
if frame is None: #If the Frame Variable has any content
time.sleep(0.05) #It gives it 0.05 seconds(50 miliseconds) to wait
continue #countines to the next thing without Returning an if error
ReturnFrame, buffer = cv2.imencode('.jpg', frame) #To check if it Encoded, and Encoding the frame in to a .jpg format
if not ReturnFrame: # If It encoded it is False
continue #If it returned a frame the loop should continue
frame_bytes = buffer.tobytes() #It Turnes the .jpg file in to Pure Bites
yield (b'--frame\r\n' b'Content-Type: image/jpeg\r\n\r\n' + frame_bytes + b'\r\n') #It makes the code Become ready for sending it to a website
@app.route('/video_feed') #It makes the route to the feed to being able to transmit the frames in to the http format
def video_feed(): #הפונקציה פה יוצרת אפשרות להעלעת תמונות לאתר
return Response(generate_frames(), mimetype='multipart/x-mixed-replace; boundary=frame') #It Activates the Frame generation func, and it replaces it with the old Frame.
@app.route('/') #It makes it the route fo the main site template(how the site will be built)
def index(): #הפונקצינה יוצרת את האתר אל בסיס הHTML
return render_template_string(Site) #It makes the site acsesible in the browser(Google/FireFox/Brave or any other altenative)
def run_flask(): #זה מתחיל את השידור למקום המדוייק
app.run(host='0.0.0.0', port=5000, debug=False) #The Ip that it's runing on is '0.0.0.0', The port is 5000, and the debug fetcher is off(it Caused problemes)
# ==-- Socket & Functions --==
def GetESPIp(): #הפונקציה מקבלת מהמשתמש כתובת IP של הESP32
global EspIP #It Gives acses to read and change the EspIp adress as a global variable
time.sleep(5) #Give the it time for the flask site to load
EspIpInput = input(f"Current ESP32 IP is {EspIP}. Enter new IP or press Enter to keep: ") #Function to get the new IP from the ESP32
if EspIpInput.strip() != '': #If the Esp32 Input is Not Empty after Clearing the spaces
EspIP = EspIpInput.strip() #It changes the old Ip Addrees for the New IP addres
print(f"Using ESP32 IP: {EspIP}") #IT prints the Esp32 Ip address
def TryConnectToEspWithTimeout(timeout=180): #הפונקציה אחראית לחיבור אם הESP32
global OutputToEsp, KillSwitch, StartTime #It Makes the Variables listed Global(Being able to read and Write)
StartTime = time.time() #Assighns a start time to the Variable
connected = False #It is not connected
while (time.time() - StartTime < timeout): #While the Time - The start time is less then the Maimum Time out (הזמן שמוקצה לחיבור) is Reached
try: #It Tries(like an if without Ending the program in case of faliure) to Compleatly connect to the Esp32
OutputToEsp.settimeout(5) #IT sets 5 seconds Timer To get a connnection
OutputToEsp.connect((EspIP, EOP)) #It Connects Compleatly to the esp32 Based on the esp32 Ip and the Ras pi asghined port
connected = True #Continues The value
print("Connected to ESP32!") #Sends a massege To the user
break #Stops the While loop
except Exception as e: #Finds the Exepetion and stors the Exeption as e
print(f"Connect failed: {e}. Retrying in 3 seconds...") #Sends The Failier meseage to the user
time.sleep(3) #It gives it a 3 seconds buffer
if connected == False: #If the connection Faild
print("Connection Failed. Experiment Forfeited.") #Sends out a message to the user about falier
KillSwitch = True #It kills the expirament Compleatly
StopExper = True #Another reasurence that the expirament will be ended Proparly
return connected #It returns the Value of the Vareiable connected
def CreateConnectionWithEsp32(): #הפונקציה אחראית אל ליצור קשר סופי אם הESP32
global InputFromEsp #Declares for writing a Global Socket variable
GetConnectionFromEsp.bind((host_ip, EIP)) #It binds compleatly the Esp32 Socket for conection and it makes it listen to the Esp32, from the Users Ip
GetConnectionFromEsp.listen(1) #It makes it use only a single connection
InputFromEsp, IgnoreThisVariable_ItIs_Usless = GetConnectionFromEsp.accept() #The Variable is tied to a socket & it ignors the senders addrr (we alredy have it)
try: #It Tries to send data to the esp32
OutputToEsp.send(b'StartExper') #It sends data to the Esp32
except Exception as e: #In case of an problem it stors the Exeption as e
print(f"SendToEsp failed: {e}") #It prints the posible error to the the user
def SortMessegeByTitleFromEsp(data): #הפונקציה אחראית אל פיצול המידע המתקבל מן הESP32
global StopExper #It Makes the Stop experiment Var a Global(Being able to read and Write to it)
try: #It is a Try Block to Decode the data from the Esp32
parts = data.decode('utf-8').split(',') #It splits the data in to the utf-8 format, and it splits the data(parts) from the Esp32
if parts[0] == "Stop": #If the first val in the dictionnery is Stop
StopExpereriment() #Activates the Stop Expirament Function
elif parts[0] == "Data": #If the first val in the dictionnery is Data
GetData(parts) #It sends the parts in to a function to put the data indexes in to lists
else: #If The if and Elif fail
print("Unknown ESP32 message:", parts) #It prints the error, and themeassage from the esp32
except Exception as e: #Finds the Exepetion(In case there is an exception) and stors the Exeption as e
print("Error parsing ESP32 data:", e) #Prints the posible Error to the user
def ReciveDataFromEsp(): #הפונקציה אחראית אל קבלת מידע מהESP32
global StopExper, KillSwitch #It Makes the Expirament ending Variables listed Global (Being able to read and Write to)
try: #Try block to Get data from the esp32
while not StopExper and not KillSwitch: #While The experiment did not end & The Expirament was not killed
DataBndwith = InputFromEsp.recv(8192)#It makes the Data Bandwith for 8192 bites per send & it waits untill it gets data from the esp32
if not DataBndwith: #if no messaage has been recived
break #Stops the While loop
SortMessegeByTitleFromEsp(DataBndwith) #Sends the Sort function The Data from the Esp32
except Exception as e: #Finds the Exepetion(error) and it stores the Exeption in the Variable e
print(f"Error ESP32 receiver thread failed: {e}") #Sends out the Error message
StopExpereriment() #Starts a function that stops the expirmaent
def StartTheExpirament(): #הפונקציה מכינה את המערכת והתמשתמש לתחילת הניסוי
global ExperRequested, KillSwitch #It Makes the variables listed as Global Type(Being able to read from The Vars declaered earlier and being able to read and write to)
while ExperRequested != 'Y' and ExperRequested != 'y': #While the expirament was not requsted(Acsepted)
ExperRequested = input("If you wish to start the experiment\nPlease Press Y: ") #Asks again if it wants an expirament
if ExperRequested == 'n' or ExperRequested == 'N': #If an exerament was not requested
print("Are you sure you want to Quit?") #Reashurence message
ExperRequested = input("Press N again to forfeit: ") #Another option to request an expirament
if ExperRequested == 'n' or ExperRequested == 'N': #if an expirament was still not requested
KillSwitch = True #It kills the expirament
StopExper = True #Another reasurence that the expirament will be ended
break #Break the while loop
return (ExperRequested) #It returns true To the expirament beeing acepted
def GetDuration(): #הפונקציה מחליטה אל כמות הזמן שהניסוי יתרחש בתוכו
global Duration #It Makes the Duration listed Global(Being able to read from earlier and to being able write to)
MaxExp = 60*30 #Maximum Experiment is 30 Minuits (60 Sec * 30 times)
while Duration is None: #While i did not get the Duration
try: #it Starts with an atempt to get the duration from the user
Duration = float(input("Please Enter the amount of seconds you want this Experiment to last: ")) #Gets the Sconds From the user
if int(Duration) == float(Duration): #IF the duration if the durations are eaquel just in a floating point type
Duration = int(Duration) #It changs the type to be Only an intiger
else: #The floating point is not eaquell to the intiger
Duration = int(Duration * 10) // 10 #An operation & calculation to make it an Intiger
except: #In case of an error/decision to not get an duration
print("Invalid input The duration will be The maximum duration posible") #A message to the user
Duration = MaxExp #The Duration is the maximem duration posible
return (Duration) #It returns the Furation
def GetData(parts): #הפונקציה אחראית אלהכנסה לתוך רשימות במוסדרות של הנדונים המתקבלים
LAT.append(float(parts[1])) #It ads to the Avarege Temp List values to the Avarege Temp
LAH.append(float(parts[2])) #It adds a value to the Avarege Humidity list
LPR.append(float(parts[3])) #It adds a Numerical value to the Presuire list
LWSPd.append(float(parts[4])) #It adds another value to the Wind speed Delta in time
LWSPB.append(float(parts[5])) #It adds Another value to the wind speed Before/First annometers speed
def CheckStable(): #הפונקציה בודקת אם מהירות האוויר התיצבה
if len(LWSPB) >= 2: #If the length of the list of The wind speed Of the first annometter is grater then 2
SpeedOldd = LWSPB[-2] #Adds another VAriable of the old speed
SpeedNeww = LWSPB[-1] #Adds a value to the val that symbolises current/new speed
ChkSpeedDelta = abs(SpeedNeww - SpeedOldd) #It adds A delta(Diffarence) calculation in absoulout(ארך מוחלט) Value
if ChkSpeedDelta <= 0.5: #if the delta is less then 0.5 m/sec
Stable.append(len(LWSPB)) #if the diffarence is eaquell or less then 0.5 m/sec(מטרים בשניה) It adds a value to a stable list
return True #It returns True (The speed is Stable)
return False #if it is not eauqell or less then 0.5 m/s then it returns False (The wind is Not stable)
return False #if it is not eauqell or less then 0.5 m/s it returns False (The wind is Not stable)
def StopExpereriment(): #הפונקציה מוודאת שהניסוי נגמר
global KillSwitch, StopExper, LAH #It Makes the Expirament ending Variables Global (Being able to Read from earlier and beeing able to Write to the vars)
KillSwitch = True #Asginigning True to the kill switch Variable(To kill the experimaent)
StopExper = True #The var is True, and it stops the experiment
print("Stopping the experiment") #To the USer it explains what happaning
OutputToEsp.send(b'StopExper') #Sends to the ESP32 as a mesage to stop the experiment
if LAH is not None: #If a randomly chousen data peramater that was sent From the ESP32 has been ressived (It came with more data) and after stabliasation
CalcculateAvareges() #It will activate the function that will caclulate and print the (Stabalised) avareges
return (KillSwitch) #Returns The Kill Switch value to the Point where it activated the Function
def camera_loop(): #הפונקציה אחראית אל חישוב המהירות המתקבלת מן המצלמה
global LWSC, StopExper, KillSwitch #It Decalres the Variables as a Global type in the func (Being able to Read & Write to the vars From Eariler)
try: #It is like an if statment that dose in case of failier it will be remeberes as e in the next block
PreviousFrameGray = None #Because there isn't any previous data
PreviousTime = time.time() #It gets the Privious Time (for the frame)
while StopExper != True and KillSwitch == False: #while the expirament was not ended or there was a kill switch
frame = SharedStream.GetLatestFrame() #It takes the value(frame) from the func Get Latest Frame in the Shared Screen Class
if frame is None: #If Frame == None (Works better without bugs from expriance)
time.sleep(0.01) #It waits for 0.01(10 miliSeconds)
continue #It Resets the Loop without any error
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) #It Turnes the Frame to Black and White (GrayScale) to Calculate it easyer
if PreviousFrameGray is None: #If there is no previous frame
PreviousFrameGray = gray #The previous frame is the current frame
continue #It Resets the Loop without an error
ROI_gray = gray[ROI[1]:ROI[1]+ROI[3], ROI[0]:ROI[0]+ROI[2]] #It analises 2 'Reagons Of Intrest' in (Grayscale)
prev_ROI = PreviousFrameGray[ROI[1]:ROI[1]+ROI[3], ROI[0]:ROI[0]+ROI[2]] #It analises the 2 'Reagons Of Intrest' in Gray scale but in the aprevious Frame
Flow = cv2.calcOpticalFlowFarneback(prev_ROI, ROI_gray, None, **fb_params) #It calculates the Optical(camera) Flow, Through the 2 frames analasis, calibration Paramaters from the start
SpeedMagnitude, _ = cv2.cartToPolar(Flow[...,0], Flow[...,1]) #It calculates the flow and it Turens them in to a vector (Speed) + (Degree)
dt = time.time() - PreviousTime #dt Mathamatical notation to difarence in Time (calculates the delta between the curent time and the Previous frames time)
PreviousTime = time.time() #It asighes new time to the Previous time frame
speed = np.mean(SpeedMagnitude) * CALIB / dt #Speed is calculated by (ממוצא חציוני מהירות * Calibration factor from earlier) / Time Difarence
LWSC.append(speed) #The List of Wind Speed from the Camera gets a new value
PreviousFrameGray = gray #The previouse image Gray is now this Gray
except Exception as e: #Finds the Exepetion and stors the Exeption as e
print(f"Error Camera thread failed: {e}") #It prints the posible error to the the user(Saved as e)
StopExper = True #The Var is True, and it stops the experiment
KillSwitch = True #Asginigning True Value to the kill switch Variable(To kill the experimaent)
finally: #After the Try and Except
SharedStream.Stop() #Sends asignal to The Function in the class to start(and it stops the experiment)
def CalcculateAvareges(): #הונקציה מחשבת ומציגה את הממוצע לאחר התיבצות של הנתונים שהתקבלו במהלך הניסוי
averages = { #all of the Variables are global
'AvgTemp': sum(LAT) / len(LAT), #Avarege Tempeter Dicionery entry
'AvgPress': sum(LPR) / len(LPR), #Avarge Pressuier Dicionery
'AvgDelta': sum(LWSPd) / len(LWSPd), #Avarege Speed Delta Dictionery
'AvgCam': sum(LWSC) / len(LWSC), #Avarege Camera Speed Dict Entry
'AvgHum': sum(LAH) / len(LAH), #Avarege Himiditi Dict Entry
}#It Gets the Avarege values in adictionery
print("Experiment Averages: \n", averages) #It prints out the Stabalised Avareges for Each paramater tested
return (averages) #It returns the avareges to the function that has sent them
# ==-- Main code Activation --==
def Main(): #הפונקציה פה מפעילה את שער הפונקציות ומגדירה אותם בסדר הפעולה(ועבודה במקביל)
try: #The Try block here is for activating the main code (without it faling if there is aproblem here like with a thred, socket, function or even a class)
threading.Thread(target=run_flask, daemon=True).start() #It starts the site In the Backround Thread
threading.Thread(target=SharedStream.StartCaptureLoop, daemon=True).start() #Starts the Camera Class in abackround(Deamon) Thread
threading.Thread(target=camera_loop, daemon=True).start() #The threaing procees starts the Camera speed calculations
GetESPIp() #A function to get the ESP32 Current IP addres
if TryConnectToEspWithTimeout(): #If the esp32 dose have a sucesfull conection
CreateConnectionWithEsp32() #It activates a function that verafy and finaloise the connecction
if StartTheExpirament() in ['Y', 'y']: #If in the start the expirament function you get back the result Y or y it continues
Duration = GetDuration() #The duration is the return value from the Get Duration function
threading.Thread(target=ReciveDataFromEsp, daemon=True).start() #It starts the Data recive function, thread in the backround(deamon)
time.sleep(Duration) #It stops the main logic for the amounto of seconsds that the expirament takes, and it gives everything for thr threads
StopExpereriment() #It activates the function that stops the expirament
except KeyboardInterrupt: #The except is that in case the disruption was the keybord(like an else statment onlyfor the keybord)
print("Shutting down...") #It prints a Mesage of shuting down to the user
SharedStream.Stop() #It stops the Main Class
GetConnectionFromEsp.close() #It Stops the connection to the Esp32 scoket
OutputToEsp.close() #It closes the output socket to the ESP32
if InputFromEsp: #In case there is an socket opend for getting an input from the esp32
InputFromEsp.close() #It closes the socket to the Esp32
if __name__ == "__main__": #It checks if the code is The main code or is it an import
Main() #Starts the Body of the expirament