def testGetSkoobotsByName(self): """ Test the getSkoobotsByName() method The method should return a list of (addr, name) tuples for all skoobots matching name """ setUpRegistry = SkoobotRegistry(self.tempPath) names = (self.skooName, self.skooDupName, "nobody", None) for name in names: with self.subTest(name=name): skoobots = setUpRegistry.getSkoobotsByName(name) if name == self.skooDupName: self.assertEqual(2, len(skoobots)) for skoobot in skoobots: self.assertEqual(self.skooDupName, skoobot[1]) # Make a list of just the addresses skooDupAddrs = [skoo[0] for skoo in skoobots] self.assertIn(self.skooDupAddr1, skooDupAddrs) self.assertIn(self.skooDupAddr2, skooDupAddrs) elif name == self.skooName: self.assertEqual(1, len(skoobots)) # There is only 1 skoobot, so test it skoobot = skoobots[0] self.assertEqual(self.skooName, skoobot[1]) self.assertEqual(self.skooAddr, skoobot[0]) else: self.assertEqual(0, len(skoobots))
def testAddSkoobot(self): """ Test addition of skoobots using the addSkoobot() method The method adds a skoobot to the registry using an address and an optional name. """ registry = SkoobotRegistry(self.tempPath) namedAddr = "ff:ff:ff:ff:ff:ff" namedName = "newSkoobot" unnamedAddr = "ff:ff:ff:ff:ff:fe" with self.subTest("Add named Skoobot"): registry.addSkoobot(namedAddr, namedName) self.assertEqual(4, len(registry.registry)) self.assertEqual(1, len(registry.getSkoobotsByAddress(namedAddr))) self.assertEqual(1, len(registry.getSkoobotsByName(namedName))) with self.subTest("Add unnamed Skoobot"): registry.addSkoobot(unnamedAddr) self.assertEqual(5, len(registry.registry)) skoobots = registry.getSkoobotsByAddress(unnamedAddr) self.assertEqual(1, len(skoobots)) self.assertIn(skoobots[0][1], registry.skoobotNames) with self.subTest("Add duplicate Skoobot"): # Bug #7: By default this replaces the existing # skoobot. If the replace=False parameter is set, # it raises a RuntimeError unless the parameters # are compatible with the existing entry. # # It is always true that it does not result in a # duplicate address. registry.addSkoobot(namedAddr, namedName) self.assertEqual(5, len(registry.registry)) registry.addSkoobot(namedAddr, replace=False) self.assertEqual(5, len(registry.registry)) with self.assertRaises(RuntimeError): registry.addSkoobot(unnamedAddr, namedName, replace=False) with self.subTest("Test invalid parameters"): with self.assertRaises(TypeError): registry.addSkoobot((namedAddr, namedName)) with self.assertRaises(TypeError): registry.addSkoobot(namedAddr, (namedAddr, namedName))
class SkoobotController: """ Control API for Skoobots """ def __init__(self): self.transport = TransportBluepy() self.registry = SkoobotRegistry() # Table of characteristic name to uuid mappings. # The characteristic names used are the ones in the firmware. self.uuids = { "cmd": "00001525-1212-efde-1523-785feabcd123", "data": "00001524-1212-efde-1523-785feabcd123", "byte2": "00001526-1212-efde-1523-785feabcd123", "byte128": "00001527-1212-efde-1523-785feabcd123", "byte4": "00001528-1212-efde-1523-785feabcd123", } self.connectedSkoobot = None def connect(self, name=None, addr=None): """ Connect to the given Skoobot. If no Skoobot is given, connects to the default. Returns the address of the connected Skoobot if successful; None otherwise. """ addrList = [] if addr != None: if isinstance(addr, str): addrList.append(addr) else: raise TypeError("addr should be a string") else: if name == None: name = self.registry.getDefaultName() elif not isinstance(name, str): raise TypeError("name should be a string") if name != None: skoobots = self.registry.getSkoobotsByName(name) addrList = [bot[0] for bot in skoobots] else: raise RuntimeError("No default skoobot defined") if len(addrList) == 0: raise ValueError( "No Skoobot with name {0:s} found".format(name)) if self.connectedSkoobot != None: self.disconnect() for botAddr in addrList: try: self.transport.connect(botAddr) self.connectedSkoobot = botAddr break except BTLEException: pass return self.connectedSkoobot def disconnect(self): self.transport.disconnect() self.connectedSkoobot = None def sendCommand(self, data, waitForResponse=False): if self.connectedSkoobot == None: raise RuntimeError("BLE not connected") data = int(data) cmdBytes = data.to_bytes(1, byteorder="little") characteristics = self.transport.getRawCharacteristicsByUUID( self.uuids["cmd"]) if len(characteristics) == 0: raise RuntimeError("cmd characteristic not supported by firmware") cmd = characteristics[0] cmd.write(cmdBytes, waitForResponse) def readBytes(self, charName="data"): """ Read an array of bytes from the named characteristic returns a bytearray of the data """ if self.connectedSkoobot == None: raise RuntimeError("BLE not connected") characteristics = self.transport.getRawCharacteristicsByUUID( self.uuids[charName]) if len(characteristics) == 0: raise RuntimeError( "{0:s} characteristic not supported by firmware".format( charName)) charac = characteristics[0] dataBytes = charac.read() return dataBytes def readData(self, charName="data"): dataBytes = self.readBytes(charName) value = int.from_bytes(dataBytes, byteorder="little") return value def cmdRight(self): self.sendCommand(CMD_RIGHT, True) def cmdLeft(self): self.sendCommand(CMD_LEFT, True) def cmdForward(self): self.sendCommand(CMD_FORWARD, True) def cmdBackward(self): self.sendCommand(CMD_BACKWARD, True) def cmdStop(self): self.sendCommand(CMD_STOP, True) def cmdSleep(self): self.sendCommand(CMD_SLEEP, True) def cmdRoverMode(self): self.sendCommand(CMD_ROVER_MODE, True) def requestDistance(self): self.sendCommand(CMD_GET_DISTANCE, True) return self.readData() def requestAmbientLight(self): self.sendCommand(CMD_GET_AMBIENT, True) return self.readData("byte2")