async def test_add_period(self):
     AsyncExclusivePeriod.add_period(self, 'test4')
     self.assertEqual(AsyncExclusivePeriod.get_obj_present_period(self),
                      'test1')
     obj_period_names = AsyncExclusivePeriod.get_obj_period_names(self)
     self.assertEqual({'test1', 'test2', 'test3', 'test4'},
                      set(obj_period_names))
    async def _handle_wsq(self):
        '''
        始终使用原生ws连接队列中的最后一个

        :return:
        '''
        _handle_raw_ws_msg_task = None
        while not self._exiting:
            # 拿到最新原生ws连接
            while True:
                self._wsq.previous_ws = self._wsq.present_ws
                self._wsq.present_ws = await self._wsq.get()
                self._wsq.task_done()

                # 关闭之前的ws连接
                if isinstance(self._wsq.previous_ws,
                              websockets.WebSocketClientProtocol):
                    asyncio.create_task(self._wsq.previous_ws.close())
                # 拿完了,得到最后一个
                if self._wsq.qsize() <= 0:
                    if _handle_raw_ws_msg_task:
                        # 删除之前的ws处理任务
                        asyncio.create_task(
                            ensureTaskCanceled(_handle_raw_ws_msg_task))
                    break
            _handle_raw_ws_msg_task = asyncio.create_task(
                self._handle_raw_ws_msg(self._wsq.present_ws))
            self._handle_raw_ws_msg_task = _handle_raw_ws_msg_task
            AsyncExclusivePeriod.set_obj_period(self, 'handing_ws')
    async def _ws_manager(self):
        # 启动ws连接队列的消息对接handlers处理任务
        self._handle_wsq_task = asyncio.create_task(self._handle_wsq())
        initail = True
        while not self._exiting or initail:
            try:
                initail = False
                new_ws = await self._create_ws()
                # 更新连接
                self._wsq.put_nowait(new_ws)
                # wait until ws is exchanged.
                await AsyncExclusivePeriod.wait_enter_period(
                    self, 'handing_ws')
                logger.debug('New ws connection opened.')

                # 等待需要更新连接的信号或者连接报错
                self._when2create_new_ws_task = asyncio.create_task(
                    self._when2create_new_ws())
                await asyncio.wait([self._when2create_new_ws_task])
                await asyncio.create_task(asyncio.sleep(1))
                # 进入更换ws时期
                AsyncExclusivePeriod.set_obj_period(self, 'exchanging_ws')
                if self._handle_raw_ws_msg_task:
                    self._handle_raw_ws_msg_task.cancel()
            except asyncio.CancelledError:
                raise
            except:
                await asyncio.create_task(asyncio.sleep(1))
 async def wait_worker_accomplish_working_then_set_period_test1():
     '''等待工人干完活就设置self到周期test1'''
     while True:
         wait_worker1_exit_working_task = asyncio.create_task(
             AsyncExclusivePeriod.wait_exit_period(
                 'worker1', 'working'))
         wait_worker2_exit_working_task = asyncio.create_task(
             AsyncExclusivePeriod.wait_exit_period(
                 'worker2', 'working'))
         await asyncio.gather(wait_worker1_exit_working_task,
                              wait_worker2_exit_working_task)
         AsyncExclusivePeriod.set_obj_period(self, 'test1')
 def __init__(self):
     self._wsq: asyncio.Queue = asyncio.Queue()
     self._wsq.present_ws: websockets.WebSocketClientProtocol = None
     self._wsq.previous_ws: websockets.WebSocketClientProtocol = None
     self._exiting = False
     self._ws_manager_task = asyncio.create_task(self._ws_manager())
     self._handlers = set()
     # 更换ws连接时期和处理ws数据时期,刚开始是exchanging_ws时期
     AsyncExclusivePeriod.create_obj_periods(self, 'exchanging_ws',
                                             'handing_ws')
     self._when2create_new_ws_task = None
     self._handle_raw_ws_msg_task = None
     self._handle_wsq_task: asyncio.Task = None
 async def worker(name: str):
     '''工人等self的test2时期,就工作,工作完再等'''
     while True:
         AsyncExclusivePeriod.set_obj_period(name, 'waiting')
         await AsyncExclusivePeriod.wait_enter_period(self, 'test2')
         AsyncExclusivePeriod.set_obj_period(name, 'working')
         q.put_nowait({
             "time": asyncio.get_running_loop().time(),
             'msg': 'start to work'
         })
         await asyncio.sleep(1)  # simulate work
         q.put_nowait({
             "time": asyncio.get_running_loop().time(),
             'msg': 'end working'
         })  # 测试间隔1s
 async def _handle_raw_ws_msg(self, ws: websockets.WebSocketClientProtocol):
     try:
         async for msg in ws:
             try:
                 # logger.debug(repr(old_ws_update_ws_task))
                 msg = await self._parse_raw_data(msg)
                 logger.debug('\n' + beeprint.pp(msg,
                                                 output=False,
                                                 string_break_enable=False,
                                                 sort_keys=False))
                 # logger.debug('\n' + repr(self._update_ws_task))
                 tasks = []
                 for handler in self._handlers:
                     if asyncio.iscoroutinefunction(handler):
                         tasks.append(
                             asyncio.create_task(handler(deepcopy(msg))))
                     else:
                         try:
                             handler(deepcopy(msg))
                         except:
                             pass
                 for task in tasks:
                     try:
                         await task
                     except:
                         pass
             except:
                 logger.error('\n' + traceback.format_exc())
     except asyncio.CancelledError:
         # print('忽略CancelledError')
         pass
     except:
         if self._when2create_new_ws_task and 'handing_ws' == AsyncExclusivePeriod.get_obj_present_period(
                 self):
             self._when2create_new_ws_task.cancel()
         logger.error(
             'ERROR leads to update connection.\n' +
             traceback.format_exc(), )
     finally:
         logger.debug('Old connection abandoned.')
 async def test_in_test1_period_after_end_working():
     await asyncio.sleep(0.5)
     self.assertEqual(
         'test1',
         AsyncExclusivePeriod.get_obj_present_period(self))
 async def setUp(self) -> None:
     AsyncExclusivePeriod.create_obj_periods(self, 'test1', 'test2',
                                             'test3')
     self.assertEqual(AsyncExclusivePeriod.get_obj_present_period(self),
                      'test1')
 async def set_test2():
     '''只要离开test2时期就1s后设置到test2时期让工人干活'''
     while True:
         await AsyncExclusivePeriod.wait_outside_period(self, 'test2')
         await asyncio.sleep(1)
         AsyncExclusivePeriod.set_obj_period(self, 'test2')  # 测试休息1s
    async def test_handle_period_then_set_period(self):
        q = asyncio.Queue()
        # 两个工人
        AsyncExclusivePeriod.create_obj_periods('worker1', 'waiting',
                                                'working')

        AsyncExclusivePeriod.create_obj_periods('worker2', 'waiting',
                                                'working')

        async def worker(name: str):
            '''工人等self的test2时期,就工作,工作完再等'''
            while True:
                AsyncExclusivePeriod.set_obj_period(name, 'waiting')
                await AsyncExclusivePeriod.wait_enter_period(self, 'test2')
                AsyncExclusivePeriod.set_obj_period(name, 'working')
                q.put_nowait({
                    "time": asyncio.get_running_loop().time(),
                    'msg': 'start to work'
                })
                await asyncio.sleep(1)  # simulate work
                q.put_nowait({
                    "time": asyncio.get_running_loop().time(),
                    'msg': 'end working'
                })  # 测试间隔1s
                # 测试在0.5s之后是test1时期

        # 激活两工人
        worker1_task = asyncio.create_task(worker('worker1'))

        worker2_task = asyncio.create_task(worker('worker2'))

        async def wait_worker_accomplish_working_then_set_period_test1():
            '''等待工人干完活就设置self到周期test1'''
            while True:
                wait_worker1_exit_working_task = asyncio.create_task(
                    AsyncExclusivePeriod.wait_exit_period(
                        'worker1', 'working'))
                wait_worker2_exit_working_task = asyncio.create_task(
                    AsyncExclusivePeriod.wait_exit_period(
                        'worker2', 'working'))
                await asyncio.gather(wait_worker1_exit_working_task,
                                     wait_worker2_exit_working_task)
                AsyncExclusivePeriod.set_obj_period(self, 'test1')

        wait_worker_accomplish_working_then_set_period_test1_task = \
            asyncio.create_task(wait_worker_accomplish_working_then_set_period_test1())

        async def noticing_in_test2():
            '''提醒工人干活的时期'''
            while True:
                await AsyncExclusivePeriod.wait_inside_period(self, 'test2')
                q.put_nowait({
                    "time": asyncio.get_running_loop().time(),
                    'msg': 'In test2, the workers should be working.'
                })

                await asyncio.sleep(0.05)  # 测试是处于工作时期内

        noticing_in_test2_task = asyncio.create_task(noticing_in_test2())

        async def set_test2():
            '''只要离开test2时期就1s后设置到test2时期让工人干活'''
            while True:
                await AsyncExclusivePeriod.wait_outside_period(self, 'test2')
                await asyncio.sleep(1)
                AsyncExclusivePeriod.set_obj_period(self, 'test2')  # 测试休息1s

        set_test2_task = asyncio.create_task(set_test2())

        work_start_time = None
        work_end_time = None
        tasks = []
        init_time = asyncio.get_running_loop().time()
        while asyncio.get_running_loop().time() - init_time <= 5:
            new_msg = await q.get()
            if new_msg['msg'] == 'start to work':
                work_start_time = new_msg['time']
                if work_end_time:
                    self.assertEqual(round(work_start_time - work_end_time), 1)

            elif new_msg['msg'] == 'end working':
                work_end_time = new_msg['time']
                self.assertEqual(round(work_end_time - work_start_time), 1)

                async def test_in_test1_period_after_end_working():
                    await asyncio.sleep(0.5)
                    self.assertEqual(
                        'test1',
                        AsyncExclusivePeriod.get_obj_present_period(self))

                tasks.append(
                    asyncio.create_task(
                        test_in_test1_period_after_end_working()))
            elif new_msg['msg'] == 'In test2, the workers should be working.':
                if work_end_time:
                    self.assertLessThan(new_msg['time'] - work_start_time, 1)
        [await task for task in tasks]
 async def _wait_then_set_period(self, obj, wait_seconds: float,
                                 period_name: str):
     await asyncio.sleep(wait_seconds)
     AsyncExclusivePeriod.set_obj_period(obj, period_name)
 async def test_set_get_obj_present_period(self):
     AsyncExclusivePeriod.set_obj_period(self, 'test2')
     self.assertEqual('test2',
                      AsyncExclusivePeriod.get_obj_present_period(self))
 def test_get_obj_period_names(self):
     obj_period_names = AsyncExclusivePeriod.get_obj_period_names(self)
     self.assertEqual({'test1', 'test2', 'test3'}, set(obj_period_names))
 def test_get_obj_period(self):
     self.assertEqual(
         AsyncExclusivePeriod._get_obj_period(self, 'test1')._name, 'test1')