Example #1
0
 def __init__(self):
     self.devices = {}
     self.proxies = {}
     self.sequencer = Sequencer(self)
     self.sequencer.start()
     self.logHandler = ControllerLogHandler()
     logging.getLogger().addHandler(self.logHandler)
     self.clients = []
     self.slaves = []
     self.daemon = Pyro4.Daemon(PyroUtils.getHostname())
Example #2
0
 def __init__(self):
     self.devices = {}
     self.proxies = {}
     self.sequencer = Sequencer(self)
     self.sequencer.start()
     self.logHandler = ControllerLogHandler()
     logging.getLogger().addHandler(self.logHandler)
     self.clients = []
     self.slaves = []
     self.daemon = Pyro4.Daemon(PyroUtils.getHostname())
Example #3
0
 def setUp(self):
     self.controller = Controller()
     self.sequencer = Sequencer(self.controller)
Example #4
0
class TestSequencer(unittest.TestCase):
    def setUp(self):
        self.controller = Controller()
        self.sequencer = Sequencer(self.controller)

    def performSequenceTest(self, *events):
        self.sequencer.sequence(*events)
        self.sequencer.start()
        sleep(len(events) + 1)

    def testSimpleEvent(self):
        m = MagicMock()
        m.deviceID = "Mock"

        e1 = Event(m.frobnicate, "badger")

        self.performSequenceTest(e1)

        m.frobnicate.assert_called_once_with("badger")

    def testControllerEvent(self):
        self.controller.frobnicate = MagicMock(return_value=True)
        e = ControllerEvent("frobnicate", "badger")

        self.performSequenceTest(e)

        self.controller.frobnicate.assert_called_once_with("badger")

    def testDeviceEvent(self):
        m = MagicMock()
        m.deviceID = "Mock"

        self.controller.addDevice(m)

        e = DeviceEvent("Mock", "frobnicate", "badger")

        self.performSequenceTest(e)

        m.frobnicate.assert_called_once_with("badger")

    def testCompositeEvent(self):
        m = MagicMock()
        e1 = Event(m, "one")
        e2 = Event(m, "two")
        ce = CompositeEvent(e1, e2)

        self.performSequenceTest(ce)

        m.assert_has_calls([call("one"), call("two")])

    @patch("avx.Sequencer.time")
    def testSleepEvent(self, time):
        e = SleepEvent(5)
        self.performSequenceTest(e)
        # N.B. time.sleep is also called by the sequencer itself
        time.sleep.assert_has_calls([call(5)])

    @patch("avx.Sequencer.logging")
    def testLogEvent(self, logging):
        e = LogEvent(logging.INFO, "This is informational")
        self.performSequenceTest(e)
        logging.log.assert_called_once_with(logging.INFO, "This is informational")
Example #5
0
class Controller(object):
    '''
    A Controller is essentially a bucket of devices, each identified with a string deviceID.
    '''
    pyroName = "avx.controller"
    version = _version.__version__

    def __init__(self):
        self.devices = {}
        self.proxies = {}
        self.sequencer = Sequencer(self)
        self.sequencer.start()
        self.logHandler = ControllerLogHandler()
        logging.getLogger().addHandler(self.logHandler)
        self.clients = []
        self.slaves = []
        self.daemon = Pyro4.Daemon(PyroUtils.getHostname())

    @staticmethod
    def fromPyro(controllerID=""):
        controllerAddress = "PYRONAME:" + Controller.pyroName
        if controllerID != "":
            controllerAddress += "." + controllerID
        logging.info("Creating proxy to controller at " + controllerAddress)

        controller = ControllerProxy(Pyro4.Proxy(controllerAddress))
        remoteVersion = controller.getVersion()
        if not versionsCompatible(remoteVersion, Controller.version):
            raise VersionMismatchError(remoteVersion, Controller.version)
        return controller

    def loadConfig(self, configFile):
        try:
            if isinstance(configFile, file):
                config = json.load(configFile)
            else:
                config = json.load(open(configFile))
            for d in config["devices"]:
                device = Device.create(d, self)
                self.addDevice(device)

            if "options" in config:

                if "controllerID" in config["options"]:
                    self.controllerID = config["options"]["controllerID"]

                if "slaves" in config["options"]:
                    for slave in config["options"]["slaves"]:
                        try:
                            sc = Controller.fromPyro(slave)

                            if versionsCompatible(sc.getVersion(), self.getVersion()):
                                self.slaves.append(sc)
                            else:
                                logging.error("This Controller is version " + str(self.getVersion()) + " but tried to add slave " + slave + " of version " + str(sc.getVersion()))
                        except NamingError:
                            logging.error("Could not connect to slave with controller ID " + slave)

                if "http" in config["options"]:
                    if config["options"]["http"] is True:
                        ch = ControllerHttp(self)
                        ch.start()

        except ValueError:
            logging.exception("Cannot parse config.json:")

    def registerClient(self, clientURI):
        self.clients.append(clientURI)
        logging.info("Registered client at " + str(clientURI))
        logging.info(str(len(self.clients)) + " client(s) now connected")

    def unregisterClient(self, clientURI):
        self.clients.remove(clientURI)
        logging.info("Unregistered client at " + str(clientURI))
        logging.info(str(len(self.clients)) + " client(s) still connected")

    def callAllClients(self, function):
        ''' function should take a client and do things to it'''
        for uri in self.clients:
            try:
                logging.debug("Calling function " + function.__name__ + " with client at " + str(uri))
                client = Pyro4.Proxy(uri)
                result = function(client)
                logging.debug("Client call returned " + str(result))
            except:
                logging.exception("Failed to call function on registered client " + str(uri) + ", removing.")
                self.clients.pop(uri)

    def getVersion(self):
        return self.version

    def addDevice(self, device):
        if self.hasDevice(device.deviceID):
            raise DuplicateDeviceIDError(device.deviceID)
        self.devices[device.deviceID] = device
        if hasattr(device, "registerDispatcher") and callable(getattr(device, "registerDispatcher")):
            device.registerDispatcher(self)

    def getDevice(self, deviceID):
        return self.devices[deviceID]

    def proxyDevice(self, deviceID):
        if deviceID not in self.proxies.keys():
            if self.hasDevice(deviceID):
                self.proxies[deviceID] = self.daemon.register(self.getDevice(deviceID))
            else:
                for slave in self.slaves:
                    if slave.hasDevice(deviceID):
                        self.proxies[deviceID] = slave.proxyDevice(deviceID)
        return self.proxies[deviceID]

    def hasDevice(self, deviceID):
        return deviceID in self.devices

    def initialise(self):
        for device in self.devices.itervalues():
            device.initialise()
        atexit.register(self.deinitialise)

    def deinitialise(self):
        for device in self.devices.itervalues():
            device.deinitialise()

    def startServing(self):
        PyroUtils.setHostname()

        ns = Pyro4.locateNS()
        uri = self.daemon.register(self)

        if hasattr(self, "controllerID"):
            name = self.pyroName + "." + self.controllerID
        else:
            name = self.pyroName

        logging.info("Registering controller as " + name)

        ns.register(name, uri)

        atexit.register(lambda: self.daemon.shutdown())

        self.daemon.requestLoop()

    def sequence(self, *events):
        self.sequencer.sequence(*events)

    def showPowerOnDialogOnClients(self):
        self.callAllClients(lambda c: c.showPowerOnDialog())

    def showPowerOffDialogOnClients(self):
        self.callAllClients(lambda c: c.showPowerOffDialog())

    def hidePowerDialogOnClients(self):
        self.callAllClients(lambda c: c.hidePowerDialog())

    def getLog(self):
        return self.logHandler.entries

    def updateOutputMappings(self, mapping):
        self.callAllClients(lambda c: c.updateOutputMappings(mapping))
Example #6
0
class Controller(object):
    '''
    A Controller is essentially a bucket of devices, each identified with a string deviceID.
    '''
    pyroName = "avx.controller"
    version = _version.__version__

    def __init__(self):
        self.devices = {}
        self.proxies = {}
        self.sequencer = Sequencer(self)
        self.sequencer.start()
        self.logHandler = ControllerLogHandler()
        logging.getLogger().addHandler(self.logHandler)
        self.clients = []
        self.slaves = []
        self.daemon = Pyro4.Daemon(PyroUtils.getHostname())

    @staticmethod
    def fromPyro(controllerID=""):
        controllerAddress = "PYRONAME:" + Controller.pyroName
        if controllerID != "":
            controllerAddress += "." + controllerID
        logging.info("Creating proxy to controller at " + controllerAddress)

        controller = ControllerProxy(Pyro4.Proxy(controllerAddress))
        remoteVersion = controller.getVersion()
        if not versionsCompatible(remoteVersion, Controller.version):
            raise VersionMismatchError(remoteVersion, Controller.version)
        return controller

    def loadConfig(self, configFile, overrideToDebug=False):
        try:
            if isinstance(configFile, file):
                config = json.load(configFile)
                self.configFile = configFile.name
            else:
                config = json.load(open(configFile))
                self.configFile = configFile

            self.config = config

            for d in config["devices"]:
                device = Device.create(d, self)
                self.addDevice(device)

            if "options" in config:

                if "controllerID" in config["options"]:
                    self.controllerID = config["options"]["controllerID"]

                if "slaves" in config["options"]:
                    for slave in config["options"]["slaves"]:
                        try:
                            sc = Controller.fromPyro(slave)

                            if versionsCompatible(sc.getVersion(), self.getVersion()):
                                self.slaves.append(sc)
                            else:
                                logging.error("This Controller is version " + str(self.getVersion()) + " but tried to add slave " + slave + " of version " + str(sc.getVersion()))
                        except NamingError:
                            logging.error("Could not connect to slave with controller ID " + slave)

                if "http" in config["options"]:
                    if config["options"]["http"] is True:
                        ch = ControllerHttp(self)
                        ch.start()

            if "logging" in config:
                logging.config.dictConfig(config["logging"])
                if overrideToDebug:
                    logging.getLogger().setLevel(logging.DEBUG)
                    logging.info("-d specified, overriding any specified default logger level to DEBUG")

            if "clients" in config:
                self.clients = list(config["clients"])
            self.config["clients"] = self.clients

        except ValueError:
            logging.exception("Cannot parse config.json!")

    def saveConfig(self):
        if hasattr(self, "configFile"):
            try:
                with open(self.configFile, "w") as cfout:
                    json.dump(self.config, cfout)
            except IOError:
                logging.exception("Cannot save config file!")

    def registerClient(self, clientURI):
        self.clients.append(str(clientURI))
        logging.info("Registered client at " + str(clientURI))
        logging.info(str(len(self.clients)) + " client(s) now connected")
        self.saveConfig()

    def unregisterClient(self, clientURI):
        self.clients.remove(str(clientURI))
        logging.info("Unregistered client at " + str(clientURI))
        logging.info(str(len(self.clients)) + " client(s) still connected")
        self.saveConfig()

    def broadcast(self, msgType, source, data=None):
        ''' Send a message to all clients '''
        logging.debug("Broadcast: {}, {}, {}".format(msgType, source, data))
        for uri in list(self.clients):
            try:
                logging.debug("Calling handleMessage on client at {}".format(uri))
                client = Pyro4.Proxy(uri)
                client._pyroTimeout = 1
                result = client.handleMessage(msgType, source, data)
                logging.debug("Client call returned " + str(result))
            except Exception:
                logging.exception("Failed to call handleMessage on registered client {}, removing.".format(uri))
                self.unregisterClient(uri)

    def getVersion(self):
        return self.version

    def addDevice(self, device):
        if self.hasDevice(device.deviceID):
            raise DuplicateDeviceIDError(device.deviceID)
        self.devices[device.deviceID] = device
        device.broadcast = lambda t, b=None: self.broadcast(t, device.deviceID, b)

    def getDevice(self, deviceID):
        return self.devices[deviceID]

    def proxyDevice(self, deviceID):
        if deviceID not in self.proxies.keys():
            if self.hasDevice(deviceID):
                self.proxies[deviceID] = self.daemon.register(self.getDevice(deviceID))
            else:
                for slave in self.slaves:
                    if slave.hasDevice(deviceID):
                        self.proxies[deviceID] = slave.proxyDevice(deviceID)
        return self.proxies[deviceID]

    def hasDevice(self, deviceID):
        return deviceID in self.devices

    def initialise(self):
        for device in self.devices.itervalues():
            device.initialise()
        atexit.register(self.deinitialise)

    def deinitialise(self):
        for device in self.devices.itervalues():
            device.deinitialise()

    def startServing(self):
        PyroUtils.setHostname()

        ns = Pyro4.locateNS()
        uri = self.daemon.register(self)

        if hasattr(self, "controllerID"):
            name = self.pyroName + "." + self.controllerID
        else:
            name = self.pyroName

        logging.info("Registering controller as " + name)

        ns.register(name, uri)

        atexit.register(lambda: self.daemon.shutdown())

        self.daemon.requestLoop()

    def sequence(self, *events):
        self.sequencer.sequence(*events)

    def getLog(self):
        return self.logHandler.entries
Example #7
0
class Controller(object):
    '''
    A Controller is essentially a bucket of devices, each identified with a string deviceID.
    '''
    pyroName = "avx.controller"
    version = _version.__version__

    def __init__(self):
        self.devices = {}
        self.proxies = {}
        self.sequencer = Sequencer(self)
        self.sequencer.start()
        self.logHandler = ControllerLogHandler()
        logging.getLogger().addHandler(self.logHandler)
        self.clients = []
        self.slaves = []
        self.daemon = Pyro4.Daemon(PyroUtils.getHostname())

    @staticmethod
    def fromPyro(controllerID=""):
        controllerAddress = "PYRONAME:" + Controller.pyroName
        if controllerID != "":
            controllerAddress += "." + controllerID
        logging.info("Creating proxy to controller at " + controllerAddress)

        controller = ControllerProxy(Pyro4.Proxy(controllerAddress))
        remoteVersion = controller.getVersion()
        if not versionsCompatible(remoteVersion, Controller.version):
            raise VersionMismatchError(remoteVersion, Controller.version)
        return controller

    def loadConfig(self, configFile):
        try:
            if isinstance(configFile, file):
                config = json.load(configFile)
            else:
                config = json.load(open(configFile))
            for d in config["devices"]:
                device = Device.create(d, self)
                self.addDevice(device)

            if "options" in config:

                if "controllerID" in config["options"]:
                    self.controllerID = config["options"]["controllerID"]

                if "slaves" in config["options"]:
                    for slave in config["options"]["slaves"]:
                        try:
                            sc = Controller.fromPyro(slave)

                            if versionsCompatible(sc.getVersion(),
                                                  self.getVersion()):
                                self.slaves.append(sc)
                            else:
                                logging.error("This Controller is version " +
                                              str(self.getVersion()) +
                                              " but tried to add slave " +
                                              slave + " of version " +
                                              str(sc.getVersion()))
                        except NamingError:
                            logging.error(
                                "Could not connect to slave with controller ID "
                                + slave)

                if "http" in config["options"]:
                    if config["options"]["http"] is True:
                        ch = ControllerHttp(self)
                        ch.start()

        except ValueError:
            logging.exception("Cannot parse config.json:")

    def registerClient(self, clientURI):
        self.clients.append(clientURI)
        logging.info("Registered client at " + str(clientURI))
        logging.info(str(len(self.clients)) + " client(s) now connected")

    def unregisterClient(self, clientURI):
        self.clients.remove(clientURI)
        logging.info("Unregistered client at " + str(clientURI))
        logging.info(str(len(self.clients)) + " client(s) still connected")

    def callAllClients(self, function):
        ''' function should take a client and do things to it'''
        for uri in self.clients:
            try:
                logging.debug("Calling function " + function.__name__ +
                              " with client at " + str(uri))
                client = Pyro4.Proxy(uri)
                result = function(client)
                logging.debug("Client call returned " + str(result))
            except:
                logging.exception(
                    "Failed to call function on registered client " +
                    str(uri) + ", removing.")
                self.clients.pop(uri)

    def getVersion(self):
        return self.version

    def addDevice(self, device):
        if self.hasDevice(device.deviceID):
            raise DuplicateDeviceIDError(device.deviceID)
        self.devices[device.deviceID] = device
        if hasattr(device, "registerDispatcher") and callable(
                getattr(device, "registerDispatcher")):
            device.registerDispatcher(self)

    def getDevice(self, deviceID):
        return self.devices[deviceID]

    def proxyDevice(self, deviceID):
        if deviceID not in self.proxies.keys():
            if self.hasDevice(deviceID):
                self.proxies[deviceID] = self.daemon.register(
                    self.getDevice(deviceID))
            else:
                for slave in self.slaves:
                    if slave.hasDevice(deviceID):
                        self.proxies[deviceID] = slave.proxyDevice(deviceID)
        return self.proxies[deviceID]

    def hasDevice(self, deviceID):
        return deviceID in self.devices

    def initialise(self):
        for device in self.devices.itervalues():
            device.initialise()
        atexit.register(self.deinitialise)

    def deinitialise(self):
        for device in self.devices.itervalues():
            device.deinitialise()

    def startServing(self):
        PyroUtils.setHostname()

        ns = Pyro4.locateNS()
        uri = self.daemon.register(self)

        if hasattr(self, "controllerID"):
            name = self.pyroName + "." + self.controllerID
        else:
            name = self.pyroName

        logging.info("Registering controller as " + name)

        ns.register(name, uri)

        atexit.register(lambda: self.daemon.shutdown())

        self.daemon.requestLoop()

    def sequence(self, *events):
        self.sequencer.sequence(*events)

    def showPowerOnDialogOnClients(self):
        self.callAllClients(lambda c: c.showPowerOnDialog())

    def showPowerOffDialogOnClients(self):
        self.callAllClients(lambda c: c.showPowerOffDialog())

    def hidePowerDialogOnClients(self):
        self.callAllClients(lambda c: c.hidePowerDialog())

    def getLog(self):
        return self.logHandler.entries

    def updateOutputMappings(self, mapping):
        self.callAllClients(lambda c: c.updateOutputMappings(mapping))