瀏覽代碼

feat: 添加互斥锁

SongZihuan 3 年之前
父節點
當前提交
9a67bd0bb0
共有 9 個文件被更改,包括 463 次插入269 次删除
  1. 96 0
      control/admin.py
  2. 1 2
      control/ranking.py
  3. 43 81
      control/station.py
  4. 0 1
      core/__init__.py
  5. 104 48
      core/garbage.py
  6. 155 88
      core/user.py
  7. 46 47
      equipment/scan.py
  8. 17 1
      sql/db.py
  9. 1 1
      sql/user.py

+ 96 - 0
control/admin.py

@@ -0,0 +1,96 @@
+# import tkinter as tk
+# import tkinter.font as font
+#
+# from tool.type_ import *
+#
+# from sql.db import DB
+# from sql.user import creat_new_user
+# from sql.garbage import creat_new_garbage
+#
+# from equipment.scan_user import write_uid_qr, write_all_uid_qr
+# from equipment.scan_garbage import write_gid_qr
+#
+# from core.user import User
+# from core.garbage import GarbageBag
+#
+#
+# class AdminStationException:
+#     ...
+#
+#
+# class ControlNotLogin(AdminStationException):
+#     ...
+#
+#
+# class CreatGarbageError(AdminStationException):
+#     ...
+#
+#
+# class CreatUserError(AdminStationException):
+#     ...
+#
+#
+# class AdminStationStatus:
+#     def __init__(self,
+#                  win,
+#                  db: DB):
+#         self._win = win
+#         self._db: DB = db
+#         self._admin: Optional[User] = None  # 操作者
+#
+#     def __check_manager_user(self):
+#         if self._admin is None or not self._admin.is_manager():
+#             raise ControlNotLogin
+#
+#     def creat_garbage(self, path: str, num: int = 1) -> List[tuple[str, Optional[GarbageBag]]]:
+#         self.__check_manager_user()
+#
+#         re = []
+#         for _ in range(num):
+#             gar = creat_new_garbage(self._db)
+#             if gar is None:
+#                 raise CreatGarbageError
+#             res = write_gid_qr(gar.get_gid(), path, self._db)
+#             re.append(res)
+#         return re
+#
+#     def creat_user(self, name: uname_t, passwd: passwd_t, phone: str, manager: bool) -> Optional[User]:
+#         user = creat_new_user(name, passwd, phone, manager, self._db)
+#         if user is None:
+#             raise CreatUserError
+#         return user
+#
+#     def creat_user_from_list(self, user_list: List[Tuple[uname_t, passwd_t, str]], manager: bool) -> List[User]:
+#         re = []
+#         for i in user_list:
+#             user = creat_new_user(i[0], i[1], i[2], manager, self._db)
+#             if user is None:
+#                 raise CreatUserError
+#             re.append(user)
+#         return re
+#
+#     def get_uid_qrcode(self, uid: uid_t, path: str) -> Tuple[str, Optional[User]]:
+#         return write_uid_qr(uid, path, self._db)
+#
+#     def get_uid_qrcode_from_list(self, uid_list: List[uid_t], path: str) -> List[Tuple[str, Optional[User]]]:
+#         re = []
+#         for uid in uid_list:
+#             res = write_uid_qr(uid, path, self._db)
+#             re.append(res)
+#         return re
+#
+#     def get_all_uid_qrcode(self, path: str, where: str = "") -> List[str]:
+#         return write_all_uid_qr(path, self._db, where=where)
+#
+#
+# class AdminStation:
+#     def __init__(self, db: DB):
+#         self._status = AdminStationStatus(self, db)
+#
+#         self._window = tk.Tk()
+#         self._sys_height = self._window.winfo_screenheight()
+#         self._sys_width = self._window.winfo_screenwidth()
+#
+#         self._win_height = int(self._sys_height * (2 / 3))
+#         self._win_width = int(self._sys_width * (1 / 3))
+#         self._full_screen = False

+ 1 - 2
control/ranking.py

@@ -117,8 +117,7 @@ class RankingStatus:
 
 
 class RankingStation:
-    def __init__(self, db: DB, refresh_delay: int = conf.tk_refresh_delay):
-        self.refresh_delay = refresh_delay
+    def __init__(self, db: DB):
         self._status = RankingStatus(self, db)
 
         self._window = tk.Tk()

+ 43 - 81
control/station.py

@@ -15,12 +15,12 @@ from core.user import User, UserNotSupportError
 from core.garbage import GarbageBag, GarbageType, GarbageBagNotUse
 
 from sql.db import DB
-from sql.user import update_user, find_user_by_id, creat_new_user
-from sql.garbage import update_garbage, creat_new_garbage
+from sql.user import update_user, find_user_by_id
+from sql.garbage import update_garbage
 
 from equipment.scan import HGSCapture, HGSQRCoder
-from equipment.scan_user import scan_user, write_uid_qr, write_all_uid_qr
-from equipment.scan_garbage import scan_garbage, write_gid_qr
+from equipment.scan_user import scan_user
+from equipment.scan_garbage import scan_garbage
 
 
 class GarbageStationException(Exception):
@@ -39,14 +39,6 @@ class CheckGarbageError(GarbageStationException):
     ...
 
 
-class CreatGarbageError(GarbageStationException):
-    ...
-
-
-class CreatUserError(GarbageStationException):
-    ...
-
-
 class RankingUserError(GarbageStationException):
     ...
 
@@ -190,48 +182,7 @@ class GarbageStationStatus:
         update_user(self._user, self._db)
         update_user(user, self._db)
 
-    def creat_garbage(self, path: str, num: int = 1) -> List[tuple[str, Optional[GarbageBag]]]:
-        self.__check_manager_user()
-        if self._user is None:
-            raise ControlNotLogin
-
-        re = []
-        for _ in range(num):
-            gar = creat_new_garbage(self._db)
-            if gar is None:
-                raise CreatGarbageError
-            res = write_gid_qr(gar.get_gid(), path, self._db)
-            re.append(res)
-        return re
-
-    def creat_user(self, name: uname_t, passwd: passwd_t, phone: str, manager: bool) -> Optional[User]:
-        user = creat_new_user(name, passwd, phone, manager, self._db)
-        if user is None:
-            raise CreatUserError
-        return user
-
-    def creat_user_from_list(self, user_list: List[Tuple[uname_t, passwd_t, str]], manager: bool) -> List[User]:
-        re = []
-        for i in user_list:
-            user = creat_new_user(i[0], i[1], i[2], manager, self._db)
-            if user is None:
-                raise CreatUserError
-            re.append(user)
-        return re
-
-    def get_uid_qrcode(self, uid: uid_t, path: str) -> Tuple[str, Optional[User]]:
-        return write_uid_qr(uid, path, self._db)
-
-    def get_uid_qrcode_from_list(self, uid_list: List[uid_t], path: str) -> List[Tuple[str, Optional[User]]]:
-        re = []
-        for uid in uid_list:
-            res = write_uid_qr(uid, path, self._db)
-            re.append(res)
-        return re
-
-    def get_all_uid_qrcode(self, path: str, where: str = "") -> List[str]:
-        return write_all_uid_qr(path, self._db, where=where)
-
+    # TODO-szh 涉及数据库, 改为异步执行避免阻塞
     def ranking(self, limit: int = 0, order_by: str = 'DESC') -> list[Tuple[uid_t, uname_t, score_t, score_t]]:
         """
         获取排行榜的功能
@@ -270,7 +221,7 @@ class GarbageStationStatus:
             self._garbage = None
         return self._garbage
 
-    def throw_garbage(self, garbage_type: enum):
+    def throw_garbage(self, garbage_type: enum):  # TODO-szh 涉及数据库, 改为异步执行避免阻塞
         self.update_user_time()
         if self._flat != GarbageStationStatus.status_get_garbage_type or self._garbage is None:
             self._win.show_warning("Operation Fail", "You should login first and scan the QR code of the trash bag")
@@ -312,9 +263,14 @@ class GarbageStationStatus:
         info = self._garbage.get_info()
         garbage_type = GarbageType.GarbageTypeStrList[int(info['type'])]
         time_str = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(float(info['use_time'])))
+        check = ""
+        if info['checker'] != 'None':
+            check = f'Checker is f{info["checker"][0:conf.tk_show_uid_len]}\nCheckResult is {info["check"]}\n'
         self._win.show_msg("Garbage Info", (f"Type is {garbage_type}\n"
+                                            f"User is {info['user'][0:conf.tk_show_uid_len]}\n"
                                             f"Location:\n  {info['loc']}\n"
-                                            f"Date:\n  {time_str}"))
+                                            f"{check}"
+                                            f"Date:\n  {time_str}"), big=False)  # 不遮蔽Pass和Fail按键
 
     def show_user_info(self):
         self.update_user_time()
@@ -416,10 +372,7 @@ The function has not yet been implemented.
             self._win.show_msg("RankError", f'Unable to get leaderboard data')
             return
 
-        self._win.show_rank(self.rank_index + 1, len(self.rank),
-                            lambda: self.show_rank(-1),
-                            lambda: self.show_rank(+1),
-                            self.rank[self.rank_index])
+        self._win.show_rank(self.rank_index + 1, len(self.rank), self.rank[self.rank_index])
 
 
 class GarbageStation:
@@ -804,17 +757,24 @@ class GarbageStation:
         self._msg_hide['command'] = lambda: self.hide_msg_rank(True)
         self._msg_hide.place(relx=0.375, rely=0.85, relwidth=0.25, relheight=0.1)
 
-    def show_msg(self, title, info, msg_type='info'):
+    def show_msg(self, title, info, msg_type='info', big: bool = True):
         self._msg_label[2].set(f'{msg_type}: {title}')
         self._msg_label[3].set(f'{info}')
 
-        self.__show_check_frame()
-        self._msg_frame.place(relx=0.45, rely=0.20, relwidth=0.53, relheight=0.50)
         frame_width = self._win_width * 0.53
         self._msg_label[1]['wraplength'] = frame_width * 0.85 - 5  # 设定自动换行的像素
 
-        self._throw_ctrl_frame.place_forget()
-        self._rank_frame.place_forget()
+        if big:
+            self._msg_frame.place(relx=0.45, rely=0.15, relwidth=0.53, relheight=0.70)
+            self._check_ctrl_frame.place_forget()
+            self._throw_ctrl_frame.place_forget()
+            self._rank_frame.place_forget()
+        else:
+            self._msg_frame.place(relx=0.45, rely=0.20, relwidth=0.53, relheight=0.50)
+            self.__show_check_frame()
+
+            self._throw_ctrl_frame.place_forget()
+            self._rank_frame.place_forget()
 
         self._msg_time = time.time()
 
@@ -850,11 +810,15 @@ class GarbageStation:
             btn['text'] = text
 
         self._rank_btn[0].place(relx=0.050, rely=0.88, relwidth=0.25, relheight=0.1)
+        self._rank_btn[0]['command'] = lambda: self._status.show_rank(-1)
+
         self._rank_btn[1].place(relx=0.375, rely=0.88, relwidth=0.25, relheight=0.1)
+        self._rank_btn[1]['command'] = lambda: self.hide_msg_rank(True)
+
         self._rank_btn[2].place(relx=0.700, rely=0.88, relwidth=0.25, relheight=0.1)
+        self._rank_btn[2]['command'] = lambda: self._status.show_rank(+1)
 
-    def set_rank_info(self, page, page_c, prev_func: Callable, next_func: Callable,
-                      rank_info: List[Tuple[int, uname_t, uid_t, score_t, score_t, Optional[str]]]):
+    def set_rank_info(self, rank_info: List[Tuple[int, uname_t, uid_t, score_t, score_t, Optional[str]]]):
         if len(rank_info) > 5:
             rank_info = rank_info[:5]
 
@@ -874,18 +838,7 @@ class GarbageStation:
             self._rank_label[i + 1].place(relx=0.04, rely=height, relwidth=0.92, relheight=0.13)
             height += 0.15
 
-        self._rank_btn[0]['command'] = prev_func
-        self._rank_btn[0]['state'] = 'normal'
-        self._rank_btn[1]['command'] = lambda: self.hide_msg_rank(True)
-        self._rank_btn[2]['command'] = next_func
-        self._rank_btn[2]['state'] = 'normal'
-
-        if page == 1:
-            self._rank_btn[0]['state'] = 'disable'
-        if page == page_c:
-            self._rank_btn[2]['state'] = 'disable'
-
-    def show_rank(self, page: int, page_c: int, prev_func: Callable, next_func: Callable,
+    def show_rank(self, page: int, page_c: int,
                   rank_info: List[Tuple[int, uname_t, uid_t, score_t, score_t, Optional[str]]],
                   title: str = 'Ranking'):
         self._rank_var[0].set(f'{title} ({page}/{page_c})')
@@ -895,7 +848,16 @@ class GarbageStation:
         for lb in self._rank_label[1:]:
             lb['wraplength'] = frame_width * 0.85 - 5  # 设定自动换行的像素
 
-        self.set_rank_info(page, page_c, prev_func, next_func, rank_info)
+        if page == 1:
+            self._rank_btn[0]['state'] = 'disable'
+        else:
+            self._rank_btn[0]['state'] = 'normal'
+        if page == page_c:
+            self._rank_btn[2]['state'] = 'disable'
+        else:
+            self._rank_btn[2]['state'] = 'normal'
+
+        self.set_rank_info(rank_info)
         self._throw_ctrl_frame.place_forget()
         self._check_ctrl_frame.place_forget()
         self._msg_frame.place_forget()
@@ -1027,7 +989,7 @@ class GarbageStation:
 
     def __switch_to_manager_user(self):
         self.manager_user_disable()
-        self.manager_user_disable()
+        self.manager_user_able()
 
     def __switch_to_no_user(self):
         self.manager_user_disable()

+ 0 - 1
core/__init__.py

@@ -1 +0,0 @@
-import tool

+ 104 - 48
core/garbage.py

@@ -1,3 +1,5 @@
+import threading
+
 from tool.type_ import *
 from tool.time_ import HGSTime, hgs_time_t
 from tool.location import HGSLocation, hgs_location_t
@@ -29,73 +31,127 @@ class GarbageBag:
         self._check = False
         self._checker: Union[uid_t, None] = None
 
+        self._lock = threading.RLock()
+
     def __repr__(self):
         tmp = GarbageType.GarbageTypeStrList
-        if not self._have_use:
-            return f"GarbageBag: {self._gid} NOT USE"
-        elif not self._have_check:
-            return (f"GarbageBag: {self._gid} "
-                    f"Type: {tmp[self._type]} "
-                    f"Time: {self._use_time.get_time()} "
-                    f"User: {self._user} "
-                    f"Location {self._loc.get_location()} "
-                    f"NOT CHECK")
-        return (f"GarbageBag: {self._gid} "
-                f"Type: {tmp[self._type]} "
-                f"Time: {self._use_time.get_time()} "
-                f"User: {self._user} "
-                f"Location {self._loc.get_location()} "
-                f"Check: {self._check}"
-                f"Checker: {self._checker}")
+
+        try:
+            self._lock.acquire()
+            if not self._have_use:
+                info = f"GarbageBag: {self._gid} NOT USE"
+            elif not self._have_check:
+                info = (f"GarbageBag: {self._gid} "
+                        f"Type: {tmp[self._type]} "
+                        f"Time: {self._use_time.get_time()} "
+                        f"User: {self._user} "
+                        f"Location {self._loc.get_location()} "
+                        f"NOT CHECK")
+            else:
+                info = (f"GarbageBag: {self._gid} "
+                        f"Type: {tmp[self._type]} "
+                        f"Time: {self._use_time.get_time()} "
+                        f"User: {self._user} "
+                        f"Location {self._loc.get_location()} "
+                        f"Check: {self._check}"
+                        f"Checker: {self._checker}")
+        finally:
+            self._lock.release()
+        return info
 
     def get_info(self) -> dict[str: str]:
-        return {
-            "gid":      str(self._gid),
-            "type":     str(self._type),
-            "use_time": (self._use_time is None) if "" else str(self._use_time.get_time()),
-            "user":     str(self._user),
-            "loc":      (self._loc is None) if "" else str(self._loc.get_location()),
-            "check":    self._check if '1' else '0',
-            "checker":  str(self._checker),
-        }
+        try:
+            self._lock.acquire()
+            info = {
+                "gid": str(self._gid),
+                "type": str(self._type),
+                "use_time": "" if (self._use_time is None) else str(self._use_time.get_time()),
+                "user": str(self._user),
+                "loc": "" if (self._loc is None) else str(self._loc.get_location()),
+                "check": 'Pass' if self._check else 'Fail',
+                "checker": "None" if self._checker is None else self._checker,
+            }
+        finally:
+            self._lock.release()
+        return info
 
     def is_use(self) -> bool:
-        return self._have_use
+        try:
+            self._lock.acquire()
+            have_use = self._have_use
+        finally:
+            self._lock.release()
+        return have_use
 
     def is_check(self) -> Tuple[bool, bool]:
-        if not self._have_check:
+        try:
+            self._lock.acquire()
+            have_check = self._have_check
+            check = self._check
+        finally:
+            self._lock.release()
+
+        if not have_check:
             return False, False
         else:
-            return True, self._check
+            return True, check
 
     def get_gid(self):
-        return self._gid
+        try:
+            self._lock.acquire()
+            gid = self._gid
+        finally:
+            self._lock.release()
+
+        return gid
 
     def get_user(self) -> uid_t:
-        if not self._have_use:
+        try:
+            self._lock.acquire()
+            user = self._user
+            have_use = self._have_use
+        finally:
+            self._lock.release()
+
+        if not have_use:
             raise GarbageBagNotUse
-        return self._user
+        return user
 
     def get_type(self):
-        if not self._have_use:
+        try:
+            self._lock.acquire()
+            type_ = self._type
+            have_use = self._have_use
+        finally:
+            self._lock.release()
+
+        if not have_use:
             raise GarbageBagNotUse
-        return self._type
+        return type_
 
     def config_use(self, garbage_type: enum, use_time: hgs_time_t, user: uid_t, location: hgs_location_t):
-        assert not self._have_use
-        self._have_use = True
-        if not isinstance(use_time, HGSTime):
-            use_time = HGSTime(use_time)
-        if not isinstance(location, HGSLocation):
-            location = HGSLocation(location)
-        self._type: enum = garbage_type
-        self._use_time: HGSTime = use_time
-        self._user: uid_t = user
-        self._loc: HGSLocation = location
+        try:
+            self._lock.acquire()
+            assert not self._have_use
+            self._have_use = True
+            if not isinstance(use_time, HGSTime):
+                use_time = HGSTime(use_time)
+            if not isinstance(location, HGSLocation):
+                location = HGSLocation(location)
+            self._type: enum = garbage_type
+            self._use_time: HGSTime = use_time
+            self._user: uid_t = user
+            self._loc: HGSLocation = location
+        finally:
+            self._lock.release()
 
     def config_check(self, use_right: bool, check_uid: uid_t):
-        assert self._have_use
-        assert not self._have_check
-        self._have_check = True
-        self._check = use_right
-        self._checker = check_uid
+        try:
+            self._lock.acquire()
+            assert self._have_use
+            assert not self._have_check
+            self._have_check = True
+            self._check = use_right
+            self._checker = check_uid
+        finally:
+            self._lock.release()

+ 155 - 88
core/user.py

@@ -1,4 +1,6 @@
 import abc
+import threading
+
 import conf
 from tool.type_ import *
 from tool.time_ import HGSTime
@@ -32,25 +34,56 @@ class User(metaclass=abc.ABCMeta):
         self._name: uname_t = name
         self._uid: uid_t = uid
         self._type: enum = user_type
+        self._lock = threading.RLock()
 
     def is_manager(self):
-        return self._type == UserType.manager
+        try:
+            self._lock.acquire()
+            _type = self._type
+        finally:
+            self._lock.release()
+
+        return _type == UserType.manager
 
     def get_uid(self) -> uid_t:
-        return self._uid
+        try:
+            self._lock.acquire()
+            uid = self._uid
+        finally:
+            self._lock.release()
+
+        return uid
 
     def get_name(self) -> uname_t:
-        return self._name
+        try:
+            self._lock.acquire()
+            name = self._name
+        finally:
+            self._lock.release()
+
+        return name
 
     def get_user_type(self) -> enum:
-        return self._type
+        try:
+            self._lock.acquire()
+            _type = self._type
+        finally:
+            self._lock.release()
+
+        return _type
 
     def get_info(self) -> Dict[str, str]:
         raise UserNotSupportError
 
     def __repr__(self):
-        tmp = UserType.UserTypeStrList
-        return f"User {self._uid} {self._name} is {tmp[self._type]}"
+        try:
+            self._lock.acquire()
+            _type = UserType.UserTypeStrList[self._type]
+            uid = self._uid
+            name = self._name
+        finally:
+            self._lock.release()
+        return f"User {uid} {name} is {_type}"
 
     def evaluate(self, is_right: bool) -> score_t:
         raise UserNotSupportError
@@ -73,25 +106,36 @@ class NormalUser(User):
         self._score = score
 
     def __repr__(self):
-        return (f"User {self._uid} {self._name} "
-                f"reputation {self._reputation} "
-                f"rubbish {self._rubbish} "
-                f"score {self._score} "
-                f"is NORMAL")
+        try:
+            self._lock.acquire()
+            info = (f"User {self._uid} {self._name} "
+                    f"reputation {self._reputation} "
+                    f"rubbish {self._rubbish} "
+                    f"score {self._score} "
+                    f"is NORMAL")
+        finally:
+            self._lock.release()
+        return info
 
     def get_info(self) -> Dict[str, str]:
         """
         获取当前用户的简单信息
         :return: 用户信息字典
         """
-        return {
-            "name": str(self._name),
-            "uid": str(self._uid),
-            "manager": '0',
-            "reputation": str(self._reputation),
-            "rubbish": str(self._rubbish),
-            "score": str(self._score)
-        }
+        try:
+            self._lock.acquire()
+            info =  {
+                "name": str(self._name),
+                "uid": str(self._uid),
+                "manager": '0',
+                "reputation": str(self._reputation),
+                "rubbish": str(self._rubbish),
+                "score": str(self._score)
+            }
+        finally:
+            self._lock.release()
+
+        return info
 
     def evaluate(self, is_right: bool) -> score_t:
         """
@@ -113,65 +157,79 @@ class NormalUser(User):
           P(A|^B) = P(A) * (P(^B|A) / P(^B)) = P(A) * (0.2/0.96) = P(A) * 0.2083
         :return: 信誉积分
         """
+        try:
+            self._lock.acquire()
+            if is_right and self._rubbish > conf.max_rubbish_week:
+                return self._reputation  # 执行 finally将释放锁
+
+            pa = self._reputation / 1000  # P(A)
+            if pa < 0.01:
+                pa = 0.01
+            p_a = 1 - pa  # P(^A)
+            pba, p_ba, pb_a, p_b_a = 0.6, 0.4, 0.3, 0.7  # P(B|A), P(^B|A), P(B|^A), P(^B|^A)
+            pb = pba * pa + pb_a * p_a  # P(B) = P(B|A) * P(A) + P(B|^A) * P(^A)
+            p_b = p_ba * pa + p_b_a * p_a  # P(^B) = P(^B|A) * P(A) + P(^B|^A) * P(^A)
+
+            if is_right:
+                new_pa = pa * (pba / pb)  # P(A|B)
+            else:
+                new_pa = pa * (p_ba / p_b)  # P(A|^B)
+            new_pa = new_pa * 1000
+            if int(new_pa) == 0:
+                new_pa = 1
+            if int(new_pa) > 1000:
+                new_pa = 999
+
+            amplitude = new_pa - self._reputation  # 分差
+            amplitude_top = 1000 - self._reputation  # 距离总分分差
+
+            if is_right:
+                if amplitude >= 20:
+                    amplitude = amplitude * (amplitude_top / 1000)  # 涨分抑制
+            else:
+                if amplitude <= -20:
+                    amplitude = amplitude * (self._reputation / 1000)  # 总分分差月小扣分越高
+
+            self._reputation += int(amplitude)
+            if self._reputation <= 5:
+                self._reputation = 5
+            reputation = self._reputation
+        finally:
+            self._lock.release()
 
-        if is_right and self._rubbish > conf.max_rubbish_week:
-            return self._reputation
-
-        pa = self._reputation / 1000  # P(A)
-        if pa < 0.01:
-            pa = 0.01
-        p_a = 1 - pa  # P(^A)
-        pba, p_ba, pb_a, p_b_a = 0.6, 0.4, 0.3, 0.7  # P(B|A), P(^B|A), P(B|^A), P(^B|^A)
-        pb = pba * pa + pb_a * p_a  # P(B) = P(B|A) * P(A) + P(B|^A) * P(^A)
-        p_b = p_ba * pa + p_b_a * p_a  # P(^B) = P(^B|A) * P(A) + P(^B|^A) * P(^A)
-
-        if is_right:
-            new_pa = pa * (pba / pb)  # P(A|B)
-        else:
-            new_pa = pa * (p_ba / p_b)  # P(A|^B)
-        new_pa = new_pa * 1000
-        if int(new_pa) == 0:
-            new_pa = 1
-        if int(new_pa) > 1000:
-            new_pa = 999
-
-        amplitude = new_pa - self._reputation  # 分差
-        amplitude_top = 1000 - self._reputation  # 距离总分分差
-
-        if is_right:
-            if amplitude >= 20:
-                amplitude = amplitude * (amplitude_top / 1000)  # 涨分抑制
-        else:
-            if amplitude <= -20:
-                amplitude = amplitude * (self._reputation / 1000)  # 总分分差月小扣分越高
-
-        self._reputation += int(amplitude)
-        if self._reputation <= 5:
-            self._reputation = 5
-        return self._reputation
+        return reputation
 
     def add_score(self, score: score_t) -> score_t:
-        if self._score + score < 0:
-            self._score = 0
-            raise UserNotScoreException
+        try:
+            self._lock.acquire()
+            if self._score + score < 0:
+                self._score = 0
+                raise UserNotScoreException
 
-        self._score += score
-        return self._score
+            self._score += score
+            score = self._score
+        finally:
+            self._lock.release()
+        return score
 
     def throw_rubbish(self, garbage: GarbageBag, garbage_type: enum, loc: location_t = conf.base_location) -> bool:
-        if self._rubbish > conf.max_rubbish_week:
-            try:
-                self.add_score(-3)
-            except UserNotScoreException:
+        try:
+            self._lock.acquire()
+            if self._rubbish > conf.max_rubbish_week:
+                try:
+                    self.add_score(-3)
+                except UserNotScoreException:
+                    raise UserRubbishException
+            elif self._rubbish > conf.limit_rubbish_week:
                 raise UserRubbishException
-        elif self._rubbish > conf.limit_rubbish_week:
-            raise UserRubbishException
 
-        if garbage.is_use() or garbage.is_check()[0]:
-            return False
-        garbage.config_use(garbage_type, HGSTime(), self._uid, loc)
+            if garbage.is_use() or garbage.is_check()[0]:
+                return False
+            garbage.config_use(garbage_type, HGSTime(), self._uid, loc)
 
-        self._rubbish += 1
+            self._rubbish += 1
+        finally:
+            self._lock.release()
         return True
 
     def check_rubbish(self, garbage: GarbageBag, right: bool, user: User) -> bool:
@@ -183,30 +241,39 @@ class ManagerUser(User):
         super(ManagerUser, self).__init__(name, uid, UserType.manager)
 
     def check_rubbish(self, garbage: GarbageBag, right: bool, user: User) -> bool:
-        if (not garbage.is_use()) or garbage.is_check()[0] or user.get_uid() != garbage.get_user():
+        if (not garbage.is_use()) or garbage.is_check()[0] or user.get_uid() != garbage.get_user():  # 调用时已经有锁
             return False
 
-        garbage.config_check(right, self._uid)
-        user.evaluate(right)
-
         try:
-            if right:
-                if garbage.get_type() == GarbageType.recyclable:
-                    user.add_score(3)
-                elif garbage.get_type() == GarbageType.kitchen or garbage.get_type() == GarbageType.hazardous:
-                    user.add_score(2)
+            self._lock.acquire()
+            garbage.config_check(right, self._uid)
+            user.evaluate(right)
+
+            try:
+                if right:
+                    if garbage.get_type() == GarbageType.recyclable:
+                        user.add_score(3)
+                    elif garbage.get_type() == GarbageType.kitchen or garbage.get_type() == GarbageType.hazardous:
+                        user.add_score(2)
+                    else:
+                        user.add_score(1)
                 else:
-                    user.add_score(1)
-            else:
-                user.add_score(-4)
-        except UserNotScoreException:
-            ...
+                    user.add_score(-4)
+            except UserNotScoreException:
+                ...
+        finally:
+            self._lock.release()
 
         return True
 
     def get_info(self) -> Dict[str, str]:
-        return {
-            "name": str(self._name),
-            "uid": str(self._uid),
-            "manager": '1'
-        }
+        try:
+            self._lock.acquire()
+            info = {
+                "name": str(self._name),
+                "uid": str(self._uid),
+                "manager": '1'
+            }
+        finally:
+            self._lock.release()
+        return info

+ 46 - 47
equipment/scan.py

@@ -1,6 +1,8 @@
 import time
-import conf
+import threading
 import cv2 as cv2
+
+import conf
 import qrcode
 from tool.type_ import *
 
@@ -12,27 +14,25 @@ class HGSCapture:
             args = *args, cv2.CAP_DSHOW
         self._capture = cv2.VideoCapture(capnum, *args, **kwargs)
         self._frame = None
+        self._lock = threading.RLock()
 
     def get_image(self):
-        ret, frame = self._capture.read()
-        if ret:
-            self._frame = frame
+        try:
+            self._lock.acquire()
+            ret, frame = self._capture.read()
+            if ret:
+                self._frame = frame
+        finally:
+            self._lock.release()
         return ret
 
-    def show_image_wait_second(self, wait_second: int = 10):
-        cv2.imshow('frame', self._frame)
-        if wait_second != 0:
-            return cv2.waitKey(wait_second * 1000)
-        return None
-
-    def show_image(self, wait: int = 10):
-        cv2.imshow('frame', self._frame)
-        if wait != 0:
-            return cv2.waitKey(wait)
-        return None
-
     def get_frame(self):
-        return self._frame
+        try:
+            self._lock.acquire()
+            frame = self._frame
+        finally:
+            self._lock.release()
+        return frame
 
 
 class QRCode:
@@ -66,43 +66,42 @@ class HGSQRCoder:
     def __init__(self, cap: HGSCapture):
         self._cap = cap
         self._last_qr: Optional[QRCode] = None
+        self._lock = threading.RLock()
 
     def get_qr_code(self) -> Optional[QRCode]:
-        re = self.is_qr_code()
+        try:
+            self._lock.acquire()
+            re = self.is_qr_code()
+            last_qr = self._last_qr
+        finally:
+            self._lock.release()
+
         if re:
-            return self._last_qr
+            return last_qr
         return None
 
     def is_qr_code(self) -> bool:
-        frame = self._cap.get_frame()
-        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
-        coder = cv2.QRCodeDetector()
-
         try:
-            data, points, straight_qrcode = coder.detectAndDecode(gray)
-        except cv2.error:
-            return False
+            self._lock.acquire()
 
-        old_qr: Optional[QRCode] = self._last_qr
+            frame = self._cap.get_frame()
+            gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
+            coder = cv2.QRCodeDetector()
 
-        if len(data) > 0:
-            self._last_qr = QRCode(data)
-            return old_qr is None or data != old_qr.get_data()
-        elif len(data) == 0 and old_qr is not None:
-            if time.time() - old_qr.get_time() >= 1.5:  # 时间间隔大于2s 自动取消
+            try:
+                data, points, straight_qrcode = coder.detectAndDecode(gray)
+            except cv2.error:
+                return False
+
+            old_qr: Optional[QRCode] = self._last_qr
+
+            if len(data) > 0:
                 self._last_qr = QRCode(data)
-        return False
-
-
-if __name__ == '__main__':
-    capture = HGSCapture()
-    qr_capture = HGSQRCoder(capture)
-    while True:
-        capture.get_image()
-        qr_data = qr_capture.get_qr_code()
-        if qr_data is not None:
-            print(qr_data)
-            qr_data.make_img("img.png")
-        capture.show_image(0)
-        if cv2.waitKey(1) == ord('q'):
-            break
+                return old_qr is None or data != old_qr.get_data()
+            elif len(data) == 0 and old_qr is not None:
+                if time.time() - old_qr.get_time() >= 1.5:  # 时间间隔大于2s 自动取消
+                    self._last_qr = QRCode(data)
+            return False
+
+        finally:
+            self._lock.release()

+ 17 - 1
sql/db.py

@@ -1,4 +1,5 @@
 import pymysql
+import threading
 from conf import MYSQL_URL, MYSQL_NAME, MYSQL_PASSWORD
 from tool.type_ import *
 
@@ -31,6 +32,7 @@ class DB:
         except pymysql.err.OperationalError:
             raise DBException
         self._cursor = self._db.cursor()
+        self._lock = threading.RLock()
 
     def __del__(self):
         self.close()
@@ -42,6 +44,7 @@ class DB:
             self._db.close()
         self._db = None
         self._cursor = None
+        self._lock = None
 
     def is_connect(self) -> bool:
         if self._cursor is None or self._db is None:
@@ -58,9 +61,12 @@ class DB:
             raise DBCloseException
 
         try:
+            self._lock.acquire()  # 上锁
             self._cursor.execute(sql)
         except pymysql.MySQLError:
             return None
+        finally:
+            self._lock.release()  # 释放锁
         return self._cursor
 
     def done(self, sql) -> Union[None, pymysql.cursors.Cursor]:
@@ -68,26 +74,36 @@ class DB:
             raise DBCloseException
 
         try:
+            self._lock.acquire()
             self._cursor.execute(sql)
         except pymysql.MySQLError:
             self._db.rollback()
             return None
         finally:
             self._db.commit()
+            self._lock.release()
         return self._cursor
 
     def done_(self, sql) -> Union[None, pymysql.cursors.Cursor]:
         if self._cursor is None or self._db is None:
             raise DBCloseException
         try:
+            self._lock.acquire()
             self._cursor.execute(sql)
         except pymysql.MySQLError:
             self._db.rollback()
             raise DBDoneException
+        finally:
+            self._lock.release()
         return self._cursor
 
     def done_commit(self):
-        self._db.commit()
+        try:
+            self._lock.acquire()
+            self._db.commit()
+        finally:
+            self._lock.release()
+
 
 if __name__ == '__main__':
     # 测试程序

+ 1 - 1
sql/user.py

@@ -72,7 +72,7 @@ def creat_new_user(name: Optional[uname_t], passwd: Optional[passwd_t], phone: p
     uid = creat_uid(name, passwd)
     if is_user_exists(uid, db):
         return None
-    is_manager = manager if '1' else '0'
+    is_manager = '1' if manager else '0'
     cur = db.done(f"INSERT INTO user(uid, name, manager, phone, score, reputation) "
                   f"VALUES ('{uid}', '{name}', {is_manager}, '{phone}', {conf.default_score}, "
                   f"{conf.default_reputation});")