class World(object): worlds = [] @staticmethod def clear_all(): for world in World.worlds: world.stop() World.worlds = [] def __init__(self, cols, rows): self.cols = cols self.rows = rows self._objects = [] self._field = [] self._timer = None self.queue = [] self.reproductions = 0 self.plants = 0 self.herbivores = 0 self.predators = 0 self.turns = 0 self.deaths = 0 for x in xrange(cols): self._field.append([ None for y in xrange(rows)]) World.worlds.append(self) def check_queue(self): newqueue = [] for turns, obj in self.queue: if turns == 0: self.add_creature(obj) else: newqueue.append((turns - 1, obj)) return newqueue def loop(self): try: self.queue = self.check_queue() for obj in self._objects: obj.turn(self) deleted = filter(lambda obj: obj.is_nothing, self._objects) #todo: need lock for obj in deleted: logging.debug("Dead: %r, turns: %s, history: %s" % (obj, obj.turns, repr(obj.history.read()))) self._field[obj.x][obj.y] = None self._objects.remove(obj) self.info_update_del(obj) self.deaths += 1 self.stabilize() self.turns += 1 except: import traceback logging.debug(traceback.format_exc()) self._timer.cancel() def start(self, speed): if self._timer: raise Exception() self.init_count_objects = len(self._objects) + len(self.queue) self._timer = Timer(speed, self.loop) self._timer.start() def stop(self): self._timer.cancel() def begin_force(self): self._timer.is_force = True def end_force(self): self._timer.is_force = False def check_position(self, x, y): return 0 <= x < self.cols and 0 <= y < self.rows def add_creature(self, creature): if not self.check_position(creature.x, creature.y): return False if self._field[creature.x][creature.y] is not None: return False self._objects.append(creature) self._field[creature.x][creature.y] = creature self.info_update_add(creature) return True def info_update_add(self, creature): name = creature.__class__.__name__ if name == 'Predator': self.predators += 1 elif name == 'Herbivore': self.herbivores += 1 elif name == 'Plant': self.plants += 1 def info_update_del(self, creature): name = creature.__class__.__name__ if name == 'Predator': self.predators -= 1 elif name == 'Herbivore': self.herbivores -= 1 elif name == 'Plant': self.plants -= 1 def add_creature_square(self, creature): if self.add_creature(creature): return True creature.x -= 1 creature.y -= 1 if self.add_creature(creature): return True creature.x += 1 if self.add_creature(creature): return True creature.x += 1 if self.add_creature(creature): return True creature.y += 1 if self.add_creature(creature): return True creature.y += 1 if self.add_creature(creature): return True creature.x -= 1 if self.add_creature(creature): return True creature.x -= 1 if self.add_creature(creature): return True creature.y -= 1 if self.add_creature(creature): return True return False def get_rnd_free_space(self): free_cells = self.rows * self. cols - len(self._objects) n = randint(1, free_cells) if n == 0: return None for x in xrange(self.cols): for y in xrange(self.rows): if self._field[x][y] is None: n -= 1 if n == 0: return (x, y) return None def get_creature(self, x, y): if not self.check_position(x, y): return return self._field[x][y] def move_creature(self, creature): cell, pos = self.get_creature_by_course(creature) if cell is None and pos is not None: #todo: need lock self._field[creature.x][creature.y] = None self._field[pos[0]][pos[1]] = creature creature.x, creature.y = pos def get_creature_by_course(self, creature): x, y = creature.x, creature.y if creature.course == COURSE_NORTH: x -= 1 elif creature.course == COURSE_EAST: y += 1 elif creature.course == COURSE_SOUTH: x += 1 elif creature.course == COURSE_WEST: y -= 1 if self.check_position(x, y): return self._field[x][y], (x, y) return None, None def stabilize(self): crnt = len(self._objects) + len(self.queue) if crnt < self.init_count_objects: from creatures import Predator, Herbivore, Plant for i in xrange(self.init_count_objects - crnt): pos = self.get_rnd_free_space() if pos is None: break cs = [self.predators, self.herbivores, self.plants] n = cs.index(min(cs)) cls = [Predator, Herbivore, Plant][n] creature = cls(x=pos[0], y=pos[1]) self.add_creature(creature) @staticmethod def load_json(file_path): fp = open(file_path) data = json.load(fp) fp.close() world = World(cols=data['cols'], rows=data['rows']) constructors = dict((unicode(i.__name__), i) for i in [Predator, Herbivore, Plant]) for obj in data['objects']: Create = constructors[obj['type']] creature = Create(x=obj['x'], y=obj['y']) creature.reproductive = obj['reproductive'] creature.current_health = obj['current_health'] creature.base_health = obj['base_health'] if isinstance(obj, Mammals): creature.course = obj['course'] creature.brain.whi = obj['brain']['whi'] creature.brain.woh = obj['brain']['woh'] world.add_creature(creature) world.start(0.1) return world def save_json(self, file_path): objects = [] for obj in self._objects: data = { 'type': obj.__class__.__name__, 'x': obj.x, 'y': obj.y, 'reproductive': obj.reproductive, 'base_health': obj.base_health, 'current_health': obj.current_health, } if isinstance(obj, Mammals): data.update({ 'course': obj.course, 'brain': { 'whi': obj.brain.whi, 'woh': obj.brain.woh, }, }) objects.append(data) data = { 'cols': self.cols, 'rows': self.rows, 'objects': objects, } fp = open(file_path, 'w') json.dump(data, fp, indent = 4) fp.close()
class DanmuWebSocket(WebSocketClient): def __init__(self, info, serveraddress='wss://broadcastlv.chat.bilibili.com/sub'): self.serveraddress = serveraddress WebSocketClient.__init__(self, serveraddress) DanmuWebSocket.event = event DanmuWebSocket.headerLength = 16 self.Info = info def opened(self): self.sendLoginPacket(self.Info['uid'], self.Info['roomid'], self.Info['protover'], self.Info['platform'], self.Info['clientver']) self.sendHeartBeatPacket() self.heartBeatHandler = Timer(20, self.sendHeartBeatPacket) print("opened") def delay_close(self): dws = DanmuWebSocket(self.Info, self.serveraddress) event.emit('reconnect', dws) def closed(self, code, reason=None): print("Closed", code, reason) if hasattr(self, "heartBeatHandler"): self.heartBeatHandler.cancel() if code == 1006: return threading.Timer(5, self.delay_close).start() def received_message(self, message): position, length = 0, len(message.data) - 1 while position < length: header_pack = struct.unpack(">IHHII", message.data[position:position + 16]) length_pack = header_pack[0] operation = header_pack[3] if operation == 3: num = header_pack[1] + position num = struct.unpack(">I", message.data[num:num + 4])[0] event.emit('heartbeat', num) elif operation == 5: data = json.loads(message.data[position + 16:position + length_pack]) event.emit('cmd', data) #print("recv:"+data["cmd"]) else: event.emit('login') position += length_pack def sendData(self, data, protover=1, operation=2, sequence=1): if type(data) == dict: data = json.dumps(data).encode() elif type(data) == str: data = data.encode() header = struct.pack(">IHHII", DanmuWebSocket.headerLength + len(data), DanmuWebSocket.headerLength, protover, operation, sequence) self.send(header + data) def sendLoginPacket(self, uid, roomid, protover=1, platform='web', clientver='1.4.6'): # Uint(4byte) + 00 10 + 00 01 + 00 00 00 07 + 00 00 00 01 + Data 登录数据包 data = { 'uid': int(uid), 'roomid': int(roomid), 'protover': protover, 'platform': platform, 'clientver': clientver } print("sendLoginPacket") data = json.dumps(data) data = data.replace(' ', '') self.sendData(data.encode(), 1, 7, 1) def sendHeartBeatPacket(self): # Uint(4byte) + 00 10 + 00 01 + 00 00 00 02 + 00 00 00 01 + Data 心跳数据包 self.sendData(b'[object Object]', 1, 2, 1) def bind(self, onreconnect=None, onlogin=None, onheartbeat=None, oncmd=None, onreceive=None): if "cmd" in event.keys: return if hasattr(onreconnect, "__call__"): event.on("reconnect", onreconnect) if hasattr(onlogin, "__call__"): event.on("login", onlogin) if hasattr(onheartbeat, "__call__"): event.on("heartbeat", onheartbeat) if hasattr(oncmd, "__call__"): event.on("cmd", oncmd) if hasattr(onreceive, "__call__"): event.on("receive", onreceive)
class Motor: """A class to represent a motor and offers an API to control it. Additionnal documentation can be found here: - http://learn.makeblock.com/en/me-encoder-motor-driver/ - https://github.com/Makeblock-official/Makeblock-Libraries/blob/master/src/MeEncoderMotor.cpp """ HEADER = [0xA5, 0x01] END = 0x5A CMD_MOVE_SPD = 0x05 CMD_MOVE_SPD_TIME = 0x08 CMD_RESET = 0x07 CMD_MOVE_AGL = 0x11 def __init__(self, pin: int, addr: int, slot: int): """Initialize I2C communication to motor. Args: pin: I2C bus' pin (2 or 4) addr: slave's address slot: motor's slot (1 or 2) """ self.__slot = slot - 1 self.__addr = addr self.__i2c = I2C(pin) self.__i2c.init(I2C.MASTER) self.__speed = 0 self._stopper = Timer(callback=self.stop) @property def speed(self): return self.__speed async def __send_data(self, data: list): """Creates a trame from the data and send it to motor via I2C. Args: data: [slot, CMD, args]: data to send """ lrc = self._lrc_calc(data) data_size = self._to_bytes("l", len(data)) trame = Motor.HEADER + data_size + data + [lrc, Motor.END] self.__i2c.send(bytearray(trame), self.__addr) await asyncio.sleep_ms(20) # A few wait time is needed for the motor. def __recv_data(self, length: int) -> bytearray: """Receives data from I2C slave's address Args: length number of bytes to receive Returns: buffer: data received in bytes """ buffer = bytearray(length) self.__i2c.recv(buffer, self.__addr) return buffer def scan(self) -> list: """Scan slaves connected to the current I2C pin. Returns: list_of_slaves: addresses of slaves that respond """ list_of_slaves = self.__i2c.scan() return list_of_slaves async def run(self, speed: float, time=None) -> None: """Controls motor rotation with speed given for an optional time. Args: speed: rotation speed (RPM) in [-200, +200] time: in seconds, runs for a specified time """ if self.__i2c.is_ready(self.__addr): # Sets time limits to [-200 , +200] and convert it in bytes if speed < -200: speed = -200 elif speed > 200: speed = 200 if speed != self.__speed: speed_bytes = self._to_bytes("f", speed) data = [self.__slot, Motor.CMD_MOVE_SPD] + speed_bytes self.__speed = speed await self.__send_data(data) if time: self._stopper.cancel() self._stopper.start(timeout=time) else: raise RuntimeError("Motor {} cannot be run.".format(self.__slot)) async def move(self, angle: float, speed: float) -> None: """Move motor of angle degrees at a speed given. Args: speed: rotation speed (RPM) in [-200, +200] angle: angle in degrees to rotate. """ if speed < -200: speed = -200 elif speed > 200: speed = 200 if speed != self.__speed: self.__speed = speed time = (angle / 360 * 60) / speed await self.run(speed, time) async def stop(self): """Reset motor position to 0 and reinitialize data received.""" data = [self.__slot, Motor.CMD_RESET] await self.__send_data(data) self.__speed = 0 @staticmethod def _lrc_calc(data) -> int: """Calculate the Longitudinal Redondancy Check (LRC) Returns: lrc: the value of LRC """ lrc = 0x00 for byte in data: lrc ^= byte return lrc @staticmethod def _to_bytes(fmt: str, data) -> list: """Convert and pack data with a given format The list of available formats can be found here: https://docs.python.org/3/library/struct.html Args: fmt: string used to pack the data from a given format. data: data to be converted to bytes. Returns: data_bytes: a list of each element of data converted to bytes. """ data_bytes = list(struct.pack(fmt, data)) return data_bytes