Ejemplo n.º 1
0
    def __init__(self, config_file, pid_file, port=8123):
        """
        Initialization.

        Args:
            config_file: The configure file path
            pid_file: The pid file path for daemonization
            port: The server's port, using 8123 by default
        """

        # Initializing parent class
        super(SwitchController, self).__init__(pid_file)

        # Switch Protocol parser
        self.proto = SwitchProtocol()

        # Plugs list
        self.config_file = config_file
        if not os.path.isfile(config_file):
            with open(config_file, 'w'):
                self.plugs = []
        else:
            with open(config_file, 'r') as config:
                self.plugs = json.load(config)
                self.lock = asyncio.Lock()

        self.PLUG_ID        = 'id'
        self.PLUG_NAME      = 'name'
        self.PLUG_STATUS    = 'status'

        # Controller (Raspberry PI)'s address
        self.controller = None

        # Initializing main event loop and server
        self.event_loop = asyncio.get_event_loop()

        self.host = ''
        self.port = port

        self.server_coro = asyncio.start_server(self.req_handler,
                                           self.host, self.port,
                                           loop = self.event_loop)
Ejemplo n.º 2
0
class SwitchController(DaemonProcess):
    """
    TCP Server for plug switch controlling.
    """

    def __init__(self, config_file, pid_file, port=8123):
        """
        Initialization.

        Args:
            config_file: The configure file path
            pid_file: The pid file path for daemonization
            port: The server's port, using 8123 by default
        """

        # Initializing parent class
        super(SwitchController, self).__init__(pid_file)

        # Switch Protocol parser
        self.proto = SwitchProtocol()

        # Plugs list
        self.config_file = config_file
        if not os.path.isfile(config_file):
            with open(config_file, 'w'):
                self.plugs = []
        else:
            with open(config_file, 'r') as config:
                self.plugs = json.load(config)
                self.lock = asyncio.Lock()

        self.PLUG_ID        = 'id'
        self.PLUG_NAME      = 'name'
        self.PLUG_STATUS    = 'status'

        # Controller (Raspberry PI)'s address
        self.controller = None

        # Initializing main event loop and server
        self.event_loop = asyncio.get_event_loop()

        self.host = ''
        self.port = port

        self.server_coro = asyncio.start_server(self.req_handler,
                                           self.host, self.port,
                                           loop = self.event_loop)


    def run(self):
        """
        Overriding the run function in parent class.
        The event loop begins from here.
        """

        # Generating asynchronous server object
        self.event_loop.run_until_complete(self.server_coro)

        # Main event loop begins to work
        self.event_loop.run_forever()


    async def req_handler(self, reader, writer):
        """
        Coroutine: Handling the request from client application.

        Args:
            reader:
                StreamReader object, read request from client.
            writer:
                StreamWriter object, write response to client.
        """

        req_raw = await reader.read(self.proto.PROTO_MAX_LEN)
        parsed_req = self.proto.parser(req_raw)

        performed = await self.perform(parsed_req, writer)

        if performed != self.proto.OP_REG:
            writer.close()


    async def perform(self, parsed_req, writer):
        """
        Coroutine: Performing operation according to request.

        Args:
            parsed_req: The parsed request generated by self.req_parser.
            writer: StreamWriter object, write response to client.

        Returns:
            The operation types or the None object
        """

        response = ''

        if parsed_req is None:
            return None
        elif parsed_req[0] == self.proto.OP_REFRESH:
            pass
        elif parsed_req[0] == self.proto.OP_CONTROL:
            await self._update_plug(parsed_req[1], parsed_req[2], parsed_req[3])
        elif parsed_req[0] == self.proto.OP_DELETE:
            await self._delete_plug(parsed_req[1], parsed_req[2])
        elif parsed_req[0] == self.proto.OP_ADD:
            await self._add_plug(parsed_req[1], parsed_req[2], parsed_req[3],
                                 parsed_req[4])
        elif parsed_req[0] == self.proto.OP_REG:
            if self.controller is not None:
                self.controller.close()
            self.controller = writer
            print(self.controller)
            return self.proto.OP_REG
        else:
            return None

        response = await self._plugs2str()
        writer.write(response.encode())

        return parsed_req[0]


    async def _plugs2str(self):
        """
        Coroutine: Convert the plug dictionary to string for client.

        Returns:
            The response content in string format.
        """

        response = ''

        with await self.lock:
            for plug in self.plugs:
                response += ';'.join([plug[self.PLUG_ID],
                                      plug[self.PLUG_NAME],
                                      plug[self.PLUG_STATUS]])
                response += ';'

        return response


    async def _update_plug(self, house, code, status):
        """
        Coroutine: Updating a plug's status

        Args:
            house: The house code of plug
            code: The unique code of plug
            status: The target status of plug
        """

        identifier = house + code

        with await self.lock:
            list_len = len(self.plugs)
            for plug_idx in range(list_len):
                if identifier == self.plugs[plug_idx][self.PLUG_ID]:
                    self.plugs[plug_idx][self.PLUG_STATUS] = status
                    await self._call_controller(identifier, status)
                    break

        await self._update_config()


    async def _delete_plug(self, house, code):
        """
        Coroutine: Deleting a plug

        Args:
            house: The house code of plug
            code: The unique code of plug
        """

        identifier = house + code

        with await self.lock:
            self.plugs = \
                list(filter(lambda plug : plug[self.PLUG_ID] != identifier,
                     self.plugs))

        await self._update_config()


    async def _add_plug(self, house, code, name, status='off'):
        """
        Coroutine: Adding a plug

        Args:
            house: The house code of plug
            code: The unique code of plug
            name: The name of the plug
            status: The target status of plug
        """

        identifier = house + code

        with await self.lock:
            existence = False
            for plug in self.plugs:
                if plug[self.PLUG_ID] == identifier:
                    plug[self.PLUG_NAME] = name
                    plug[self.PLUG_STATUS] = status
                    existence = True
                    await self._call_controller(identifier, status)

            if not existence:
                self.plugs.append({self.PLUG_ID : identifier,
                                   self.PLUG_NAME : name,
                                   self.PLUG_STATUS : status})

        await self._update_config()


    async def _update_config(self):
        """
        Coroutine: Updating the configure file.
        """

        with await self.lock, open(self.config_file, 'w') as config:
            json.dump(self.plugs, config)


    async def _call_controller(self, identifier, status):
        """
        Coroutine: Sending commands to Raspberry PI

        Args:
            identifier: Plug's ID
            status: The new status.
        """

        if self.controller is None:
            return
        else:
            content = '%s;%s' % (identifier, status)
            self.controller.write(content.encode())
            self.controller.drain()