-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathmain.py
More file actions
185 lines (145 loc) · 5.85 KB
/
Copy pathmain.py
File metadata and controls
185 lines (145 loc) · 5.85 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
import os
# Must be set before OpenCV/Tk initializes X11/XCB on Linux.
os.environ.setdefault("LIBXCB_ALLOW_SLOPPY_LOCK", "1")
import asyncio
import gc
import multiprocessing as mp
import pathlib
import platform
import sys
import time
from typing import List, Optional
import typer
from loguru import logger
from src.config import ConfigManager
from src.Logging import initialize_logger
from src.tasks.recognizer import init_recognizers, start_recognizer_process
from src.tasks.websockets import main_ws_loop
if platform.system() != "Windows":
import uvloop
else:
uvloop = None
def _app_dir() -> pathlib.Path:
if getattr(sys, "frozen", False):
return pathlib.Path(sys.executable).resolve().parent
return pathlib.Path(__file__).resolve().parent
def _resolve_config_paths(config_paths: list[str]) -> list[str]:
resolved_paths = []
app_dir = _app_dir()
for config_path in config_paths:
path = pathlib.Path(config_path)
if path.exists() or path.is_absolute():
resolved_paths.append(str(path))
continue
exe_neighbor_path = app_dir / path
if exe_neighbor_path.exists():
resolved_paths.append(str(exe_neighbor_path))
continue
resolved_paths.append(str(path))
return resolved_paths
def _resolve_runtime_paths():
config = ConfigManager.get_config()
config_dir = ConfigManager.get_config_dir(False)
if config_dir is None:
return
data_dir = config_dir / "data"
logs_dir = data_dir / "logs"
data_dir.mkdir(parents=True, exist_ok=True)
logs_dir.mkdir(parents=True, exist_ok=True)
db_path = pathlib.Path(config.vision_setting.face_DB_path)
if not db_path.is_absolute():
db_path = (config_dir / db_path).resolve()
config.vision_setting.face_DB_path = str(db_path)
db_path.parent.mkdir(parents=True, exist_ok=True)
def _run_tasks(coro):
if uvloop is not None:
return asyncio.run(coro, loop_factory=uvloop.new_event_loop)
return asyncio.run(coro)
def _configure_multiprocessing():
mp.freeze_support()
if platform.system() == "Windows":
return
try:
mp.set_start_method("spawn", force=True)
except RuntimeError:
pass
async def tasks_runner(interval: float = 0.001, force_open_camera_window: bool = False):
config = ConfigManager.get_config()
config.vision_setting.interval_sec = interval
window_lock = mp.RLock()
recognizer_tasks = init_recognizers(force_open_camera_window, begin_processes=True, window_lock=window_lock)
websocket_task = asyncio.create_task(main_ws_loop(recognizer_tasks))
restart_after: dict[int, float] = {}
try:
while True:
for cam_id, (process, in_queue, out_queue) in list(recognizer_tasks.items()):
if process.is_alive():
continue
now = time.monotonic()
if now < restart_after.get(cam_id, 0):
continue
logger.error(f"recognizer process cam_id={cam_id} stopped; restarting")
new_process = start_recognizer_process(cam_id, in_queue, out_queue, force_open_camera_window, window_lock)
recognizer_tasks[cam_id] = (new_process, in_queue, out_queue)
restart_after[cam_id] = now + 5
await asyncio.sleep(0.1)
except KeyboardInterrupt:
logger.info("tasks_runner cancelled")
for process, _, _ in recognizer_tasks.values():
process.kill()
except Exception as e:
logger.exception(f"unhandled exception in tasks_runner: {e}")
for process, _, _ in recognizer_tasks.values():
process.kill()
raise e
finally:
websocket_task.cancel()
await asyncio.gather(websocket_task, return_exceptions=True)
# tasks = [
# asyncio.create_task(recognizer_loop(camera_uri=uri, interval=interval,
# open_camera_window=open_camera_window)),
# # add more tasks here
# ]
# try:
# await asyncio.gather(*tasks)
# except (asyncio.CancelledError, Exception) as e:
# if isinstance(e, asyncio.CancelledError):
# logger.info("tasks_runner cancelled")
# else:
# logger.exception(f"unhandled exception in tasks_runner: {e}")
# finally:
# current = asyncio.current_task()
# pending = [t for t in tasks if t is not current and not t.done()]
# for t in pending:
# t.cancel()
# if pending:
# await asyncio.gather(*pending, return_exceptions=True)
# logger.info("tasks_runner cleanup complete")
def CLI(config_paths: List[str] = typer.Option(["config.yaml"], "-c", "--config-path", help="path of config files"),
initialize_logs: bool = True,
interval: Optional[float] = typer.Option(None, "-i", "--interval", help="interval of vision"),
open_camera_window: bool = False):
config = ConfigManager.read_multiple_config_files(*_resolve_config_paths(config_paths))
config = ConfigManager.get_config()
_resolve_runtime_paths()
if config is None:
logger.error("config is not initialized from server, using values from config file")
return
if initialize_logs: initialize_logger(__file__)
try:
_run_tasks(
tasks_runner(
interval=interval or config.vision_setting.interval_sec,
force_open_camera_window=open_camera_window,
)
)
except SystemExit as e:
logger.critical(f"exiting app with error_code={e.code} ...")
raise e
except Exception as e:
logger.exception(f"exception occurred in main loop: {e.__class__}: {str(e)}")
finally:
gc.collect()
if __name__ == "__main__":
_configure_multiprocessing()
typer.run(CLI)