makakwastaken commited on
Commit
5190f6d
·
1 Parent(s): 7ba776c

Added ConnectionManager for better connection handling

Browse files
Files changed (3) hide show
  1. app.py +27 -15
  2. connectionManager.py +55 -0
  3. requirements.txt +1 -1
app.py CHANGED
@@ -4,6 +4,10 @@ from fastapi import FastAPI, WebSocket, WebSocketDisconnect
4
  from fastapi.middleware.cors import CORSMiddleware
5
  from fastapi.logger import logger
6
 
 
 
 
 
7
  from model import Model
8
  import base64
9
  from io import BytesIO
@@ -44,10 +48,12 @@ async def startup_event():
44
 
45
  # Initialize the pytorch model
46
  model = Model()
 
47
 
48
  # add model and other preprocess tools too app state
49
  app.package = {
50
- "model": model
 
51
  }
52
 
53
  @app.get("/ping")
@@ -102,28 +108,31 @@ async def do_predict(body: ImageBody):
102
  img_str = 'data:image/jpeg;base64,' + base64.b64encode(buffered.getvalue()).decode("utf-8")
103
 
104
  return {"ok": True, "status": "FINISHED", "result": img_str}
105
-
106
 
107
  @app.websocket("/ws")
108
  async def websocket_endpoint(websocket: WebSocket):
109
- await websocket.accept()
 
 
110
  while True:
111
  try:
112
- data = await websocket.receive_json()
 
 
 
 
 
 
 
 
 
113
  image: str = data.get('image')
114
  threshold: float = data.get('threshold') or 0.15
115
  num_objects: int = data.get('num_objects') or 1
116
 
117
- await websocket.send_json({"ok": True, "status": "STARTED"})
118
 
119
- if image == None:
120
- await websocket.send_json({
121
- "ok": False,
122
- "status": "ERROR",
123
- "message": "No image provided"
124
- })
125
- break
126
-
127
  # Run the algorithm
128
  result = predict(app.package, image, threshold, num_objects)
129
 
@@ -132,12 +141,15 @@ async def websocket_endpoint(websocket: WebSocket):
132
  result.save(buffered, format="JPEG")
133
  img_str = 'data:image/jpeg;base64,' + base64.b64encode(buffered.getvalue()).decode("utf-8")
134
 
135
- await websocket.send_json({"ok": True, "status": "FINISHED", "result": img_str})
136
 
137
  await websocket.close()
 
138
  except WebSocketDisconnect:
 
 
139
  break
140
 
141
  if __name__ == '__main__':
142
  # server api
143
- uvicorn.run("app:app", host="0.0.0.0", port=8000, reload=True)
 
4
  from fastapi.middleware.cors import CORSMiddleware
5
  from fastapi.logger import logger
6
 
7
+ # Connection Manager
8
+ from connectionManager import ConnectionManager
9
+
10
+ # Model
11
  from model import Model
12
  import base64
13
  from io import BytesIO
 
48
 
49
  # Initialize the pytorch model
50
  model = Model()
51
+ connectionManager = ConnectionManager()
52
 
53
  # add model and other preprocess tools too app state
54
  app.package = {
55
+ "model": model,
56
+ "connectionManager": connectionManager
57
  }
58
 
59
  @app.get("/ping")
 
108
  img_str = 'data:image/jpeg;base64,' + base64.b64encode(buffered.getvalue()).decode("utf-8")
109
 
110
  return {"ok": True, "status": "FINISHED", "result": img_str}
111
+
112
 
113
  @app.websocket("/ws")
114
  async def websocket_endpoint(websocket: WebSocket):
115
+ connectionManager = app.package.get('connectionManager')
116
+ await connectionManager.connect(websocket)
117
+ await connectionManager.send_json({"ok": True, "status": "CONNECTED"}, websocket)
118
  while True:
119
  try:
120
+ data = await connectionManager.receive_json(websocket)
121
+ if (data is None):
122
+ # Wait for data
123
+ if not connectionManager.isConnected(websocket):
124
+ break
125
+ if connectionManager.shouldDisconnect(websocket):
126
+ await websocket.close()
127
+ connectionManager.disconnect(websocket)
128
+ break
129
+ continue
130
  image: str = data.get('image')
131
  threshold: float = data.get('threshold') or 0.15
132
  num_objects: int = data.get('num_objects') or 1
133
 
134
+ await connectionManager.send_json({"ok": True, "status": "STARTED"}, websocket)
135
 
 
 
 
 
 
 
 
 
136
  # Run the algorithm
137
  result = predict(app.package, image, threshold, num_objects)
138
 
 
141
  result.save(buffered, format="JPEG")
142
  img_str = 'data:image/jpeg;base64,' + base64.b64encode(buffered.getvalue()).decode("utf-8")
143
 
144
+ await connectionManager.send_json({"ok": True, "status": "FINISHED", "result": img_str}, websocket)
145
 
146
  await websocket.close()
147
+ connectionManager.disconnect(websocket)
148
  except WebSocketDisconnect:
149
+ print('websocket disconnect')
150
+ connectionManager.disconnect(websocket)
151
  break
152
 
153
  if __name__ == '__main__':
154
  # server api
155
+ uvicorn.run("app:app", host="0.0.0.0", port=7860, reload=True)
connectionManager.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import WebSocket
2
+
3
+ from datetime import datetime
4
+ import json
5
+ from typing import Dict, List
6
+
7
+ class Connection:
8
+ websocket: WebSocket
9
+ connection_time: datetime
10
+
11
+ def __init__(self, websocket: WebSocket, connection_time: datetime):
12
+ self.websocket = websocket
13
+ self.connection_time = connection_time
14
+
15
+ class ConnectionManager:
16
+ timeout = 60 * 5 # 5 minutes
17
+
18
+ def __init__(self):
19
+ self.active_connections: List[Connection] = []
20
+
21
+ async def connect(self, websocket: WebSocket):
22
+ await websocket.accept()
23
+ # Add connection time and websocket to active connections
24
+ self.active_connections.append(Connection(websocket=websocket, connection_time=datetime.now()))
25
+
26
+ def isConnected(self, websocket: WebSocket):
27
+ for connection in self.active_connections:
28
+ if connection.websocket == websocket:
29
+ return True
30
+ return False
31
+
32
+ def shouldDisconnect(self, websocket: WebSocket):
33
+ for connection in self.active_connections:
34
+ if connection.websocket == websocket:
35
+ if (datetime.now() - connection.connection_time).total_seconds() > self.timeout:
36
+ return True
37
+ return False
38
+
39
+ async def receive_json(self, websocket: WebSocket):
40
+ if not self.isConnected(websocket):
41
+ return None
42
+ data = await websocket.receive_json()
43
+ return data
44
+
45
+ def disconnect(self, websocket: WebSocket):
46
+ for connection in self.active_connections:
47
+ if connection.websocket == websocket:
48
+ self.active_connections.remove(connection)
49
+ return True
50
+ return False
51
+
52
+ async def send_json(self, json, websocket: WebSocket):
53
+ # Only send the message if the connection is still active
54
+ if self.isConnected(websocket):
55
+ await websocket.send_json(json)
requirements.txt CHANGED
@@ -8,7 +8,7 @@ torchvision==0.14.1
8
  tqdm==4.64.1
9
 
10
  pycocotools==2.0.6
11
- fastapi==0.94.0
12
  websockets==10.4
13
  pydantic==1.8.2
14
  uvicorn[standard]==0.21.0
 
8
  tqdm==4.64.1
9
 
10
  pycocotools==2.0.6
11
+ fastapi==0.94.1
12
  websockets==10.4
13
  pydantic==1.8.2
14
  uvicorn[standard]==0.21.0