Example #1
0
    def start_web_interface(self, port):
        """启动 web 管理服务器

        Args:
            port (int): web 服务器端口号
        """
        if self.web_interface_thread is None:
            # start new
            self.web_interface_thread = HttpProcess(port, self.rtk_names_queue,
                                                    self.status_queue)
            self.web_interface_thread.start()
Example #2
0
    def start_web_interface(self, port):
        """启动 web 管理服务器

        Args:
            port (int): web 服务器端口号
        """
        if self.web_interface_thread is None:
            # start new
            self.web_interface_thread = HttpProcess(port, self.rtk_names_queue, self.status_queue)
            self.web_interface_thread.start()
Example #3
0
class Rtk:
    def __init__(self):
        self.rtk_threads = {}
        self.thread_count = 0
        self.web_interface_thread = None
        self.is_interrupt = False
        self.rtk_names_queue = multiprocessing.Queue()
        self.status_queue = multiprocessing.Queue()
        # log init
        self.configs = self.load_config()
        if 'logPath' in self.configs.keys() and os.path.isdir(self.configs['logPath']):
            log.log_dir = self.configs['logPath']
        multiprocessing.current_process().name = 'rtk'
        log.init(multiprocessing.current_process().name, True)

    def exit_by_signal(self, signum, frame):
        """响应 SIGINT, SIGTERM"""
        self.is_interrupt = True

    def wait_for_keyboard(self):
        """quit when press q or press ctrl-c, or exception from other threads"""
        try:
            while True:
                time.sleep(2)   # 减少死锁概率
                print("enter 'q' to quit, 'r' to reload, 'l' to list ports.")
                key = input().lower().strip()
                if key == 'q':
                    break
                elif key == 'r':
                    log.info('main: reload config.')
                    self.start_threads_from_config()
                elif key == 'l':
                    try:
                        print('name, station_port, dispatch_port, control_port')
                        for name, config in sorted(self.configs['entry'].items()):
                            control_port = str(config['controlPort']) if 'controlPort' in config else None
                            print('%s, %d, %d, %s'
                                  % (name, config['stationPort'], config['listenPort'], control_port))
                    except Exception as e:
                        print('Error when list config: %s' % e)
        except KeyboardInterrupt:
            pass
        except (EOFError, OSError):
            # no input
            signal.signal(signal.SIGINT, self.exit_by_signal)
            signal.signal(signal.SIGTERM, self.exit_by_signal)
            while not self.is_interrupt:
                time.sleep(1)

    def start_threads_from_config(self):
        """读取配置文件,启动所有 rtk 线程"""
        # config
        configs = self.load_config()
        self.configs = configs

        # web 管理界面
        try:
            port = configs['webInterface']['port']
            if configs['webInterface']['allow'].lower() == 'true':
                self.start_web_interface(port)
        except Exception as e:
            log.error('main: failed to start web interface: %s' % e)
        # rtk 转发服务
        try:
            if 'entry' in configs.keys():
                self.start_rtk_threads(configs['entry'])
        except Exception as e:
            log.error('main: failed to start rtk threads: %s' % e)
        # web 管理界面的配置
        try:
            if configs['webInterface']['allow'].lower() == 'true':
                self.update_web_interface(sorted(configs['entry'].keys()))
        except Exception as e:
            log.error('main: failed to update web interface: %s' % e)

    def start_rtk_threads(self, entries):
        """停止某 rtk 线程,不等待

        之后需要调用 wait_for_thread

        Args:
            entries (dict[str, dict]): 各组 rtk 转发配置
        """
        if isinstance(entries, dict):
            # start threads from config
            for name, config in entries.items():
                # start one thread
                try:
                    if name in self.rtk_threads.keys():
                        # 如果已有
                        rtk_group = self.rtk_threads[name]
                        # 判断配置是否发生改变,如果不变并且在运行,就跳过
                        if rtk_group.is_alive() and rtk_group.config == config:
                            continue
                        self.stop_and_wait_for_thread(name)
                    rtk_group = RtkGroup(name, self.thread_count, config, self.status_queue)
                    self.thread_count += 1
                    rtk_group.start()
                    self.rtk_threads[name] = rtk_group
                except Exception as e:
                    log.error('main: failed to start thread %s: %s' % (name, e))
            # stop threads not in config
            for name in self.rtk_threads.keys():
                if name not in entries.keys():
                    self.stop_and_wait_for_thread(name)
                    del self.rtk_threads[name]

    def stop_thread(self, name):
        """停止某 rtk 线程,不等待

        之后需要调用 wait_for_thread

        Args:
            name (str): rtk 线程名
        """
        try:
            if name in self.rtk_threads.keys():
                rtk_thread = self.rtk_threads[name]
                if isinstance(rtk_thread, RtkGroup):
                    rtk_thread.stop()
                    log.info('main: require stop thread %d %s.' % (rtk_thread.thread_id, name))
        except Exception as e:
            log.error('main: failed to stop thread %s: %s' % (name, e))

    def wait_for_thread(self, name):
        """等待某 rtk 线程完全退出

        在 stop_thread 之后调用

        Args:
            name (str): rtk 线程名
        """
        try:
            if name in self.rtk_threads.keys():
                rtk_thread = self.rtk_threads[name]
                if isinstance(rtk_thread, RtkGroup):
                    # wait
                    rtk_thread.join()
                log.info('main: thread %d %s has stopped.' % (rtk_thread.thread_id, name))
                # remove
                del self.rtk_threads[name]
        except Exception as e:
            log.error('main: error when wait for thread %s: %s' % (name, e))

    def stop_and_wait_for_thread(self, name):
        """停止某 rtk 线程,等待直到退出成功

        Args:
            name (str): rtk 线程名
        """
        self.stop_thread(name)
        self.wait_for_thread(name)

    def start_web_interface(self, port):
        """启动 web 管理服务器

        Args:
            port (int): web 服务器端口号
        """
        if self.web_interface_thread is None:
            # start new
            self.web_interface_thread = HttpProcess(port, self.rtk_names_queue, self.status_queue)
            self.web_interface_thread.start()

    def update_web_interface(self, rtk_names):
        """更新 web 管理服务器的配置

        Args:
            rtk_names (list[str]): 开启的 rtk 服务名
        """
        if self.web_interface_thread is not None:
            self.web_interface_thread.update_names(rtk_names)

    def stop_and_wait_for_web_interface(self):
        """关闭 web 管理服务器"""
        if isinstance(self.web_interface_thread, HttpProcess):
            self.web_interface_thread.stop()
            self.web_interface_thread.join()
        self.web_interface_thread = None

    def load_config(self):
        """载入配置文件

        先读入 conf/config.json 中的配置,再读入 conf/ 中其他 json 文件里的 entry
        """
        config_dir = os.path.join(sys.path[0], 'conf')
        configs = {}
        # main config
        config_file_name = os.path.join(config_dir, 'config.json')
        try:
            with open(config_file_name) as config_fp:
                configs = json.load(config_fp)
        except Exception as e:
            log.error('main: failed to load config from conf/config.json: %s' % e)
        if 'entry' not in configs.keys():
            configs['entry'] = {}

        # other entries
        for dir_path, dir_names, file_names in os.walk(config_dir):
            for file_name in file_names:
                if file_name != 'config.json' and file_name.endswith('.json'):
                    # load sub config
                    config_file_name = os.path.join(config_dir, file_name)
                    try:
                        with open(config_file_name) as config_fp:
                            sub_configs = json.load(config_fp)
                        # insert into main config
                        for name, config in sorted(sub_configs['entry'].items()):
                            if name not in configs['entry'].keys():
                                configs['entry'][name] = config
                    except Exception as e:
                        log.error('main: failed to load config from conf/%s: %s' % (file_name, e))

        return configs

    def main(self):
        log.info('main: start')

        # start rtk
        self.start_threads_from_config()

        # wait
        self.wait_for_keyboard()

        # quit & clean up
        for name in self.rtk_threads.keys():
            self.stop_thread(name)
        for name in sorted(self.rtk_threads.keys()):
            self.wait_for_thread(name)
        self.stop_and_wait_for_web_interface()

        # clear queue
        while not self.status_queue.empty():
            self.status_queue.get(block=False)

        log.info('main: bye')
        log.close_all()
Example #4
0
class RtkProcessMgr(threading.Thread):
    def __init__(self, configs):
        super().__init__()
        self.init_configs = configs
        self.rtk_threads = {}
        self.thread_count = 0
        self.web_interface_thread = None
        self.rtk_names_queue = multiprocessing.Queue()
        self.status_queue = multiprocessing.Queue()
        self.running = True

    def start_threads_from_config(self, configs):
        """读取配置文件,启动所有 rtk 线程"""

        # web 管理界面
        try:
            port = configs['webInterface']['port']
            if configs['webInterface']['allow'].lower() == 'true':
                self.start_web_interface(port)
        except Exception as e:
            log.error('main: failed to start web interface: %s' % e)
        # rtk 转发服务
        try:
            if 'entry' in configs.keys():
                self.start_rtk_threads(configs['entry'])
        except Exception as e:
            log.error('main: failed to start rtk threads: %s' % e)
        # web 管理界面的配置
        try:
            if configs['webInterface']['allow'].lower() == 'true':
                self.update_web_interface(sorted(configs['entry'].keys()))
        except Exception as e:
            log.error('main: failed to update web interface: %s' % e)

    def start_rtk_threads(self, entries):
        """根据配置文件启动 rtk 线程

        Args:
            entries (dict[str, Entry]): 各组 rtk 转发配置
        """
        if isinstance(entries, dict):
            # stop threads not in config
            for name in sorted(self.rtk_threads.keys()):
                if name not in entries.keys():
                    self.stop_and_wait_for_thread(name)
            # start threads from config
            for name, config in entries.items():
                if isinstance(config, Entry):
                    # start one thread
                    try:
                        if name in self.rtk_threads.keys():
                            # 如果已有
                            rtk_group = self.rtk_threads[name]
                            # 判断配置是否发生改变,如果不变并且在运行,就跳过
                            if rtk_group.is_alive(
                            ) and rtk_group.config == config:
                                continue
                            self.stop_and_wait_for_thread(name)
                        rtk_group = RtkGroup(name, self.thread_count, config,
                                             self.status_queue)
                        self.thread_count += 1
                        rtk_group.start()
                        self.rtk_threads[name] = rtk_group
                    except Exception as e:
                        log.error('main: failed to start thread %s: %s' %
                                  (name, e))

    def stop_thread(self, name):
        """停止某 rtk 线程,不等待

        之后需要调用 wait_for_thread

        Args:
            name (str): rtk 线程名
        """
        try:
            if name in self.rtk_threads.keys():
                rtk_thread = self.rtk_threads[name]
                if isinstance(rtk_thread, RtkGroup):
                    rtk_thread.stop()
                    log.info('main: require stop thread %d %s.' %
                             (rtk_thread.thread_id, name))
        except Exception as e:
            log.error('main: failed to stop thread %s: %s' % (name, e))

    def wait_for_thread(self, name):
        """等待某 rtk 线程完全退出

        在 stop_thread 之后调用

        Args:
            name (str): rtk 线程名
        """
        try:
            if name in self.rtk_threads.keys():
                rtk_thread = self.rtk_threads[name]
                if isinstance(rtk_thread, RtkGroup):
                    # wait
                    rtk_thread.join()
                log.info('main: thread %d %s has stopped.' %
                         (rtk_thread.thread_id, name))
                # remove
                del self.rtk_threads[name]
        except Exception as e:
            log.error('main: error when wait for thread %s: %s' % (name, e))

    def stop_and_wait_for_thread(self, name):
        """停止某 rtk 线程,等待直到退出成功

        Args:
            name (str): rtk 线程名
        """
        self.stop_thread(name)
        self.wait_for_thread(name)

    def start_web_interface(self, port):
        """启动 web 管理服务器

        Args:
            port (int): web 服务器端口号
        """
        if self.web_interface_thread is None:
            # start new
            self.web_interface_thread = HttpProcess(port, self.rtk_names_queue,
                                                    self.status_queue)
            self.web_interface_thread.start()

    def update_web_interface(self, rtk_names):
        """更新 web 管理服务器的配置

        Args:
            rtk_names (list[str]): 开启的 rtk 服务名
        """
        if self.web_interface_thread is not None:
            self.web_interface_thread.update_names(rtk_names)

    def stop_and_wait_for_web_interface(self):
        """关闭 web 管理服务器"""
        if isinstance(self.web_interface_thread, HttpProcess):
            self.web_interface_thread.stop()
            self.web_interface_thread.join()
        self.web_interface_thread = None

    def run(self):
        """线程管理工具的主循环,阻塞运行直到 looping 为 False"""
        # start rtk
        self.start_threads_from_config(self.init_configs)

        # wait
        while self.running:
            time.sleep(1)

        # quit & clean up
        for name in self.rtk_threads.keys():
            self.stop_thread(name)
        for name in sorted(self.rtk_threads.keys()):
            self.wait_for_thread(name)
        self.stop_and_wait_for_web_interface()

        # clear queue
        while not self.status_queue.empty():
            self.status_queue.get(block=False)