def __init__(self, args): """Initialization method of :class:`t_system.accession.NetworkManager` class. Args: args: Command-line arguments. """ self.folder = dot_t_system_dir + "/network" if not os.path.exists(self.folder): os.mkdir(self.folder) self.table = DBFetcher(self.folder, "db", "login").fetch() self.wpa_supplicant = WpaSupplicant(args) self.wlan = args["wlan"] self.current_cells = [] self.current_available_networks = [] self.known_networks = [] set_local_ip_address(args["wlan"], args["static_ip"]) self.scan() self.set_available_networks() self.refresh_known_networks()
def __init__(self, name, id=None): """Initialization method of :class:`t_system.face_encoder.Face` class. Args: name (str): The name of the man who has face in dataset. id (str): The id of the face. """ self.name = name self.id = id self.id = id if not id: self.id = str(uuid.uuid1()) self.recognition_folder = f'{dot_t_system_dir}/recognition' self.dataset_folder = f'{self.recognition_folder}/dataset/{self.name}' self.pickle_file = f'{self.recognition_folder}/encodings/{self.name}_encoding.pickle' self.__check_folders() self.table = DBFetcher(self.recognition_folder, "db", "faces").fetch() self.image_names = [] self.refresh_image_names() self.__db_upsert()
def __init__(self, to_be_used=False, accounts=None, active_account=None): """Initialization method of :class:`t_system.r_sync.DropBox` class. Args: to_be_used (bool): To be used flag that specify usage status of service on folder synchronization. accounts: DropBox account owner name and account API key list. active_account: hexadecimal stream key that use in current stream of the website. """ self.accounts = accounts if not accounts: self.accounts = [] self.active_account = active_account if not active_account: self.active_account = {} self.name = "Dropbox" self.to_be_used = to_be_used self.dbx = None self.folder = f'{dot_t_system_dir}/r_sync' self.sync_sou_dir = f'{dot_t_system_dir}/records' self.sync_tar_dir = "Media-from-T_System" self.table = DBFetcher(self.folder, "db", "services").fetch() self.__db_upsert()
def __init__(self, args): """Initialization method of :class:`t_system.accession.NetworkConnector` class. Args: args: Command-line arguments. """ self.folder = f'{dot_t_system_dir}/network' if not os.path.exists(self.folder): os.mkdir(self.folder) self.login_table = DBFetcher(self.folder, "db", "login").fetch() self.status_table = DBFetcher(self.folder, "db", "status").fetch() self.activity = None self.__refresh_status() self.wpa_supplicant = WpaSupplicant(args) self.wlan = args["wlan"] self.interface_ec = 0 self.current_cells = [] self.current_available_networks = [] self.known_networks = [] if args["static_ip"]: set_local_ip_address(args["wlan"], args["static_ip"]) if self.activity: self.scan() self.__set_available_networks() self.__refresh_known_networks()
def __init__(self, camera, hearer): """Initialization method of :class:`t_system.online_stream.OnlineStream` class. Args: camera: Camera object from PiCamera. hearer: Hearer object. """ self.folder = f'{dot_t_system_dir}/streaming' self.__check_folders() self.websites_table = DBFetcher(self.folder, "db", "websites").fetch() self.websites = [] self.stream_pipes = [] self.streamer_config_file = f'{T_SYSTEM_PATH}/online_stream/config.json' self.__set_websites() if not self.websites: self.__create_websites() self.camera = camera self.hearer = hearer
def __init__(self): """Initialization method of :class:`t_system.administration.Administrator` class. """ self.table = DBFetcher(dot_t_system_dir, "db", "admin").fetch() self.ssid_hash = None self.password_hash = None self.private_key = None self.get_keys()
def __init__(self): """Initialization method of :class:`t_system.administration.Identifier` class. """ self.table = DBFetcher(dot_t_system_dir, "db", "identity").fetch() self.public_id = None self.private_id = None self.name = None self.__get_keys()
def __get_db(self, db_folder, db_name, cache_size=30): """Function to set the database of the position. Args: db_folder (str): Containing folder of the database file db_name (str): Database of the position abject. cache_size (int): TinyDB caches query result for performance. """ if self.is_for_scenario: return DBFetcher(db_folder, db_name, "scenarios", cache_size).fetch() else: return DBFetcher(db_folder, db_name, "positions", cache_size).fetch()
def __init__(self, name, url, server, to_be_used=False, stream_ids=None, active_stream_id=None, id=None): """Initialization method of :class:`t_system.online_stream.OnlineStream` class. Args: name: Name of the WebSite. youtube, facebook etc. url: Website's page URL. server: Website's Live stream server RTMP URL. to_be_used (bool): To be used flag that specify usage status of website on live stream. stream_ids: hexadecimal stream keys of the website. active_stream_id: hexadecimal stream key that use in current stream of the website. id: Unique ID of the website. """ self.id = id if not id: self.id = str(uuid.uuid1()) self.stream_ids = stream_ids if not stream_ids: self.stream_ids = [] self.active_stream_id = active_stream_id if not active_stream_id: self.active_stream_id = {} self.name = name self.url = url self.server = server self.to_be_used = to_be_used self.streaming_folder = f'{dot_t_system_dir}/streaming' self.keys_folder = f'{self.streaming_folder}/keys' self.parent_folder = f'{self.streaming_folder}/websites' self.folder = f'{self.parent_folder}/{self.name}' self.key_file = f'{self.keys_folder}/{self.name}.key' self.__check_folders() self.table = DBFetcher(self.streaming_folder, "db", "websites").fetch() self.__db_upsert()
def __init__(self): """Initialization method of :class:`t_system.motion.action.ActionManager` class. """ db_folder = f'{T_SYSTEM_PATH}/motion/action' self.predicted_db_name = 'predicted_missions' self.predicted_scenarios_table = DBFetcher(db_folder, self.predicted_db_name, "scenarios", 30).fetch() self.predicted_positions_table = DBFetcher(db_folder, self.predicted_db_name, "positions", 30).fetch() db_folder = dot_t_system_dir self.db_name = 'missions' self.scenarios_table = DBFetcher(db_folder, self.db_name, "scenarios").fetch() self.positions_table = DBFetcher(db_folder, self.db_name, "positions").fetch() self.predicted_positions = [] self.predicted_scenarios = [] self.positions = [] self.scenarios = [] self.refresh_members() self.actor = Actor()
def __init__(self, d_m_y, h_m_s, scope, record_formats, id=None, name=None, length=None): """Initialization method of :class:`t_system.recordation.Record` class. Args: d_m_y (str): Date that is day_mount_year format. h_m_s (str): Date that is hour_minute_second format. scope (str): The working type during recording. record_formats (dict): Formats of the records for video, audio and merged. id (str): The id of the record. name (str): The name of the record. length (str): The length of the record as m:s. """ self.id = id if not id: self.id = str(uuid.uuid1()) self.name = name if not name: self.name = h_m_s self.date = d_m_y # table name at the same time self.time = h_m_s self.scope = scope self.record_formats = record_formats self.length = length self.records_folder = f'{dot_t_system_dir}/records' self.parent_folder = f'{self.records_folder}/{self.date}' self.folder = f'{self.parent_folder}/{self.time}' self.video_file = f'{self.folder}/{self.time}.{self.record_formats["video"]}' self.audio_file = f'{self.folder}/{self.time}.{self.record_formats["audio"]}' self.merged_file = f'{self.folder}/{self.time}.{self.record_formats["merged"]}' self.db = DBFetcher(self.records_folder, "db").fetch() self.__check_folders() if length is None: self.length = self.__calc_length() self.__db_upsert()
def __init__(self): """Initialization method of :class:`t_system.recordation.RecordManager` class. """ self.records_folder = f'{dot_t_system_dir}/records' if not os.path.exists(self.records_folder): os.mkdir(self.records_folder) self.db = DBFetcher(self.records_folder, "db").fetch() self.records = [] self.__set_records()
def get_db_table(root, db_name): """Method to set work database by root. Args: root (bool): Root privileges flag. db_name (str): Name of the registered Database. It uses if administration privileges activated. """ table = "scenarios" if root: db_folder = f'{T_SYSTEM_PATH}/motion/action' return DBFetcher(db_folder, db_name, table).fetch() else: db_folder = dot_t_system_dir db_name = 'missions' return DBFetcher(db_folder, db_name, table).fetch()
def __init__(self): """Initialization method of :class:`t_system.motion.action.EmotionManager` class. """ db_folder = f'{T_SYSTEM_PATH}/motion/action' self.db_name = 'emotions' self.scenarios_table = DBFetcher(db_folder, self.db_name, "scenarios", 30).fetch() self.positions_table = DBFetcher(db_folder, self.db_name, "positions", 30).fetch() self.positions = [] self.scenarios = [] self.__refresh_members() self.actor = Actor()
def __init__(self): """Initialization method of :class:`t_system.recordation.RecordManager` class. """ self.records_folder = f'{dot_t_system_dir}/records' self.__check_folders() self.shoots_table = DBFetcher(self.records_folder, "db", "shoots").fetch() self.shot_table = DBFetcher(self.records_folder, "db", "shots").fetch() self.shoots = [] self.shots = [] self.__set_records()
def __init__(self, editable=False, verbose=False): """Initialization method of :class:`t_system.updation.UpdateManager` class. Args: editable: Editable updation mode flag. For making updates as development. verbose: Verbosity flag about printing debug messages. """ self.editable = editable self.verbose = verbose # this argument will be added. self.table = DBFetcher(dot_t_system_dir, "db", "update").fetch() self.auto_update = None self.refresh_members() self.updater = Updater(self.verbose) self.installer = Installer(self.editable, self.verbose)
def __init__(self): """Initialization method of :class:`t_system.motion.arm.modelisation.ArmModeler` class. """ self.name = None self.config_file = f'{T_SYSTEM_PATH}/motion/arm/config.json' self.db = DBFetcher(f'{T_SYSTEM_PATH}/motion/arm', "model").fetch() self.joint_count = 0 self.alpha = None self.a = None self.q = None self.d = None self.dh_params = {} self.tf_matrices = [] self.jacobian_matrix = None
def __init__(self): """Initialization method of :class:`t_system.r_sync.RSynchronizer` class. """ self.folder = f'{dot_t_system_dir}/r_sync' self.__check_folders() self.services_table = DBFetcher(self.folder, "db", "services").fetch() self.status_table = DBFetcher(self.folder, "db", "status").fetch() self.services = [] self.auto_sync = None self.__set_services() self.__refresh_status() if not self.services: self.__create_services()
def __get_db(db_folder, db_name, cache_size=None): """Function to set the database of the scenario. Args: db_folder (str): Containing folder of the database file db_name (str): Database file of the scenario abject. cache_size (int): TinyDB caches query result for performance. """ return DBFetcher(db_folder, db_name, "scenarios", cache_size).fetch()
def __init__(self, detection_method="hog"): """Initialization method of :class:`t_system.face_encoding.FaceEncodeManager` class. Args: detection_method (str): face detection model """ self.detection_method = detection_method # either `hog` or `cnn` self.recognition_folder = f'{dot_t_system_dir}/recognition' self.encodings_folder = f'{self.recognition_folder}/encodings' self.dataset_folder = f'{self.recognition_folder}/dataset' self.main_encoding_file = f'{self.recognition_folder}/main_encoding.pickle' self.__check_folders() self.table = DBFetcher(self.recognition_folder, "db", "faces").fetch() self.face_encoder = FaceEncoder(detection_method) self.faces = [] self.__refresh_faces()
def __init__(self, d_m_y, h_m_s, shot_format, id=None, name=None, size=None): """Initialization method of :class:`t_system.recordation.Record` class. Args: d_m_y (str): Date that is day_mount_year format. h_m_s (str): Date that is hour_minute_second format. shot_format (str): Format of the shot. (jpg, png etc.) id (str): The id of the record. name (str): The name of the record. size (str): The length of the record as m:s. """ self.id = id if not id: self.id = str(uuid.uuid1()) self.name = name if not name: self.name = h_m_s self.date = d_m_y # table name at the same time self.time = h_m_s self.shot_format = shot_format self.size = size self.records_folder = f'{dot_t_system_dir}/records' self.parent_folder = f'{self.records_folder}/{self.date}' self.folder = f'{self.parent_folder}/{self.time}' self.shot_file = f'{self.folder}/{self.time}.{self.shot_format}' self.table = DBFetcher(self.records_folder, "db", "shots").fetch() self.__check_folders() if size is None: self.size = self.__calc_size() self.__db_upsert()
class ArmModeler: """Class to define the D-H matrix modeler of T_System arm . This class provides necessary initiations and a function named :func:`t_system.motion.arm.ArmModeler.create` for the create D-H model of the given arm. """ def __init__(self): """Initialization method of :class:`t_system.motion.arm.modelisation.ArmModeler` class. """ self.name = None self.config_file = f'{T_SYSTEM_PATH}/motion/arm/config.json' self.db = DBFetcher(f'{T_SYSTEM_PATH}/motion/arm', "model").fetch() self.joint_count = 0 self.alpha = None self.a = None self.q = None self.d = None self.dh_params = {} self.tf_matrices = [] self.jacobian_matrix = None def create(self, arm_name): """Method to create D-H model of given arm. Args: arm_name (str): A robotic arm name in to the config.json file. """ try: with open(self.config_file) as conf_file: arm_configs = json.load(conf_file)[ arm_name] # config file returns the arms. except KeyError: raise Exception(f'{arm_name} is not exit in configuration file.') joint_configs = arm_configs["joints"] self.joint_count = len(joint_configs) self.__prepare_dh_params() self.__set_dh_params(joint_configs) self.__calc_jacobian_matrix() self.__db_upsert(force_insert=True) def get(self, arm_name=None): """Method to create D-H model of given arm. Args: arm_name (str): A robotic arm name in to the config.json file. """ if arm_name: model = self.db.search((Query().name == arm_name)) if model: return { "alpha": loads(model[0]["alpha"].encode("raw_unicode_escape")), "a": loads(model[0]["a"].encode("raw_unicode_escape")), "q": loads(model[0]["q"].encode("raw_unicode_escape")), "d": loads(model[0]["d"].encode("raw_unicode_escape")), "dh_params": loads(model[0]["dh_params"].encode("raw_unicode_escape")), "transform_matrices": loads(model[0]["transform_matrices"].encode( "raw_unicode_escape")), "jacobian_matrix": loads(model[0]["jacobian_matrix"].encode( "raw_unicode_escape")) } return None else: arms = [] for model in self.db.all(): arms.append({ "alpha": loads(model[0]["alpha"].encode("raw_unicode_escape")), "a": loads(model[0]["a"].encode("raw_unicode_escape")), "q": loads(model[0]["q"].encode("raw_unicode_escape")), "d": loads(model[0]["d"].encode("raw_unicode_escape")), "dh_params": loads(model[0]["dh_params"].encode("raw_unicode_escape")), "transform_matrices": loads(model[0]["transform_matrices"].encode( "raw_unicode_escape")), "jacobian_matrix": loads(model[0]["jacobian_matrix"].encode( "raw_unicode_escape")) }) return arms def show(self, arm_name=None): """Method to show model by given name parameter. If there is no name, print all arms models to the screen. Args: arm_name (str): A robotic arm name in to the config.json file. """ if arm_name: model = self.get(arm_name) if model: for key, value in model.items(): print(f'{key}: \n{value}') return True logger.error(f'There is no model for arm {arm_name}') return False else: models = self.get() if models: for model in models: for key, value in model.items(): print(f'{key}: \n{value}') return True logger.error(f'There is no any created model.') return False def __prepare_dh_params(self): """Method to preparing D-H parameters of Arm. """ self.alpha = symbols('alpha0:' + str(self.joint_count)) self.a = symbols('a0:' + str(self.joint_count)) self.q = symbols('q1:' + str(self.joint_count + 1)) self.d = symbols('d1:' + str(self.joint_count + 1)) def __set_dh_params(self, joints): """Method to setting joint's D-H parameters. Args: joints (list): The arm's joints list for preparing parameters of Denavit-Hartenberg chart. """ self.dh_params = {} for i in range(len(joints)): self.dh_params[self.alpha[i]] = joints[i]["alpha"] self.dh_params[self.a[i]] = joints[i]["a"] if joints[i]["structure"] == "revolute": self.dh_params[self.q[i]] = self.q[i] self.dh_params[self.d[i]] = joints[i]["init_d"] elif joints[i]["structure"] == "prismatic": self.dh_params[self.q[i]] = joints[i]["init_q"] self.dh_params[self.d[i]] = self.d[i] elif joints[i]["structure"] == "constant": self.dh_params[self.q[i]] = joints[i]["init_q"] self.dh_params[self.d[i]] = joints[i]["init_d"] self.__set_transform_matrices() def show_dh_params(self): """Method to getting D-H parameters of joints of Arm as string message. """ print(f'DH Parameters are: {self.dh_params}') def __set_transform_matrices(self): """Method to setting D-H transform matrices. """ self.tf_matrices = [] transform_matrix = eye( 4) # creates a unit matrix via passing argument. for i in range(self.joint_count): transform_matrix = transform_matrix * self.__create_tf_matrix( self.alpha[i], self.a[i], self.d[i], self.q[i]).subs( self.dh_params) self.tf_matrices.append(transform_matrix) def show_transform_matrices(self): """Method to getting D-H parameters of joints of Arm as string message. """ print(f'Transform Matrices are: {self.tf_matrices}') @staticmethod def __create_tf_matrix(alpha, a, d, q): """Method to calculate transform matrix of Denavit-Hartenberg Method. Args: alpha: The twist angle. Axis angle between consecutive two axes. a: The limb length between consecutive two axis. d: link offset. The displacement along the same axis. q: The rotation theta angle about the joint axis. Returns: object: The Denavit-Hartenberg transform matrix object. """ tf_matrix = Matrix([[cos(q), -sin(q), 0., a], [ sin(q) * cos(alpha), cos(q) * cos(alpha), -sin(alpha), -sin(alpha) * d ], [ sin(q) * sin(alpha), cos(q) * sin(alpha), cos(alpha), cos(alpha) * d ], [0., 0., 0., 1.]]) return tf_matrix def __calc_jacobian_matrix(self): """Method to calculate jacobian matrix of Arm's General Denavit-Hartenberg Transform Matrix. """ tf_matrix_first_to_last = self.tf_matrices[-1] self.jacobian_matrix = [ diff(tf_matrix_first_to_last[:3, -1], self.q[i]).reshape(1, 3) for i in range(len(self.q)) ] self.jacobian_matrix = Matrix( self.jacobian_matrix).T # .T returns the transpose of matrix. def __db_upsert(self, force_insert=False): """Function to insert(or update) the record to the database. Args: force_insert (bool): Force insert flag. Returns: str: Response. """ if self.db.search((Query().name == self.name)): if force_insert: self.db.update( { 'name': self.name, 'alpha': dumps(self.alpha).decode("raw_unicode_escape"), 'a': dumps(self.a).decode("raw_unicode_escape"), 'q': dumps(self.q).decode("raw_unicode_escape"), 'd': dumps(self.d).decode("raw_unicode_escape"), 'dh_params': dumps(self.dh_params).decode("raw_unicode_escape"), 'transform_matrices': dumps(self.tf_matrices).decode("raw_unicode_escape"), 'jacobian_matrix': dumps( self.jacobian_matrix).decode("raw_unicode_escape") }, Query().name == self.name) else: return "Already Exist" else: self.db.insert({ 'name': self.name, 'alpha': dumps(self.alpha).decode("raw_unicode_escape"), 'a': dumps(self.a).decode("raw_unicode_escape"), 'q': dumps(self.q).decode("raw_unicode_escape"), 'd': dumps(self.d).decode("raw_unicode_escape"), 'dh_params': dumps(self.dh_params).decode("raw_unicode_escape"), 'transform_matrices': dumps(self.tf_matrices).decode("raw_unicode_escape"), 'jacobian_matrix': dumps(self.jacobian_matrix).decode("raw_unicode_escape") }) # insert the given data return ""
class NetworkConnector: """Class to define an accessing to the around networks ability of tracking system. This class provides necessary initiations and functions named :func:`t_system.audition.Audition.listen_async` as the loop for asynchronous collecting audio data to the vision ability, named :func:`t_system.audition.Audition.listen_sync` for the synchronous collecting audio data to the vision ability and named :func:`t_system.audition.Audition.start_recording` as entry point from vision ability for starting recording processes. """ def __init__(self, args): """Initialization method of :class:`t_system.accession.NetworkManager` class. Args: args: Command-line arguments. """ self.folder = dot_t_system_dir + "/network" if not os.path.exists(self.folder): os.mkdir(self.folder) self.table = DBFetcher(self.folder, "db", "login").fetch() self.wpa_supplicant = WpaSupplicant(args) self.wlan = args["wlan"] self.current_cells = [] self.current_available_networks = [] self.known_networks = [] set_local_ip_address(args["wlan"], args["static_ip"]) self.scan() self.set_available_networks() self.refresh_known_networks() def scan(self, wlan=None): """The high-level method to scan around for searching available networks. Args: wlan: wi-fi interface that will be used to create hotSpot. """ if wlan: self.wlan = wlan self.current_cells = list(Cell.all(self.wlan)) def set_available_networks(self): """The low-level method to setting available networks with their ssid and passwords. """ self.current_available_networks.clear() for cell in self.current_cells: network = {"ssid": cell.ssid} self.current_available_networks.append(network) def add_network(self, ssid, password): """The high-level method to set network parameter for reaching it. Args: ssid: The name of the surrounding access point. password: The password of the surrounding access point. """ admin_id = check_secret_root_entry(ssid, password) if admin_id: return True, admin_id status = False for network in self.current_available_networks: if ssid == network["ssid"]: self.db_upsert(ssid, password) self.refresh_known_networks() status = True return status, admin_id def delete_network(self, ssid): """The high-level method to set network parameter for reaching it. Args: ssid: The name of the surrounding access point. """ self.table.remove((Query().ssid == ssid)) @dispatch() def connect(self): """The high-level method to try to connect to one of the available networks using wpa_supplicant.conf file that is restarted by subprocess. """ result = False self.wpa_supplicant.restart_ws_service() time.sleep(5) if self.is_network_online(): print("Network connection established!") # result = True return result @dispatch(str, str) def connect(self, ssid, password): """The high-level method to try connect to one of available networks with using `wifi` library. Args: ssid (str): The name of the surrounding access point. password (str): The password of the surrounding access point. """ result = False for cell in self.current_cells: if cell.ssid == ssid: try: scheme = Scheme.for_cell(self.wlan, ssid, cell, password) scheme.activate() result = True except Exception as e: print(e) return result def try_creating_connection(self): """The high-level method to try connect to one of available networks. """ for network in self.known_networks: if self.connect(network["ssid"], network["password"]): return True return False def db_upsert(self, ssid, password, force_insert=False): """Function to insert(or update) the position to the database. Args: ssid: The name of the surrounding access point. password: The password of the surrounding access point. force_insert (bool): Force insert flag. Returns: str: Response. """ if self.table.search((Query().ssid == ssid)): if force_insert: # self.already_exist = False self.table.update({'password': password, 'wlan': self.wlan}, Query().ssid == ssid) else: # self.already_exist = True return "Already Exist" else: self.table.insert({ 'wlan': self.wlan, 'ssid': ssid, 'password': password }) # insert the given data return "" def refresh_known_networks(self): """The low-level method to refreshing known networks from the database (and creating objects for them.) """ self.known_networks.clear() self.wpa_supplicant.create_wsc() for login in self.table.all(): network = {"ssid": login["ssid"], "password": login["password"], "wlan": login["wlan"]} self.known_networks.append(network) self.wpa_supplicant.add_network_to_wsc(login["ssid"], login["password"]) @staticmethod def is_network_online(): """The top-level method to check the internet access of the current network connection via sending request to Google. Returns: bool: status. """ url = 'http://www.google.com/' timeout = 5 try: _ = requests.get(url, timeout=timeout) return True except requests.ConnectionError: # print("Internet connection could not be established.") pass return False
class DropBox: """Class to define a file synchronizer to an Dropbox account. This class provides necessary initiations and functions named :func:`t_system.r_sync.DropBox.sync` to provide synchronizing recorded videos with the Dropbox account. """ def __init__(self, to_be_used=False, accounts=None, active_account=None): """Initialization method of :class:`t_system.r_sync.DropBox` class. Args: to_be_used (bool): To be used flag that specify usage status of service on folder synchronization. accounts: DropBox account owner name and account API key list. active_account: hexadecimal stream key that use in current stream of the website. """ self.accounts = accounts if not accounts: self.accounts = [] self.active_account = active_account if not active_account: self.active_account = {} self.name = "Dropbox" self.to_be_used = to_be_used self.dbx = None self.folder = f'{dot_t_system_dir}/r_sync' self.sync_sou_dir = f'{dot_t_system_dir}/records' self.sync_tar_dir = "Media-from-T_System" self.table = DBFetcher(self.folder, "db", "services").fetch() self.__db_upsert() def sync(self): """Method to synchronizing folder that keeps recorded videos with user's Dropbox account. """ if self.__prepare_sync(): for dn, dirs, files in os.walk(self.sync_sou_dir): sub_folder = dn[len(self.sync_sou_dir):].strip(os.path.sep) listing = self.list_folder(self.sync_tar_dir, sub_folder) logger.info(f'Descending into {sub_folder}...') # First do all the files. for file in files: file_path = os.path.join(dn, file) if isinstance(file, bytes): file = file.decode('utf-8') n_name = unicodedata.normalize('NFC', file) if file.startswith('.'): logger.info(f'Skipping dot file: {file}') elif file.startswith('@') or file.endswith('~'): logger.info(f'Skipping temporary file: {file}') elif file.endswith('.pyc') or file.endswith('.pyo'): logger.info(f'Skipping generated file: {file}') elif file.endswith('.json'): logger.info(f'Skipping database file: {file}') elif n_name in listing: md = listing[n_name] mtime = os.path.getmtime(file_path) mtime_dt = datetime.datetime(*time.gmtime(mtime)[:6]) size = os.path.getsize(file_path) if isinstance( md, dropbox.files.FileMetadata ) and mtime_dt == md.client_modified and size == md.size: logger.info( f' {file} is already synced [stats match]') else: logger.info( file, f'{file} exists with different stats, downloading' ) res = self.download(self.sync_tar_dir, sub_folder, file) with open(file_path) as f: data = f.read() if res == data: logger.info( f'{file} is already synced [content match]' ) else: logger.info( f'{file} has changed since last sync') self.upload(file_path, self.sync_tar_dir, sub_folder, file, overwrite=True) else: self.upload(file_path, self.sync_tar_dir, sub_folder, file) # Then choose which subdirectories to traverse. keep = [] for directory in dirs: if directory.startswith('.'): logger.info(f'Skipping dot directory: {directory}') elif directory.startswith('@') or directory.endswith('~'): logger.info( f'Skipping temporary directory: {directory}') elif directory == '__pycache__': logger.info( f'Skipping generated directory: {directory}') else: logger.info(f'Keeping directory: {directory}') keep.append(directory) dirs[:] = keep def __prepare_sync(self): """Method to prepare DropBoxer to folder synchronization. """ if self.activate_account: self.dbx = dropbox.Dropbox(self.active_account["key"]) return True return False def list_folder(self, folder, sub_folder): """Method to list a folder. Args: folder (str): Top-folder that contains synchronized items. sub_folder (str): Sub-folder that belongs to top-folder. Returns: a dict mapping unicode filenames to FileMetadata|FolderMetadata entries. """ path = '/%s/%s' % (folder, sub_folder.replace(os.path.sep, '/')) while '//' in path: path = path.replace('//', '/') path = path.rstrip('/') try: res = self.dbx.files_list_folder(path) except dropbox.exceptions.ApiError as err: logger.error( f'Folder listing failed for {path} -- assumed empty: {err}') return {} else: rv = {} for entry in res.entries: rv[entry.name] = entry return rv def download(self, folder, sub_folder, file_name): """Method to download a file from Dropbox account. Args: folder (str): Top-folder that contains synchronized items. sub_folder (str): Sub-folder that belongs to top-folder. file_name (str): The name of the file that will be uploaded. Returns: The bytes of the file, or None if it doesn't exist. """ path = '/%s/%s/%s' % (folder, sub_folder.replace(os.path.sep, '/'), file_name) while '//' in path: path = path.replace('//', '/') try: md, res = self.dbx.files_download(path) except dropbox.exceptions.HttpError as err: print('*** HTTP error', err) return None data = res.content logger.info(f'{len(data)} bytes; md: {md}') return data def upload(self, file_path, folder, sub_folder, file_name, overwrite=False): """Method to upload a file to Dropbox account. Args: file_path (str): The path of the file that will be uploaded. folder (str): Top-folder that contains synchronized items. sub_folder (str): Sub-folder that belongs to top-folder. file_name (str): The name of the file that will be uploaded. overwrite (bool): The overwriting flag of file that will be uploaded. Returns: The request response, or None in case of error. """ path = '/%s/%s/%s' % (folder, sub_folder.replace(os.path.sep, '/'), file_name) while '//' in path: path = path.replace('//', '/') mode = (dropbox.files.WriteMode.overwrite if overwrite else dropbox.files.WriteMode.add) mtime = os.path.getmtime(file_path) with open(file_path, 'rb') as f: data = f.read() try: res = self.dbx.files_upload( data, path, mode, client_modified=datetime.datetime(*time.gmtime(mtime)[:6]), mute=True) except dropbox.exceptions.ApiError as err: logger.error(f'API error {err}') return None logger.info(f'uploaded as {res.name.encode("utf8")}') return res def set_usage_stat(self, to_be_used): """Method to set website as to be used or not to be used. Args: to_be_used (bool): To be used flag that specify usage status of service on folder synchronization. """ self.to_be_used = to_be_used self.__db_upsert(force_insert=True) def activate_account(self, account_name): """Method to set given API key of given account for using on the current folder synchronization. Args: account_name (str): Name of the Dropbox account. """ for account in self.accounts: if account["is_active"]: account["is_active"] = False break for account in self.accounts: if account["name"] == account_name: account["is_active"] = True self.active_account = account self.__db_upsert(force_insert=True) return True return False def upsert_account(self, name, key): """Method to insert(or update) new API key and its account name for reaching the Dropbox. Args: name (str): Name of the Dropbox account. key (str): API key of Dropbox account. """ is_update = False for account in self.accounts: if name == account["name"]: account["key"] = key is_update = True break if not is_update: self.accounts.append({ "name": name, "key": key, "is_active": False }) self.__db_upsert(force_insert=True) return True def remove_account(self, name): """Method to remove existing stream key and its account name. Args: name (str): Name of the Dropbox account. """ for account in self.accounts: if name == account["name"]: self.accounts.remove(account) # for removing object from list self.__db_upsert(force_insert=True) return True return False def delete_self(self): """Method to delete website itself. """ self.table.remove((Query().name == self.name)) def __db_upsert(self, force_insert=False): """Function to insert(or update) the record to the database. Args: force_insert (bool): Force insert flag. Returns: str: Response. """ if self.table.search((Query().name == self.name)): if force_insert: self.table.update( { 'info': { 'to_be_used': self.to_be_used, 'accounts': self.accounts, 'active_account': self.active_account } }, Query().name == self.name) else: return "Already Exist" else: self.table.insert({ 'name': self.name, 'info': { 'to_be_used': self.to_be_used, 'accounts': self.accounts, 'active_account': self.active_account } }) # insert the given data return ""
class MissionManager: """Class to define a mission manager to managing movements of Arm and Locking System(when it is using independent from seer during tracking non-moving objects) during job. This class provides necessary initiations and a function named :func:`t_system.motion.action.MissionManager.execute` for the realize mission that is formed positions or scenarios. """ def __init__(self): """Initialization method of :class:`t_system.motion.action.ActionManager` class. """ db_folder = f'{T_SYSTEM_PATH}/motion/action' self.predicted_db_name = 'predicted_missions' self.predicted_scenarios_table = DBFetcher(db_folder, self.predicted_db_name, "scenarios", 30).fetch() self.predicted_positions_table = DBFetcher(db_folder, self.predicted_db_name, "positions", 30).fetch() db_folder = dot_t_system_dir self.db_name = 'missions' self.scenarios_table = DBFetcher(db_folder, self.db_name, "scenarios").fetch() self.positions_table = DBFetcher(db_folder, self.db_name, "positions").fetch() self.predicted_positions = [] self.predicted_scenarios = [] self.positions = [] self.scenarios = [] self.refresh_members() self.actor = Actor() def refresh_members(self): """Method to refreshing the members """ self.predicted_scenarios = [] self.predicted_positions = [] self.scenarios = [] self.positions = [] predicted_scenarios = self.predicted_scenarios_table.all() for scenario in predicted_scenarios: self.predicted_scenarios.append( Scenario(scenario["name"], scenario["id"], root=True, db_name=self.predicted_db_name)) predicted_positions = self.predicted_positions_table.all() for position in predicted_positions: self.predicted_positions.append( Position(position["name"], position["id"], position["cartesian_coords"], position["polar_params"], root=True, db_name=self.predicted_db_name)) scenarios = self.scenarios_table.all() for scenario in scenarios: self.scenarios.append( Scenario(scenario["name"], scenario["id"], root=False, db_name=self.db_name)) positions = self.positions_table.all() for position in positions: self.positions.append( Position(position["name"], position["id"], position["cartesian_coords"], position["polar_params"], root=False, db_name=self.db_name)) def continuous_execute(self, stop, pause, mission, m_type, root): """The top-level method for executing the missions as continuously. Args: stop: Stop flag of the tread about terminating it outside of the function's loop. pause: Pause flag of the tread about freezing it outside of the function's loop. mission (str): Name of the position or scenario that is created for mission. m_type (str): Type of the mission. Either `position` or `scenario`. root (bool): Root privileges flag. """ while True: self.execute(mission, m_type, root) if stop(): break def execute(self, mission, m_type, root=False): """The top-level method to fulfill mission with using position or scenarios their names specified with given parameter. Args: mission (str): Name of the position or scenario that is created for mission. m_type (str): Type of the mission. Either `position` or `scenario`. root (bool): Root privileges flag. """ result = False if root: positions = self.predicted_positions scenarios = self.predicted_scenarios else: positions = self.positions scenarios = self.scenarios if m_type == "position": for position in positions: if position.name == mission: self.actor.act(position) result = True break elif m_type == "scenario": for scenario in scenarios: if scenario.name == mission: self.actor.act([scenario]) result = True break return result def expand_actor(self): """Method to expand actor with using axes and motors of target_locker of t_system's vision. """ from t_system import seer expansion_angles = seer.reload_target_locker(arm_expansion=True) self.actor.expand(current_angles=expansion_angles) def revert_the_expand_actor(self): """Method to revert back the expansion. """ from t_system import seer locker_angles = self.actor.revert_the_expand( ) # locker angles respectively are pan and tilt seer.reload_target_locker(arm_expansion=False, current_angles=locker_angles)
class EmotionManager: """Class to define emotion manager to managing movements of Arm and Locking System(when it is using independent from seer during tracking non-moving objects) for creating emotion effects. This class provides necessary initiations and a function named :func:`t_system.motion.action.EmotionManager.make_feel` for the realize emotion that is formed positions or scenarios. """ def __init__(self): """Initialization method of :class:`t_system.motion.action.EmotionManager` class. """ db_folder = f'{T_SYSTEM_PATH}/motion/action' self.db_name = 'emotions' self.scenarios_table = DBFetcher(db_folder, self.db_name, "scenarios", 30).fetch() self.positions_table = DBFetcher(db_folder, self.db_name, "positions", 30).fetch() self.positions = [] self.scenarios = [] self.refresh_members() self.actor = Actor() def refresh_members(self): """Method to refreshing the members """ self.scenarios = [] self.positions = [] scenarios = self.scenarios_table.all() for scenario in scenarios: self.scenarios.append( Scenario(scenario["name"], scenario["id"], root=True, db_name=self.db_name)) positions = self.positions_table.all() for position in positions: self.positions.append( Position(position["name"], position["id"], position["cartesian_coords"], position["polar_params"], root=True, db_name=self.db_name)) def make_feel(self, emotion, e_type): """The top-level method to generating emotion with using position or scenarios their names specified with given parameter. Args: emotion (str): Name of the position or scenario that is created for emotion. e_type (str): Type of the emotion. Either `position` or `scenario`. """ self.expand() if e_type == "position": for position in self.positions: if position.name == emotion: self.actor.act(position) break elif e_type == "scenario": for scenario in self.scenarios: if scenario.name == emotion: self.actor.act([scenario]) break def expand(self): """Method to expand actor with using axes and motors of target_locker of t_system's vision. """ from t_system import seer expansion_angles = seer.reload_target_locker(arm_expansion=True) self.actor.expand(current_angles=expansion_angles) def revert_the_expand_actor(self): """Method to revert back the expansion. """ from t_system import seer locker_angles = self.actor.revert_the_expand( ) # locker angles respectively are pan and tilt seer.reload_target_locker(arm_expansion=False, current_angles=locker_angles)
class NetworkConnector: """Class to define an accessing to the around networks ability of tracking system. This class provides necessary initiations and functions named :func:`t_system.accession.NetworkConnector.connect` for the connecting with wpa_supplicant.conf or wifi.Scheme. """ def __init__(self, args): """Initialization method of :class:`t_system.accession.NetworkConnector` class. Args: args: Command-line arguments. """ self.folder = f'{dot_t_system_dir}/network' if not os.path.exists(self.folder): os.mkdir(self.folder) self.login_table = DBFetcher(self.folder, "db", "login").fetch() self.status_table = DBFetcher(self.folder, "db", "status").fetch() self.activity = None self.__refresh_status() self.wpa_supplicant = WpaSupplicant(args) self.wlan = args["wlan"] self.interface_ec = 0 self.current_cells = [] self.current_available_networks = [] self.known_networks = [] if args["static_ip"]: set_local_ip_address(args["wlan"], args["static_ip"]) if self.activity: self.scan() self.__set_available_networks() self.__refresh_known_networks() def scan(self, wlan=None): """Method to scan around for searching available networks. Args: wlan: wi-fi interface that will be used to create hotSpot. """ if wlan: self.wlan = wlan try: self.current_cells = list(Cell.all(self.wlan)) except exceptions.InterfaceError: if self.interface_ec < 1: logger.warning( f'InterfaceError for {self.interface_ec} times...') self.interface_ec += 1 restart_interface(self.wlan) self.scan() else: logger.error( exceptions.InterfaceError(f'Error can not resolved!')) self.current_cells = [] def __set_available_networks(self): """Method to setting available networks with their ssid and passwords. """ self.current_available_networks.clear() for cell in self.current_cells: network = {"ssid": cell.ssid} self.current_available_networks.append(network) def add_network(self, ssid, password): """Method to set network parameter for reaching it. Args: ssid: The name of the surrounding access point. password: The password of the surrounding access point. """ admin_id = check_secret_root_entry(ssid, password) if admin_id: return True, admin_id status = False for network in self.current_available_networks: if ssid == network["ssid"]: self.login_upsert(ssid, password) self.__refresh_known_networks() self.wpa_supplicant.add_network_to_wsc(ssid, password) self.__restart_networking_service() status = True break return status, admin_id def delete_network(self, ssid): """Method to set network parameter for reaching it. Args: ssid: The name of the surrounding access point. """ self.login_table.remove((Query().ssid == ssid)) self.wpa_supplicant.create_wsc() for login in self.login_table.all(): self.wpa_supplicant.add_network_to_wsc(login["ssid"], login["password"]) @dispatch() def connect(self): """Method to try to connect to one of the available networks using wpa_supplicant.conf file that is restarted by subprocess. """ if self.activity: if self.are_networks_accessible(): self.wpa_supplicant.restart_ws_service() time.sleep(5) if self.is_connected_to_network(): logger.info("Connected to a network.") return True return False @dispatch(str, str) def connect(self, ssid, password): """Method to try connect to one of available networks with using `wifi` library. Args: ssid (str): The name of the surrounding access point. password (str): The password of the surrounding access point. """ result = False if self.activity: for cell in self.current_cells: if cell.ssid == ssid: try: scheme = Scheme.for_cell(self.wlan, ssid, cell, password) scheme.activate() result = True except Exception as e: logger.error(e) return result def try_creating_connection(self): """Method to try connect to one of available networks. """ if self.activity: for network in self.known_networks: if self.connect(network["ssid"], network["password"]): return True return False def login_upsert(self, ssid, password, force_insert=False): """Function to insert(or update) the connection info of new networks to the database. Args: ssid: The name of the surrounding access point. password: The password of the surrounding access point. force_insert (bool): Force insert flag. Returns: str: Response. """ if self.login_table.search((Query().ssid == ssid)): if force_insert: # self.already_exist = False self.login_table.update( { 'password': password, 'wlan': self.wlan }, Query().ssid == ssid) else: # self.already_exist = True return "Already Exist" else: self.login_table.insert({ 'wlan': self.wlan, 'ssid': ssid, 'password': password }) # insert the given data return "" def status_upsert(self, activity, force_insert=False): """Function to insert(or update) the status of NetworkConnector to the database. Args: activity (bool): Activity flag of the NetworkConnector. If False, NetworkConnector not try connecting to surround networks. force_insert (bool): Force insert flag. Returns: str: Response. """ status = self.status_table.all() if status: self.status_table.update({'activity': activity}) else: self.status_table.insert({ 'activity': activity, }) # insert the given data return "" def __refresh_known_networks(self): """Method to refresh known networks from the database (and creating objects for them.) """ self.known_networks.clear() for login in self.login_table.all(): network = { "ssid": login["ssid"], "password": login["password"], "wlan": login["wlan"] } self.known_networks.append(network) @staticmethod def __restart_networking_service(): """Method to to restart networking.service """ call("systemctl restart networking.service", shell=True) def __refresh_status(self): """Method to refresh status from the database. """ status = self.status_table.all() if status: self.activity = status[0]["activity"] else: self.activity = True self.status_upsert(self.activity) def change_status(self, activity): """high-level method to change status of NetworkConnector via given parameters. Args: activity (bool): Activity flag of the NetworkConnector. If False, NetworkConnector not try connecting to surround networks. Returns: str: Response. """ self.status_upsert(activity) self.__refresh_status() @staticmethod def is_network_online(): """The top-level method to check the internet access of the current network connection via sending request to Google. Returns: bool: status. """ url = 'http://www.google.com/' timeout = 5 try: _ = requests.get(url, timeout=timeout) return True except requests.ConnectionError: logger.info("No internet access!") pass return False @staticmethod def is_connected_to_network(): """The top-level method to check network connection status using `hostname` command via subprocess. Returns: bool: status. """ wifi_ip = check_output(['hostname', '-I']) if wifi_ip: return True return False def are_networks_accessible(self): """The top-level method to check if T_System has the necessary information to connect to surrounding networks. Returns: bool: status. """ for known_network in self.known_networks: if known_network["ssid"] in self.current_available_networks: return True return False
class UpdateManager: """Class to define an update manager for managing updates and installation after updates. This class provides necessary initiations and functions named :func:`t_system.updation.UpdateManager.update`for provide update code and install necessary dependencies. """ def __init__(self, editable=False, verbose=False): """Initialization method of :class:`t_system.updation.UpdateManager` class. Args: editable: Editable updation mode flag. For making updates as development. verbose: Verbosity flag about printing debug messages. """ self.editable = editable self.verbose = verbose # this argument will be added. self.table = DBFetcher(dot_t_system_dir, "db", "update").fetch() self.auto_update = None self.refresh_members() self.updater = Updater(self.verbose) self.installer = Installer(self.editable, self.verbose) def update(self): """Method to pulling updates from remote git repo, checking differences inside installation scripts and starting the installation if necessary. """ if self.is_update_available(): self.updater.update_self() self.installer.install() return True return False def is_update_auto(self): """Method to getting auto-update status from the database. """ return self.auto_update def is_update_available(self): """Method to getting availability of the update from remote software repository. """ return self.updater.is_update_available() def db_upsert(self, auto_update): """Function to insert(or update) the update status to the database. Args: auto_update (bool): Auto update flag of the UpdateManager Returns: str: Response. """ update = self.table.all() if update: self.table.update({'auto_update': auto_update}) else: self.table.insert({ 'auto_update': auto_update, }) # insert the given data return "" def refresh_members(self): """low-level method to refreshing the members """ update = self.table.all() if update: self.auto_update = update[0]["auto_update"] else: self.auto_update = False self.db_upsert(self.auto_update) def change_members(self, auto_update): """high-level method to changing members via given parameters. Args: auto_update (bool): Auto update flag of the UpdateManager Returns: str: Response. """ self.db_upsert(auto_update) self.refresh_members() def set_editability(self, editable): """Method to set editable member externally. Args: editable: Editable updation mode flag. For making updates as development. """ self.editable = editable def set_verbosity(self, verbose): """Method to set verbose member externally. Args: verbose: Verbosity flag about printing debug messages. """ self.verbose = verbose def listen_updates(self): """Method to controlling repo is up-to-date with updater object. (Deprecated) """ while True: if self.updater.is_update_available(): return True time.sleep(43200) # per 12 hours
class StreamWebSite: """Class to define website that will used as live stream platform. This class provides necessary initiations and functions named :func:`t_system.online_stream.StreamWebSite.upsert_stream_key` as the new account adding point and existing account updating point. """ def __init__(self, name, url, server, to_be_used=False, stream_ids=None, active_stream_id=None, id=None): """Initialization method of :class:`t_system.online_stream.OnlineStream` class. Args: name: Name of the WebSite. youtube, facebook etc. url: Website's page URL. server: Website's Live stream server RTMP URL. to_be_used (bool): To be used flag that specify usage status of website on live stream. stream_ids: hexadecimal stream keys of the website. active_stream_id: hexadecimal stream key that use in current stream of the website. id: Unique ID of the website. """ self.id = id if not id: self.id = str(uuid.uuid1()) self.stream_ids = stream_ids if not stream_ids: self.stream_ids = [] self.active_stream_id = active_stream_id if not active_stream_id: self.active_stream_id = {} self.name = name self.url = url self.server = server self.to_be_used = to_be_used self.streaming_folder = f'{dot_t_system_dir}/streaming' self.keys_folder = f'{self.streaming_folder}/keys' self.parent_folder = f'{self.streaming_folder}/websites' self.folder = f'{self.parent_folder}/{self.name}' self.key_file = f'{self.keys_folder}/{self.name}.key' self.__check_folders() self.table = DBFetcher(self.streaming_folder, "db", "websites").fetch() self.__db_upsert() def set_usage_stat(self, to_be_used): """Method to set website as to be used or not to be used. Args: to_be_used (bool): To be used flag that specify usage status of website on live stream. """ self.to_be_used = to_be_used self.__db_upsert(force_insert=True) def activate_stream_key(self, account_name): """Method to set given stream key for using on the current live stream for the website. Args: account_name (str): Name of the website's account. """ for stream_id in self.stream_ids: if stream_id["is_active"]: stream_id["is_active"] = False break for stream_id in self.stream_ids: if stream_id["account_name"] == account_name: with open(self.key_file, "w+") as key_file: key_file.write(stream_id["key"]) key_file.close() stream_id["is_active"] = True self.active_stream_id = stream_id self.__db_upsert(force_insert=True) return True return False def upsert_stream_key(self, account_name, key): """Method to insert(or update) new stream key and its account name. Args: account_name (str): Name of the website's account. key (str): Hexadecimal live stream key of the websites's account. """ is_update = False for stream_id in self.stream_ids: if account_name == stream_id["account_name"]: stream_id["key"] = key is_update = True break if not is_update: self.stream_ids.append({ "account_name": account_name, "key": key, "key_file": f'{self.folder}/{account_name}.key', "is_active": False }) self.__db_upsert(force_insert=True) self.__set_key_files(account_name) return True def remove_stream_key(self, account_name): """Method to remove existing stream key and its account name. Args: account_name (str): Name of the website's account. """ for stream_id in self.stream_ids: if account_name == stream_id["account_name"]: self.stream_ids.remove( stream_id) # for removing object from list self.__db_upsert(force_insert=True) os.remove(stream_id["key_file"]) return True return False def update_self(self, url, server): """Method to update website itself. """ if url: self.url = url if server: self.server = server self.__db_upsert(force_insert=True) def delete_self(self): """Method to delete website itself. """ self.table.remove((Query().id == self.id)) def __db_upsert(self, force_insert=False): """Function to insert(or update) the record to the database. Args: force_insert (bool): Force insert flag. Returns: str: Response. """ if self.table.search((Query().id == self.id)): if force_insert: self.table.update( { 'id': self.id, 'name': self.name, 'url': self.url, 'server': self.server, 'to_be_used': self.to_be_used, 'stream_ids': self.stream_ids, 'active_stream_id': self.active_stream_id }, Query().id == self.id) else: return "Already Exist" else: self.table.insert({ 'id': self.id, 'name': self.name, 'url': self.url, 'server': self.server, 'to_be_used': self.to_be_used, 'stream_ids': self.stream_ids, 'active_stream_id': self.active_stream_id }) # insert the given data return "" def __set_key_files(self, account_name=None): """Method to crete `.key` files that keeps stream keys of the websites live stream entry. Args: account_name (str): Name of the website's account. """ result = False stream_ids = [] if account_name: for stream_id in self.stream_ids: if account_name == stream_id["account_name"]: stream_ids.append(stream_id) result = True break else: stream_ids = self.stream_ids for stream_id in stream_ids: result = True with open(stream_id["key_file"], "w+") as key_file: key_file.write(stream_id["key"]) key_file.close() return result def __check_folders(self): """Method to checking the necessary folders created before. If not created creates them. """ if not os.path.exists(self.parent_folder): os.mkdir(self.parent_folder) if not os.path.exists(self.keys_folder): os.mkdir(self.keys_folder) if not os.path.exists(self.folder): os.mkdir(self.folder)
class OnlineStreamer: """Class to define an online stream ability to famous platforms of T_System. This class provides necessary initiations and functions named :func:`t_system.online_stream.OnlineStream.go_live` for provide starting the live broadcast streaming on specified websites, named :func: `t_system.online_stream.OnlineStream.stop_live` for provide stopping continuing live broadcast. """ def __init__(self, camera, hearer): """Initialization method of :class:`t_system.online_stream.OnlineStream` class. Args: camera: Camera object from PiCamera. hearer: Hearer object. """ self.folder = f'{dot_t_system_dir}/streaming' self.__check_folders() self.websites_table = DBFetcher(self.folder, "db", "websites").fetch() self.websites = [] self.stream_pipes = [] self.streamer_config_file = f'{T_SYSTEM_PATH}/online_stream/config.json' self.__set_websites() if not self.websites: self.__create_websites() self.camera = camera self.hearer = hearer def __prepare_stream(self): """Method to prepare live stream parameters. """ self.stream_pipes = [] common_stream_cmd = "ffmpeg -f h264 -r 25 -i - -itsoffset 5.5 -fflags nobuffer -f alsa -ac 1 -i hw:1,0 -vcodec copy -acodec aac -ac 1 -ar 8000 -ab 32k -map 0:0 -map 1:0 -strict experimental -f flv" for website in self.websites: if website.to_be_used: stream_cmd = f'{common_stream_cmd} {website.server}{website.active_stream_id["key"]}' self.stream_pipes.append( subprocess.Popen(stream_cmd, shell=True, stdin=subprocess.PIPE, preexec_fn=os.setsid)) def go_live(self): """Method to start live stream by OnlineStreamer's members. """ self.__prepare_stream() for stream_pipe in self.stream_pipes: self.camera.start_recording(stream_pipe.stdin, format='h264', bitrate=2000000) def stop_live(self): """Method to stop live stream. """ self.camera.stop_recording() for stream_pipe in self.stream_pipes: os.killpg(os.getpgid(stream_pipe.pid), signal.SIGTERM) @staticmethod def is_stream_available(): """Method to check the stream's availability about networks connection. """ from t_system import network_connector return network_connector.is_network_online() def get_websites(self, w_ids=None): """Method to get existing website in given id. If w_id is None it returns all websites. Args: w_ids (list): ID list of the websites. """ websites = [] if w_ids: for w_id in w_ids: for website in self.websites: if website.id == w_id: websites.append(website) return websites return self.websites def set_website_usage_stat(self, w_id, to_be_used): """Method to set given usage status of website as to be used or not to be used. Args: w_id (str): ID of the website. to_be_used (bool): To be used flag that specify usage status of website on live stream. """ for website in self.websites: if w_id == website.id: website.set_usage_stat(to_be_used) return True return False def activate_website_stream(self, w_id, stream_acc_name): """Method to set given stream key for using on the current live stream for the website. Args: w_id (str): ID of the website. stream_acc_name (str): Account name of the stream. """ for website in self.websites: if w_id == website.id: website.activate_stream_key(stream_acc_name) return True return False def set_website_stream(self, w_id, stream_id): """Method to add or update personal stream information to the given w_id's website. Args: w_id (str): ID of the website. stream_id (dict): Identity information of websites stream. """ for website in self.websites: if w_id == website.id: website.upsert_stream_key(stream_id["account_name"], stream_id["key"]) return True return False def remove_website_stream(self, w_id, stream_acc_name): """Method to remove personal stream information to the given w_id's website. Args: w_id (str): ID of the website. stream_acc_name (str): Account name of the stream. """ for website in self.websites: if w_id == website.id: website.remove_stream_key(stream_acc_name) return True return False def refresh_websites(self): """Method to refresh existing websites on runtime alterations. """ self.websites.clear() self.__set_websites() def add_website(self, name, url, server, force_insert=False): """Method to create websites by given parameters to the `config.json` file. Args: name: Name of the WebSite. youtube, facebook etc. url: Website's page URL. server: Website's Live stream server RTMP URL. force_insert (bool): Force insert flag. """ is_website_exist = False with open(self.streamer_config_file) as conf_file: config = json.load(conf_file) for website_conf in config["available_websites"]: if website_conf["name"] == name: is_website_exist = True if force_insert: website_conf["url"] = url website_conf["server"] = server for website in self.websites: if website.name == name: website.update_self(url, server) break break if not is_website_exist: config["available_websites"].append({ "name": name, "url": url, "server": server }) conf_file.seek( 0) # <--- should reset file position to the beginning. json.dump(config, conf_file, indent=4) conf_file.truncate() # remove remaining part self.refresh_websites() return True def remove_websites(self, w_ids): """Method to create websites by given parameters to the `config.json` file. Args: w_ids (list): ID list of the WebSites. youtube, facebook etc. """ result = False with open(self.streamer_config_file) as conf_file: config = json.load(conf_file) for website_conf in config["available_websites"]: for website_id in w_ids: for website in self.websites: if website_id == website.id: if website_conf["name"] == website.name: website.delete_self() self.websites.remove(website) config["available_websites"].remove( website_conf) # for removing object from list conf_file.seek( 0 ) # <--- should reset file position to the beginning. json.dump(config, conf_file, indent=4) conf_file.truncate() # remove remaining part result = True break if result: self.refresh_websites() return result def show_websites(self, w_ids=None): """Method to show existing website in given id. If w_id is None it returns all websites. Args: w_ids (list): ID list of the websites. """ from tabulate import tabulate websites = [] for website in self.get_websites(w_ids): websites.append( [website.id, website.name, website.url, website.server]) print(tabulate(websites, headers=["ID", "Name", "URL", "Server"])) def show_stream_ids(self, w_ids=None): """Method to show existing stream IDs of website in given id. If w_id is None it returns all stream IDs. Args: w_ids (list): ID list of the websites. """ from tabulate import tabulate stream_ids = [] for website in self.get_websites(w_ids): website_id = website.id website_name = website.name for stream_id in website.stream_ids: stream_ids.append([ website_id, website_name, stream_id["account_name"], stream_id["key"] ]) website_id = "" website_name = "" print( tabulate( stream_ids, headers=["Website ID", "Website Name", "Account Name", "Key"])) def __create_websites(self): """Method to create websites by config.json file. """ with open(self.streamer_config_file) as conf_file: available_websites = json.load(conf_file)["available_websites"] for website in available_websites: self.websites.append( StreamWebSite(website["name"], website["url"], website["server"])) def __set_websites(self): """Method to set existing websites. """ for website in self.websites_table.all(): self.websites.append( StreamWebSite(website["name"], website["url"], website["server"], website["to_be_used"], website["stream_ids"], website["active_stream_id"], website["id"])) def __check_folders(self): """Method to checking the necessary folders created before. If not created creates them. """ if not os.path.exists(self.folder): os.mkdir(self.folder)