예제 #1
0
    def handle_device_page_read(self, type, seq, kwargs):
        page_id = kwargs.get('page_id')
        discard = kwargs.get('discard', True)

        if self.has_page(page_id):
            page = self.get_page(page_id)
            if discard:
                page.clear_buffer()

            if page.buffer_data_valid():
                message = ServerMessage(Message.MSG_DEVICE_PAGE_READ,
                                        self.id(),
                                        seq,
                                        value=page)
                self.prepare_message(message)
            else:
                kwargs = {'page_id': page_id}
                command = ServerMessage(Message.CMD_DEVICE_PAGE_READ,
                                        self.id(),
                                        self.next_seq(seq),
                                        addr=page.addr(),
                                        size=page.size(),
                                        page_id=page_id)
                self.prepare_command(command)
        else:
            ServerError("Unknown page read {} requested".format(page_id))
            self.nak_command(seq)
예제 #2
0
    def handle_device_page_write(self, type, seq, kwargs):
        page_id = kwargs.get('page_id')
        offset = kwargs.get('offset', 0)
        value = kwargs.get('value')
        assert isinstance(value, (tuple, list, array.array))

        if self.has_page(page_id):
            page = self.get_page(page_id)
            page_size = page.size()
            buf_size = page.data_length()
            buf = page.buf()

            st = None
            cmd_list = []
            val = value[:page_size - offset]
            for i, v in enumerate(val):
                if i >= buf_size:  #buf data is not enough
                    if st is None:
                        st = i
                        break

                if v != buf[offset + i]:
                    if st is None:
                        st = i
                else:
                    if st is not None:
                        cmd = ServerMessage(Message.CMD_DEVICE_PAGE_WRITE,
                                            self.id(),
                                            self.next_seq(seq),
                                            addr=page.addr() + st + offset,
                                            size=i - st,
                                            value=val[st:i],
                                            page_id=page_id)
                        cmd_list.append(cmd)  #build group command
                        st = None
            if st:  #last trunk
                cmd = ServerMessage(Message.CMD_DEVICE_PAGE_WRITE,
                                    self.id(),
                                    self.next_seq(seq),
                                    addr=page.addr() + st + offset,
                                    size=len(val) - st,
                                    value=val[st:],
                                    page_id=page_id)
                cmd_list.append(cmd)
            if cmd_list:
                self.prepare_command(cmd_list)
            else:
                self.handle_nak_msg(seq, error="Not data changed")
        else:
            ServerError("Unknown page write {} requested".format(page_id))
            self.nak_command(seq)
예제 #3
0
 def handle_attached_msg(self, seq, data):
     val = data.get('value', None)
     if val:
         kwargs = {'repeat': LogicDevice.INTERVAL_POLL_DEVICE}
         command = ServerMessage(Message.CMD_POLL_DEVICE, self.id(),
                                 self.next_seq(Message.seq_root()),
                                 **kwargs)
         self.prepare_command(command)
     else:
         message = ServerMessage(Message.MSG_DEVICE_ATTACH,
                                 self.id(),
                                 seq,
                                 value=None)
         self.prepare_message(message)
예제 #4
0
 def handle_nak_msg(self, seq, error=None):
     print(self.__class__.__name__, "handle NAK message:", seq, error)
     message = ServerMessage(Message.MSG_DEVICE_NAK,
                             self.id(),
                             seq,
                             error=error)
     self.prepare_message(message)
예제 #5
0
    def handle_page_write_msg(self, seq, cmd, data):
        type = cmd.type()
        cmd_info = cmd.extra_info()
        cmd_addr = cmd_info['addr']
        cmd_value = cmd_info['value']  # store how many data will be sent
        cmd_group = cmd_info.pop('group', None)
        cmd_size = len(cmd_value)
        cmd_page_id = cmd_info['page_id']

        #print(self.__class__.__name__, "handle_page_write_msg", cmd, data)

        data_size = data['value']
        if data_size == 0 or data_size > cmd_size:  #something error
            self.handle_nak_msg(
                seq, "data_size is invalid data_size {} cmd_size {}".format(
                    data_size, cmd_size))
            return

        page = self.get_page(cmd_page_id)
        start = cmd_addr - page.addr()
        result = page.save_to_buffer(start, cmd_value[:data_size])
        if not result:
            self.handle_nak_msg(
                seq, "save_to_buffer error start {} data {}".format(
                    start, data['value']))
            return

        if data_size == len(cmd_value):
            if not cmd_group:
                message = ServerMessage(Message.MSG_DEVICE_PAGE_WRITE,
                                        self.id(),
                                        seq,
                                        value=page)
                self.prepare_message(message)
            else:
                self.prepare_command(cmd_group)
        else:
            #size = cmd_size - data_size
            value = cmd_value[data_size:]
            command = ServerMessage(Message.CMD_DEVICE_PAGE_WRITE,
                                    self.id(),
                                    self.next_seq(seq),
                                    addr=cmd_addr + data_size,
                                    value=value,
                                    page_id=cmd_page_id)
            self.prepare_command(command)
예제 #6
0
    def handle_page_read_msg(self, seq, cmd, data):
        type = cmd.type()
        cmd_info = cmd.extra_info()
        cmd_addr = cmd_info['addr']
        cmd_size = cmd_info['size']  # how many data is left
        cmd_page_id = cmd_info['page_id']

        print(self.__class__.__name__, "handle_page_read_msg", cmd, data)

        data_size = len(data['value'])  #current readout data size
        if data_size == 0 or data_size > cmd_size:  #something error
            self.handle_nak_msg(
                seq, "data_size is invalid data_size {} cmd_size {}".format(
                    data_size, cmd_size))
            return

        page = self.get_page(cmd_page_id)
        start = cmd_addr - page.addr()
        result = page.save_to_buffer(start, data['value'])
        if not result:
            self.handle_nak_msg(
                seq, "save_to_buffer error start {} data {}".format(
                    start, data['value']))
            return

        if data_size == cmd_size:  # no data left
            result = self.page_parse(cmd_page_id)
            if result:
                message = ServerMessage(Message.MSG_DEVICE_PAGE_READ,
                                        self.id(),
                                        seq,
                                        value=page)
                self.prepare_message(message)
            else:  #failed
                page.clear_buffer()
                self.handle_nak_msg(seq,
                                    "parse page failed {}".format(cmd_page_id))
        else:
            command = ServerMessage(Message.CMD_DEVICE_PAGE_READ,
                                    self.id(),
                                    self.next_seq(seq),
                                    addr=cmd_addr + data_size,
                                    size=cmd_size - data_size,
                                    page_id=cmd_page_id)
            self.prepare_command(command)
예제 #7
0
 def handle_device_msg_output(self, type, seq, kwargs):
     page_id = (5, 0)
     if self.has_page(page_id):
         page = self.get_page(page_id)
         command = ServerMessage(Message.CMD_DEVICE_MSG_OUTPUT,
                                 self.id(),
                                 self.next_seq(seq),
                                 addr=page.addr(),
                                 size=page.size() - 1)
         self.prepare_command(command)
     else:
         ServerError("T5 is not ready for output")
         self.nak_command(seq)
예제 #8
0
    def __init__(self):

        self.GamerAccount = GamerAccount(config.server.UsrAccountAddr)

        # 创建套接字
        self.WelcomeSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 设置服务器使用固定地址
        self.WelcomeSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,
                                      1)
        # 绑定端口
        self.WelcomeSocket.bind(('', config.connect.ServerPort))

        self.UnloggedGamers = []  # socket已连接,但是逻辑还没有登录的玩家
        self.Gamers = GamerGroup(config.game.MaxGamer)  # 已登录的玩家群组
        self.GameRoundPerGamer = config.game.MaxRound  # 每一个玩家出题的循环次数
        self.ServerMessage = ServerMessage()  # 服务器端发送消息模板类
        self.TimerManager = TimerManager()  # 服务器端计时器管理实例,可以方便地创建定时器
        self.GamerCmdListenThreads = []  # 游戏状态消息循环监听的消息接受线程列表
        self.GameLogic = None  # 游戏逻辑实体类
        self.CmdQueue = Queue()  # 监听消息线程间通信队列
        self.GameBeginFlag = ThreadValue(True)  # 指示游戏是否开始的标志
        self.MessageLoopFlag = True  # 指示消息循环是否退出的标志
예제 #9
0
    def handle_connected_msg(self, seq, data):
        #print("handle_connected_msg")
        if LogicDevice.PAGE_ID_INFORMATION_QUERY_CONNECTED:
            page_id = Page.ID_INFORMATION
            if not self.page_valid(page_id):
                self.page_read(page_id, discard=True)

        address = 0x01  #FIXME: need report device Phy I2C Address

        message = ServerMessage(Message.MSG_DEVICE_CONNECTED,
                                self.id(),
                                seq,
                                value=address)
        self.prepare_message(message)
예제 #10
0
    def handle_attach_msg(self, id, seq, ext_info):
        logic_pipe = ext_info['value']

        #remove device
        if id in self.devices.keys():
            if not logic_pipe:
                dev = self.devices[id]
                del self.devices[id]
                del dev

                #   logice device is removed, so there should send message to UI individually
                ServerMessage(Message.MSG_DEVICE_ATTACH,
                              id,
                              seq,
                              value=None,
                              pipe=self.ui_pipe).send()
        else:
            if logic_pipe:
                dev = LogicDevice(
                    id, logic_pipe
                )  #each logic device will communicate to a physical device
                self.devices[id] = dev
예제 #11
0
 def nak_command(self, seq):
     message = ServerMessage(Message.MSG_DEVICE_NAK, self.id(), seq)
     self.prepare_message(message)
예제 #12
0
 def set_get_chip_info(self):
     kwargs = {'addr': 0, 'size': 7}
     command = ServerMessage(Message.CMD_DEVICE_PAGE_READ, self.id(),
                             self.next_seq(Message.seq_root()), **kwargs)
     self.prepare_command(command)
예제 #13
0
 def handle_interrupt_data_msg(self, seq, data):
     message = ServerMessage(Message.MSG_DEVICE_INTERRUPT_DATA,
                             self.id(),
                             seq,
                             value=data['value'])
     self.prepare_message(message)
예제 #14
0
 def handle_message_output_msg(self, seq, cmd, data):
     message = ServerMessage(Message.MSG_DEVICE_MSG_OUTPUT,
                             self.id(),
                             seq,
                             value=data['value'])
     self.prepare_message(message)
예제 #15
0
 def handle_raw_data_msg(self, seq, cmd, data):
     message = ServerMessage(Message.MSG_DEVICE_RAW_DATA,
                             self.id(),
                             seq,
                             value=data['value'])
     self.prepare_message(message)
예제 #16
0
 def handle_device_raw_send(self, type, seq, kwargs):
     command = ServerMessage(Message.CMD_DEVICE_RAW_DATA, self.id(),
                             self.next_seq(seq), **kwargs)
     self.prepare_command(command)
예제 #17
0
 def set_bridge_poll(self, kwargs={}):
     command = ServerMessage(Message.CMD_POLL_BRIDGE, self.id(),
                             self.next_seq(Message.seq_root()), **kwargs)
     self.prepare_command(command)
예제 #18
0
 def handle_device_poll(self, type, seq, kwargs):
     command = ServerMessage(Message.CMD_POLL_DEVICE, self.id(),
                             self.next_seq(seq), **kwargs)
     self.prepare_command(command)
예제 #19
0
 def set_raw_command(self, raw_data):
     kwargs = {'value': raw_data}
     command = ServerMessage(Message.CMD_DEVICE_RAW_DATA, self.id(),
                             self.next_seq(Message.seq_root()), **kwargs)
     self.prepare_command(command)
예제 #20
0
class Server:
    def __init__(self):

        self.GamerAccount = GamerAccount(config.server.UsrAccountAddr)

        # 创建套接字
        self.WelcomeSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 设置服务器使用固定地址
        self.WelcomeSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,
                                      1)
        # 绑定端口
        self.WelcomeSocket.bind(('', config.connect.ServerPort))

        self.UnloggedGamers = []  # socket已连接,但是逻辑还没有登录的玩家
        self.Gamers = GamerGroup(config.game.MaxGamer)  # 已登录的玩家群组
        self.GameRoundPerGamer = config.game.MaxRound  # 每一个玩家出题的循环次数
        self.ServerMessage = ServerMessage()  # 服务器端发送消息模板类
        self.TimerManager = TimerManager()  # 服务器端计时器管理实例,可以方便地创建定时器
        self.GamerCmdListenThreads = []  # 游戏状态消息循环监听的消息接受线程列表
        self.GameLogic = None  # 游戏逻辑实体类
        self.CmdQueue = Queue()  # 监听消息线程间通信队列
        self.GameBeginFlag = ThreadValue(True)  # 指示游戏是否开始的标志
        self.MessageLoopFlag = True  # 指示消息循环是否退出的标志

    def send_cmd_by_id(self, id_, command, **kwargs):
        gamer = self.Gamers.get_gamer_by_id(id_)
        gamer.send_cmd(command=command, **kwargs)

    def send_all_cmd(self, command, **kwargs):
        for g in self.Gamers:
            g.send_cmd(command, **kwargs)

    def send_all_cmd_unlogged(self, command, **kwargs):
        for g in self.UnloggedGamers:
            g.send_cmd(command, **kwargs)

    def run(self):
        logger.info('Server.run', 'Server is running now')
        self.login_state()

        # 对所有gamer,启动监听其请求的线程
        for g in self.Gamers:
            t = threading.Thread(target=g.listen_gamer_command,
                                 args=(self.CmdQueue, ),
                                 daemon=True)
            self.GamerCmdListenThreads.append(t)

        for t in self.GamerCmdListenThreads:
            t.start()

        self.game_state()

    def game_state(self):
        logger.debug('server.gamer_state', 'entering game state')
        # 初始化游戏逻辑
        self.GameLogic = GameLogic(len(self.Gamers))

        for round_index in range(self.GameRoundPerGamer):
            # 游戏循环次数等于玩家数量
            for cur_gamer_index in range(len(self.Gamers)):
                cur_gamer = self.Gamers[cur_gamer_index]

                self.GameLogic.init_game_state(cur_gamer.Id)
                self.send_all_cmd(**make_newround_command())
                # 将当前出题者加入到已回答玩家列表中,防止其自己猜自己
                self.GameLogic.add_answered_gamer_id(cur_gamer.Id)
                # 发送开始画图和通告画图者的通知
                paint_message = self.ServerMessage.make_paint_inform_message(
                    cur_gamer.UserName)
                self.send_all_cmd(**make_inform_command(paint_message))
                # 当前画图者发出开始画图指令
                cur_gamer.send_cmd(**make_begin_paint_command())

                # 进入指令处理循环
                self.MessageLoopFlag = True
                while self.MessageLoopFlag:
                    msg = self.CmdQueue.get()  # 阻塞队列,处理接受到的命令
                    try:
                        cmd, cmd_body = decode_msg(msg, raise_exception=True)
                        handler = get_handler(S_GAME_STATE, cmd)
                        handler(self,
                                cur_gamer=cur_gamer,
                                raw_message=msg,
                                **cmd_body)
                    except DecodeError as de:
                        logger.error(
                            'server.game_state',
                            f'decoding error in game message loop: {de}')
                    except Exception as e:
                        she = ServerHandlingError(cmd, cmd_body, e)
                        logger.error(
                            'server.game_state',
                            f'unknown error when handling in game state: {she}'
                        )

        # 关闭游戏
        self.close()

    def handle_host_begin_game_cmd(self, host_index=0):
        logger.debug('handle_host_cmd', 'host handling threading start!')
        while True:
            if len(self.UnloggedGamers) != 0:
                cmd, body = self.UnloggedGamers[host_index].recv_cmd()
                logger.debug('handle_host_cmd', f'cmd: {cmd}, body:{body}')
                handler = get_handler(S_LOGIN_STATE, cmd, supress_log=True)
                handler(self, gamer=self.UnloggedGamers[host_index], **body)

                # 该线程如果接收到了主机有关游戏开始的命令后就退出
                if cmd == CMD_BEGIN_GAME:
                    return
            else:
                time.sleep(1)

    def login_state(self):
        # 欢迎socket监听3秒就开始监听主机命令
        self.WelcomeSocket.settimeout(config.server.ServerAcceptInterval)
        self.WelcomeSocket.listen(100)  # todo: 划定最大连接数量

        login_threads = []
        # 外层循环接受玩家连接
        while True:
            # 接受玩家连接的套接字
            gamer = self.accept_new_gamer()
            if gamer is None:  # accept内部逻辑会判断是否停止接受更多玩家,以None返回
                break

            # 每一个连接的套接字都启用一个线程处理登录
            login_thread = threading.Thread(None,
                                            target=gamer.check_login,
                                            args=(self, ))
            login_thread.start()

            # 主机线程处理
            if len(self.UnloggedGamers) == 1:
                # 必须等到主机登录线程结束以后再启动主机的监听线程
                # 同时,主机没有登录成功时,阻塞其他玩家的登录
                login_thread.join()
                host_thread = threading.Thread(
                    None, target=self.handle_host_begin_game_cmd, args=(0, ))
                login_threads.append(host_thread)
                host_thread.start()
            else:
                login_threads.append(login_thread)

        # 等待所有玩家登录就绪
        # todo: 玩家登录目前没有超时检查
        for login_thread in login_threads:
            login_thread.join()

        # 关闭欢迎socket
        self.WelcomeSocket.close()

    def accept_new_gamer(self):
        '''
        使用欢迎套接字来接受玩家连接,同时监听Host的开始游戏命令来终止
        接受更多的玩家连接
        :return: 玩家连接的套接字和地址,如果游戏开始则返回None
        '''
        self.WelcomeSocket.settimeout(
            config.server.HostCommandInterval)  # 设置accept退出的检查周期
        while self.GameBeginFlag.get_val():
            try:
                con_socket, addr = self.WelcomeSocket.accept()
                logger.info('Server.login', 'new gamer connecting...')
                gamer = UnloggedGamer(len(self.UnloggedGamers), addr,
                                      con_socket)
                # 接受到新玩家以后加入到gamer列表中
                # 由于accept套接字的时候是串行的,因此不需要互斥读写未登录玩家列表
                self.UnloggedGamers.append(gamer)
                return gamer
            # socket超时后检查游戏开始的全局flag是否被修改
            except socket.timeout:
                pass
        return None

    def close(self):
        self.send_all_cmd(**make_end_game_command())
        for gamer in self.Gamers:
            gamer.close()
        self.WelcomeSocket.close()