Пример #1
0
def __scan_and_load_wrh_modules(path):
    if not os.path.exists(path):
        log(f'Path {path} for additional WRH modules does not exist')
        return ()

    from modules.base import ModuleBase
    classes = []
    for mdir in (e for e in os.listdir(path)
                 if e[0] != '.' and os.path.isdir(pjoin(path, e))):
        _path = pjoin(path, mdir)
        if '__init__.py' not in os.listdir(_path): continue
        module_files = (
            f for f in os.listdir(_path)
            if f[0] != '.' and f != '__init__.py' and f[-3:] == '.py')
        modules = [
            import_module(str(pjoin(_path, m)).replace(os.sep, '.')[:-3])
            for m in module_files
        ]
        attributes = [
            getattr(module, attr) for module in modules
            for attr in module.__dict__
        ]
        classes.extend(
            attr for attr in attributes
            if isinstance(attr, type) and issubclass(attr, ModuleBase)
            and issubclass(attr, Base) and getattr(attr, 'WRHID', None))
    return tuple(classes)
Пример #2
0
 def run_registration_procedure(self, new_id):
     """
     Runs interactive procedure to register new module.
     """
     log(f'*** Registering new {self.TYPE_NAME} module ***')
     self.id = new_id
     self.name = wrh_input('Module name: ')
Пример #3
0
 async def prepare(self):
     # TODO: Check that incoming connections are are only from within VPN!
     if not self.last_modules_update or abs(datetime.now() - self.last_modules_update) > self.MODULES_CACHE_TIME:
         log('Invalidating cached modules list and syncing new one')
         BaseWrhForm.last_modules_update = datetime.now()
         BaseWrhForm.modules = await self._get_modules()
         BaseWrhForm.installed_module_types = list({m.type for m in self.modules})
 def time_changed(self, date, rango_port):
     """
     Reacts to time change and runs scenario (self) if needed.
     :param date: current date (day of the month, etc.)
     :param rango_port: port of the Rango Iryga system
     """
     if self._should_activate(date):
         log('Scenario created from request string {} has been activated'.
             format(self.request))
         self._activate(rango_port)
Пример #5
0
def print_process_errors(process):
    """
    Awaits process error information on the STDERR stream.
    Process should be piped!
    This method does not return until process finishes!
    :param process: process to be polled
    """
    while does_process_exist(process):
        _, err = process.communicate()
        log(err.decode('utf_8'), Color.FAIL)
Пример #6
0
 def __init__(self, port, tornado_port):
     self.socket = None
     self.port = port
     self._should_end = False
     log(
         'Following additional db models have been found: \n*{}'.format(
             '\n*'.join(str(m.__name__) for m in WRH_MODULES)), Color.BLUE)
     self.wrh_modules = {m.WRHID: m for m in WRH_MODULES}
     self._read_db_configuration()
     self.tornado_server = TornadoServer(tornado_port, self.sessionmaker)
Пример #7
0
 def _edit_module(self):
     log('\nChoose which module to edit')
     [log('%d) %s named %s' % (i, module.TYPE_NAME, module.name)) for i, module in enumerate(self.installed_modules)]
     choice = ninput('> ')
     try:
         self.installed_modules[int(choice)].edit()
         self.configuration_parser.save_configuration(self.installed_modules)
         log("Success!", Color.GREEN)
     except (KeyError, ValueError, IndexError):
         pass
Пример #8
0
 def _remove_module(self):
     log('\nChoose which module to remove')
     [log(f'{i}) {module.TYPE_NAME} named {module.name}') for i, module in enumerate(self.installed_modules)]
     choice = ninput('> ')
     try:
         del self.installed_modules[int(choice)]
         self.configuration_parser.save_configuration(self.installed_modules)
         log('Success!', Color.GREEN)
     except (KeyError, ValueError, IndexError):
         pass
Пример #9
0
 def _delete_client(self, session):
     log('\n*** Existing clients ***')
     [
         log(f'{client.id} --- {client.name}', Color.BLUE)
         for client in self.wrh_clients.values()
     ]
     client_id = npinput(message='Id of client to remove: ')
     to_remove = session.query(WRHClient).filter(
         WRHClient.id == client_id).first()
     if to_remove:
         session.delete(to_remove)
Пример #10
0
 def start_work(self):
     if not self.wrh_clients:
         log('No point in running while there are no clients configured!',
             Color.WARNING)
     else:
         self._should_end = False
         signal.signal(signal.SIGINT, self._sigint_handler)
         self._start_tornado()
         self._await_connections()
         log('Stopping work')
         signal.signal(signal.SIGINT, signal.SIG_DFL)
Пример #11
0
 def _modify_client(self, session):
     log('\n*** Existing clients ***')
     [
         log(f'{client.id} --- {client.name}', Color.BLUE)
         for client in self.wrh_clients.values()
     ]
     client_id = npinput(message='Id of client to edit: ')
     to_edit = session.query(WRHClient).filter(
         WRHClient.id == client_id).first()
     if to_edit:
         to_edit.name = wrh_input(message='Input new name of the client: ')
Пример #12
0
 def _show_options(self):
     actions = {'1': self._edit_module,
                '2': self._add_new_module,
                '3': self._remove_module,
                '4': self._run_system}
     while True:
         self.should_end = False
         log('\n[1] Edit module\n[2] Add new module\n[3] Delete module\n[4] Start modules\n[5] Exit')
         choice = ninput('> ')
         if choice == '5': break
         if choice not in actions: continue
         actions[choice]()
Пример #13
0
 def _read_db_configuration(self, _file_=None):
     try:
         log('Connecting to database')
         db_configuration = json.loads(_file_.read())
         self.db_engine = create_engine(
             '{dialect}://{username}:{password}@{host}:{port}/{database}'.
             format(**db_configuration))
         Base.metadata.create_all(self.db_engine)
         self.sessionmaker = sessionmaker(bind=self.db_engine)
     except (IOError, KeyError) as e:
         log(f'Error when trying to read db configuration: {e}', Color.FAIL)
         raise
Пример #14
0
 def _react_to_connection(self, connection, _):
     state, time_wait = (connection.recv(1024).decode('utf-8') +
                         ",").split(',')[:2]
     message = f'{self.TYPE_NAME} received request for setting socket {state} to state for {time_wait} seconds'
     if str(state) == "ON" or str(state) == "on":
         log(message)
         self._set_socket_state(True, time_wait)
     elif str(state) == "OFF" or str(state) == "off":
         log(message)
         self._set_socket_state(False, time_wait)
     elif str(state) == "STATE" or str(state) == "state":
         connection.send(self.get_measurement().encode('utf-8'))
Пример #15
0
    def _add_new_module(self):
        log("\nChose which module to add")
        module_classes = self.module_classes.values()
        [log(f'{i}) {m_class.TYPE_NAME}') for i, m_class in enumerate(module_classes)]

        try:
            module = module_classes[int(ninput('> '))]()
            module.run_registration_procedure(self.configuration_parser.get_new_module_id())
            self.installed_modules.append(module)
            self.configuration_parser.save_configuration(self.installed_modules)
            log('Success!', Color.GREEN)
        except (KeyError, ValueError, IndexError):
            pass
Пример #16
0
 def _react_to_connection(self, connection, _):
     state, number, time_wait, repeats, = (
         connection.recv(1024).decode('utf-8') + ',,,').split(',')[:4]
     message = '{} received request for setting relay {} to state {} (seconds: {}, repeats {})'.format(
         self.TYPE_NAME, number, state, time_wait, repeats)
     if str(state).upper() == "ON":
         log(message)
         self._set_relay_state(number, True, time_wait, repeats)
     elif str(state).upper() == "OFF":
         log(message)
         self._set_relay_state(number, False, time_wait, repeats)
     elif str(state).upper() == "STATE":
         connection.send(self.get_measurement().encode('utf-8'))
Пример #17
0
 def _new_connection(self, connection, address):
     try:
         data = json.loads(
             connection.recv(4096).decode('utf-8').replace('\0', ''))
         log(f'New connection from {address} who sent: {data}')
         wrh_client = self.wrh_clients.get(data['token'])
         if wrh_client:
             self._update_module_info(wrh_client.id, data['module_id'],
                                      data['module_type'],
                                      data['module_name'])
             self._upload_new_measurement(wrh_client, data)
     finally:
         connection.close()
Пример #18
0
 def __init__(self, module_classes):
     """
     Creates configuration parses object that parses configuration file in the provided path.
     :param module_classes: dictionary of module classes, with keys being the class names and values class objects
     :type module_classes: dict
     :raises UnknownModuleException: configuration file contains unknown module info
     :raises BadConfigurationException: configuration file is invalid
     """
     try:
         open(WRH_CONFIGURATION_FILENAME).close()
     except IOError:
         log('No configuration file found, creating new one', Color.WARNING)
         self.save_configuration([])
     self.module_classes = module_classes
     self._check_file_sanity()
Пример #19
0
 def edit(self):
     """
     Runs interactive procedure to edit module.
     Returns connection status and response.
     """
     log('Provide new module information (leave fields blank if you don\'t want to change)'
         )
     self.name = wrh_input(message='New module\'s name: ',
                           allowed_empty=True) or self.name
     self.gpio = wrh_input(
         message="Please input new IP address of ESP8266 device: ",
         allowed_empty=True) or self.gpio
     self.port = npinput(
         "Please input new port on which module will be listening for commands: ",
         allowed_empty=True) or self.port
 def edit(self):
     """
     Runs interactive procedure to edit module.
     Returns connection status and response.
     """
     log('Provide new module information (leave fields blank if you don\'t want to change)'
         )
     log('Please note that changes other than name will always succeed')
     self.name = wrh_input(message='New module\'s name: ',
                           allowed_empty=True) or self.name
     self.port = iinput(
         "Please input new port on which this module will be listening for commands: ",
         allowed_empty=True) or self.port
     self.rango_port = iinput(
         "Please input new port number of Rango Iryga installed in this system: ",
         allowed_empty=True) or self.rango_port
 def edit(self):
     """
     Runs interactive procedure to edit module.
     Returns connection status and response.
     """
     log('Provide new module information (leave fields blank if you don\'t want to change)'
         )
     self.name = wrh_input(message='New module\'s name: ',
                           allowed_empty=True) or self.name
     self.port = iinput(
         "Please input new port on which this module will be listening for commands: ",
         allowed_empty=True) or self.port
     new_api_location = wrh_input(
         message="Please input new GoogleAPI key location: ",
         allowed_empty=True)
     if new_api_location:
         self.api_location = new_api_location
         self.drive = GoogleDriveManager(self.api_location, self.id)
Пример #22
0
 def edit(self):
     """
     Runs interactive procedure to edit module.
     Returns connection status and response.
     """
     log('Provide new module information (leave fields blank if you don\'t want to change)'
         )
     self.name = wrh_input(message='New module\'s name: ',
                           allowed_empty=True) or self.name
     self.gpio = iinput(
         "Please input new gpio pin number to which sensor is connected: ",
         allowed_empty=True) or self.gpio
     self.interval = iinput(
         "Please input new interval (in minutes) for taking consecutive measurements: ",
         allowed_empty=True) or self.interval
     self.port = iinput(
         "Please input new port on which this module will be listening for commands: ",
         allowed_empty=True) or self.port
Пример #23
0
 def edit(self):
     """
     Runs interactive procedure to edit module.
     Returns connection status and response.
     """
     log('Provide new module information (leave fields blank if you don\'t want to change)'
         )
     self.name = wrh_input(message='New module\'s name: ',
                           allowed_empty=True) or self.name
     self.camera_address = wrh_input(
         message="Please input new IP address of camera: ",
         allowed_empty=True) or self.camera_address
     self.camera_port = wrh_input(
         message=
         "Please input new port on which IP camera can be accessed: ",
         allowed_empty=True) or self.camera_port
     self.port = iinput(
         "Please input new port on which streamed images can be accessed: ",
         allowed_empty=True) or self.port
Пример #24
0
 def _await_connections(self):
     predicate = lambda: self._should_end is False
     self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
     self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
     bind_result = wait_bind_socket(
         self.socket,
         '',
         self.port,
         sleep=10,
         retries=5,
         predicate=predicate,
         error_message=f'Unable to bind to port {self.port}')
     if bind_result:
         log(f'Listening for incoming connections on port {self.port}')
         self.socket.listen(5)
         await_connection(self.socket,
                          self._new_connection,
                          predicate=predicate,
                          close_connection=False)
Пример #25
0
 def _web_service_thread(self):
     predicate = lambda: not self._should_end
     self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
     self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
     bind_result = wait_bind_socket(
         self.socket,
         '',
         self.port,
         10,
         predicate=predicate,
         error_message=
         f'{self.TYPE_NAME} {self.name} port bind failed. (ERROR_CODE, ERROR_MESSAGE) = '
     )
     if bind_result:
         log(f'{self.TYPE_NAME} {self.name} started listening')
         self.socket.listen(10)
         await_connection(self.socket,
                          self._start_new_connection_thread,
                          predicate=predicate,
                          close_connection=False)
Пример #26
0
    def get_measurement(self):
        """
        Returns measurements taken by this module: two strings with download and upload speed values.
        """
        command = ["/usr/bin/python2.7", "modules/speed_test/speedtest-cli/speedtest_cli.py"]
        try:
            results = subprocess.check_output(command).decode('utf_8')
        except subprocess.CalledProcessError:
            results = ""
        results = results.replace('\n', '')
        log("The results are as follows: " + str(results))
        pattern = ".+?Download: (.+?/s).+?Upload: (.+?/s).*"
        checker = re.compile(pattern)

        if not checker.match(str(results)):
            download, upload = "0 Mbit/s", "0 Mbit/s"
        else:
            download = re.search(pattern, results).group(1)
            upload = re.search(pattern, results).group(2)

        return download, upload
Пример #27
0
 def _send_measurement(self, measurement, _file_=None):
     """
     Sends measured data to remote database.
     Remote server's credentials should be present in WRH server configuration file.
     :param measurement: measurement value to be stored in database
     :type measurement: any
     :param _file_: parameter filled by function decorator
     :type _file_: file
     """
     conf = json.loads(_file_.read())
     data = {
         'token': conf['token'],
         'module_type': self.WRHID,
         'module_id': self.id,
         'module_name': self.name,
         'measurement': measurement,
         'date': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
     }
     for connection in open_connection((conf['host'], conf['port'])):
         connection.send(json.dumps(data).encode('utf-8'))
         log(f'Module {self.name} successfully uploaded its measurement')
Пример #28
0
    def create_folder(self, folder_name, path=None):
        """
        Creates folder in the Google Drive.
        :param folder_name: name of the folder
        :param path: placement of the folder in the drive filesystem
        :return: id of newly created folder or None if an error occurred
        """
        # TODO: Add support for path parameter!
        file_metadata = {
            'name': folder_name,
            'mimeType': 'application/vnd.google-apps.folder',
        }

        folder_id = None
        try:
            folder = self._service.files().create(body=file_metadata,
                                                  fields='id').execute()
            folder_id = folder.get('id')
        except googleapiclient.errors.HttpError as e:
            log(str(e), Color.EXCEPTION)

        return folder_id
Пример #29
0
 def edit(self):
     """
     Runs interactive procedure to edit module.
     Returns connection status and response.
     """
     log('Provide new module information (leave fields blank if you don\'t want to change)'
         )
     self.name = wrh_input(message='New module\'s name: ',
                           allowed_empty=True) or self.name
     self.gpio = wrh_input(
         message=
         "Please input new name of the webcam device (usually /dev/video# where # is the specific number): ",
         allowed_empty=True) or self.gpio
     self.port = iinput(
         "Please input new port on which streamed images can be accessed: ",
         allowed_empty=True) or self.port
     self.login = wrh_input(
         message="Please input new login used to access the video stream: ",
         allowed_empty=True) or self.login
     self.password = wrh_input(
         message=
         "Please input new password used to access the video stream: ",
         allowed_empty=True) or self.password
Пример #30
0
def end_process(process, timeout, suppress_messages=False):
    """
    Tries to end process by sending SIGINT signal. The request is repeated until process ends or timeout is reached.
    It the timeout is reached SIGKILL is sent instead.
    :param process: process to stop
    :param timeout: (in seconds) timeout after which SIGKILL process
    :param suppress_messages: should messages be printed to STDOUT
    """
    try:
        if not suppress_messages:
            log('Sending SIGINT to process: ' + str(process.pid))
        process.send_signal(signal.SIGINT)
        while does_process_exist(process) and timeout > 0:
            time.sleep(1)
            timeout -= 1
        if does_process_exist(process):
            if not suppress_messages:
                log(
                    'Process ' + str(process.pid) +
                    " not responding. Sending SIGTERM.", Color.WARNING)
            process.terminate()
    except OSError:
        pass