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)
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()