Pythony应用(01)-学习监控(03)

UC01–远程查看孩子的实时电脑桌面

US01-日志记录:

  1. 日志对象:MyLogger
  • 简单的日志格式:2022-05-02 20:09:14.399::MainProcess:: INFO:: 主进程启动,主进程pid:18016
  • 复杂的日志格式:2022-05-02 20:10:27.318 +0800|MainProcess|MainThread:18024-12804|capture_uc01.py:103|INFO|主程序运行中
  • 日志支持控制台和文件双输出
  • 日志文件为循环日志文件,每个5M,备份3个文件,utf-8编码
  • 日志文件名:日志名称.log, 备份文件为 日志文件名.1.log,例如:MainProcess.log, MainProcess.1.log, MainProcess.2.log
  1. 主进程对象:MainProcess
  • 信号量处理
  • 输出运行状态

import logging
from logging.handlers import RotatingFileHandler
import os
import signal
import sys
import time


__all__ = ("MyLogger", "MainProcess")


def __dir__():
    return __all__


class MyLogger:
    log_path = os.path.join(os.path.dirname(__file__), "logs", "my.log")

    @classmethod
    def get(cls, log_name=__name__, level=logging.INFO,
            fmt_simple=True, logger=None):
        if not logger:
            logger = logging.getLogger(log_name)
            logger.setLevel(level=level)

        fmt_str = ("%(asctime)s.%(msecs)03d {}|%(name)s|"
                   "%(threadName)s:%(process)d-%(thread)d|"
                   "%(filename)s:%(lineno)s|"
                   "%(levelname)s|%(message)s").format(time.strftime("%z"))
        if fmt_simple:
            fmt_str = ("%(asctime)s.%(msecs)03d::%(name)s::"
                       "%(levelname)5s:: %(message)s")

        date_fmt = "%Y-%m-%d %H:%M:%S"
        log_format = logging.Formatter(fmt_str, datefmt=date_fmt)

        # 循环日志
        log_file_name = os.path.join(os.path.dirname(cls.log_path),
                                     f"{log_name}.log")
        handle = RotatingFileHandler(log_file_name, maxBytes=5 * 1024 * 1024,
                                     backupCount=3, encoding="utf-8")
        handle.setLevel(level)
        handle.setFormatter(log_format)
        # 设置日志名称
        handle.namer = cls.log_file_name

        # 给logger添加handler
        logger.addHandler(handle)

        # 控制台日志
        console = logging.StreamHandler()
        console.setLevel(level)
        console.setFormatter(log_format)
        logger.addHandler(console)

        return logger

    @staticmethod
    def log_file_name(default_filename):
        dir_name = os.path.dirname(default_filename)
        file_name = os.path.basename(default_filename)

        fields = file_name.split(".")
        if len(fields) < 3:
            return default_filename
        fields[-1], fields[-2] = fields[-2], fields[-1]
        file_name = ".".join(fields)
        file_name = os.path.join(dir_name, file_name)
        return file_name


class MainProcess:
    logger = MyLogger.get("MainProcess", level=logging.INFO)

    @classmethod
    def break_signal_func(cls, signal_value, frame):
        cls.logger.info("信号:%s %s", str(signal_value), str(frame))
        time.sleep(1)
        sys.exit(0)

    @classmethod
    def signal_proc(cls):
        signal.signal(signal.SIGINT, cls.break_signal_func)
        signal.signal(signal.SIGTERM, cls.break_signal_func)
        signal.signal(signal.SIGILL, cls.break_signal_func)

    @classmethod
    def main_run(cls):
        cls.logger.info("主进程启动,主进程pid:%d", os.getpid())

        # 主进程
        cls.signal_proc()
        while True:
            cls.logger.info("主程序运行中")
            time.sleep(30)


if __name__ == '__main__':
    MainProcess.main_run()

US02-多进程处理:

  1. 主进程对象:MainProcess
  • capture() 抓屏进程处理函数
  • web() web服务进程处理函数
  1. Web服务对象:MonitorPageWebService
  • main_run() web服务主函数
  1. 抓屏对象:Capture
  • capture() 抓屏处理函数
  • del_his_file() 删除三天前的历史图片文件函数

class MonitorPageWebService:
    logger = None  # 在main_run中初始化
    save_capture_pic_path = os.path.join(
        os.path.dirname(__file__), "static", "capture_pic")

    @classmethod
    def main_run(cls):
        cls.logger = MyLogger.get("WebService", level=logging.INFO)
        while True:
            cls.logger.info("程序运行中")
            time.sleep(30)

class Capture:
    sampling_times_minute = 1  # 每分钟采样次数
    save_pic_path = os.path.join(os.path.dirname(__file__),
                                 "static", "capture_pic")

    logger = MyLogger.get("Capture", level=logging.DEBUG)

    @classmethod
    def capture(cls):
        while True:
            cls.logger.info("程序运行中")
            time.sleep(30)

    @classmethod
    def del_his_file(cls, del_secs=3 * 24 * 3600):
        """"""


class MainProcess:
    logger = MyLogger.get("MainProcess", level=logging.INFO)
    child_process = []

    @classmethod
    def break_signal_func(cls, signal_value, frame):
        cls.logger.info("信号:%s %s", str(signal_value), str(frame))
        for child in cls.child_process:
            child.terminate()
            # os.kill(child.pid, signal.SIGTERM)
            cls.logger.info("终止:%s", str(child))

        cls.logger.info("等待子进程退出")
        time.sleep(1)
        sys.exit(0)

    @classmethod
    def signal_proc(cls):
        signal.signal(signal.SIGINT, cls.break_signal_func)
        signal.signal(signal.SIGTERM, cls.break_signal_func)
        signal.signal(signal.SIGILL, cls.break_signal_func)

    @classmethod
    def main_run(cls):
        cls.logger.info("主进程启动,主进程pid:%d", os.getpid())

        # 启动抓屏进程
        capture_proc = mp.Process(target=cls.capture, name="抓屏进程")
        cls.child_process.append(capture_proc)
        capture_proc.start()
        cls.logger.info("已启动进程:%s", str(capture_proc))

        # 启动web service进程
        web_proc = mp.Process(target=cls.web, name="Web服务进程")
        cls.child_process.append(web_proc)
        web_proc.start()
        cls.logger.info("已启动进程:%s", str(web_proc))

        # 主进程
        cls.signal_proc()
        while True:
            cls.logger.info("主程序运行中")
            time.sleep(30)

    @classmethod
    def capture(cls):
        Capture.sampling_times_minute = 2
        Capture.del_his_file()
        Capture.capture()

    @classmethod
    def web(cls):
        MonitorPageWebService.main_run()

US03-提供Web服务:

  1. 界面设计:
  • 上面一排操作单选框:实时屏幕画面、当前屏幕图片、历史屏幕图片、实时摄像头画面
  • 下面是图片或者视频窗口
  1. Web服务对象:MonitorPageWebService
  • main_run() web服务主函数
  • get_html() 主界面Html模板代码
  • camera_gen(camera) 生成摄像头视频流一帧
  • camera_video_feed() 返给前台视频流一帧
  • screen_gen() 生成屏幕视频流一帧
  • screen_video_feed():返给前台屏幕视频流一帧
  • new_capture_pic() 获取抓屏最新一张图片
  • his_capture_pic(filename): 返回前台一张图片,使用send_file, mimetype=‘image/png’
  • main_page(): 返给前台主页面
  • args_proc(): 命令行参数处理,获取允许的接入ip地址和服务的端口
  1. main_run函数分析:
  • main_page(): 主页面, url路由:/
  • screen_video_feed(): 屏幕视频流地址, url路由:/screen_video_feed
  • new_capture_pic(): 最新图片, url路由:/new_capture_pic
  • his_capture_pic(): 历史图片, url路由:/his_capture_pic//
  • camera_video_feed(): 摄像头视频流地址, url路由:/camera_video_feed
  • limit_remote_addr(): 限定访问的IP地址
class MonitorPageWebService:
    logger = None  # 在main_run中初始化
    save_capture_pic_path = os.path.join(
        os.path.dirname(__file__), "static", "capture_pic")

    def __init__(self):
        """"""

    @classmethod
    def get_html(cls, form_field):
        html = """
            <!DOCTYPE html>
            <html lang="en">
            <head>
                <meta charset="UTF-8">
                <title>学习屏幕监控器</title>
                <link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
            </head>
            <body>
            <form id=main_from action="." method="POST">
                <table border = 0 cellpadding="20">
                    <tr>
                        <td></td>
                    {% for handle in handlers.values() %}
                        <td>
                            <input type="radio" name="operator" 
                            value="{{handle['value']}}" 
                            οnclick="main_from.submit();" 
                            {{handle['checked']}}>{{handle['text']}}
                        </td>
                    {% endfor %}
                    </tr>
                </table>
                <table border = 0>
                <tr>
                <td colspan="2">
                <div id="displayFileName" style="text-align:center">

                </div>
                </td>
                </tr>
                <tr>
                    <td>
                    <div style="height:600px; width:100%; overflow:auto">
                    <div>
                    {% for filename in his_filename_list %}
                        <a href="javascript:void(0);" οnclick="fa_click(this)"> 
                        {{ filename }} </a> <BR>
                    {% endfor %}
                    </div>
                    </div>
                    </td>

                    <td>
                        <img id="displayPic" src="{{ cur_handler.imgSrc }}" 
                        width="100%" height="100%" alt="monitor capture" 
                        οnclick="img_display_pic_onclick(this)">
                    </td>

                </tr>

                </table>
            </form>         
                <script type="text/javascript">
                    function img_display_pic_onclick(elem){
                        var src = elem.src.split('?')[0];
                        src = src + '?' + (new Date().getTime());
                        elem.src = src;
                    }
                </script>   
            </body>
            
            </html>
        """

        handlers = {
            "realTimeScreen": {
                "value": "realTimeScreen", "text": "实时屏幕画面",
                "checked": "checked", "imgSrc": url_for('screen_video_feed')
            },
            "curPic": {"value": "curPic", "text": "当前屏幕图片",
                       "checked": "", "imgSrc": url_for('new_capture_pic')},
            "hisPic": {"value": "hisPic", "text": "历史屏幕图片",
                       "checked": "", "imgSrc": url_for('new_capture_pic')},
            "realTimeCamera": {
                "value": "realTimeCamera", "text": "实时摄像头画面",
                "checked": "", "imgSrc": url_for('camera_video_feed')},
        }

        cur_operator = ""
        if form_field.get("operator", None):
            for item in handlers.values():
                if item["value"] == form_field["operator"]:
                    cur_operator = form_field["operator"]
                    item["checked"] = "checked"
                else:
                    item["checked"] = ""

        his_filename_list = []
        if form_field["operator"] == "hisPic":
            cur_operator = "hisPic"
            his_filename_list = cls.get_capture_his_pic()
            handlers["hisPic"]["imgSrc"] = url_for('new_capture_pic')

        return render_template_string(html, form_field=form_field,
                                      handlers=handlers,
                                      cur_handler=handlers[cur_operator],
                                      his_filename_list=his_filename_list)

    @staticmethod
    def camera_gen(camera):
        return ''

    @classmethod
    def camera_video_feed(cls):
        """视频流的路线。将其放在img标记的src属性中。"""
        return ""

    @staticmethod
    def screen_gen():
        return ''

    @classmethod
    def screen_video_feed(cls):
        """视频流的路线。将其放在img标记的src属性中。"""
        return ''

    @classmethod
    def new_capture_pic(cls):
        return ''

    @classmethod
    def his_capture_pic(cls, filename):
        if not filename:
            filename_list = os.listdir(cls.save_capture_pic_path)
            if filename_list:
                filename = filename_list[0]
            else:
                filename = os.path.join(cls.save_capture_pic_path,
                                        "..", "favicon.ico")
        new_filename = os.path.join(cls.save_capture_pic_path, filename)
        return send_file(new_filename, mimetype='image/png')

    @classmethod
    def main_page(cls):
        """主页面"""
        if request.method not in ('GET', 'POST'):
            return jsonify({'code': 500, 'msg': '不支持该请求'})

        form_field = {"operator": "realTimeScreen"}
        if request.method == 'POST':
            form_field = request.form
        return cls.get_html(form_field)

    @staticmethod
    def args_proc():
        allowed_remote_address = ""  # "ip1;ip2;ip3;"
        port = 22430

        if len(sys.argv) > 1:
            port = sys.argv[1]

        if len(sys.argv) > 2:
            allowed_remote_address = f";{sys.argv[2]};"

        return allowed_remote_address, port

    @classmethod
    def main_run(cls):
        app = Flask(__name__)
        cls.logger = MyLogger.get("WebService", level=logging.INFO,
                                  logger=app.logger)
        allowed_remote_address, port = cls.args_proc()

        # 主页面
        @app.route('/', methods=['GET', 'POST'])
        def main_page():
            return cls.main_page()

        @app.route('/screen_video_feed')
        def screen_video_feed():
            return cls.screen_video_feed()

        @app.route('/new_capture_pic')
        def new_capture_pic():
            return cls.new_capture_pic()

        @app.route('/his_capture_pic/<filename>/')
        def his_capture_pic(filename=""):
            return cls.his_capture_pic(filename)

        @app.route('/camera_video_feed')
        def camera_video_feed():
            return cls.camera_video_feed()

        @app.before_request
        def limit_remote_addr():
            if not allowed_remote_address:
                return

            if allowed_remote_address.find(request.remote_addr) < 0:
                abort(403)  # Forbidden

        app.run(host="0.0.0.0", port=port)

US04-抓屏:

  1. 抓屏对象Capture:
  • sampling_times_minute 每分钟采样次数
  • save_pic_path 保存图片的路径
  • logger 日志对象
  • capture() 实时不停的抓拍
  • get_filename() 获取截图文件名
  • del_his_file() 删除del_secs=3 * 24 * 3600秒前的历史文件

class Capture:
    sampling_times_minute = 1  # 每分钟采样次数
    save_pic_path = os.path.join(os.path.dirname(__file__),
                                 "static", "capture_pic")

    logger = MyLogger.get("Capture", level=logging.DEBUG)

    @classmethod
    def capture(cls):
        while True:
                filename = cls.get_filename()
                im = ImageGrab.grab()
                im.save(filename)
                cls.logger.info("生成文件:%s", filename)
                time.sleep(0.4)

    @classmethod
    def get_filename(cls):
        now_str = time.strftime("%Y%m%d_%H%M%S")
        filename = os.path.join(cls.save_pic_path, f"capture_{now_str}.png")
        return filename

    @classmethod
    def del_his_file(cls, del_secs=3 * 24 * 3600):
        """"""
        for filename in os.listdir(cls.save_pic_path):
            filename = os.path.join(cls.save_pic_path, filename)
            if not os.path.isfile(filename):
                continue
            file_stat = os.stat(filename)
            if file_stat.st_ctime + del_secs < time.time():
                # os.remove(filename)
                cls.logger.info("删除文件:%s", filename)

US05-视频流:

  1. WEb服务对象MonitorPageWebService:
  • screen_gen 产生一帧图片
  • screen_video_feed 返回给前台
  • main_run.screen_video_feed 路由
  • img的src为screen_video_feed,从url获取图片
class MonitorPageWebService:
    ......
    @classmethod
    def get_html(cls, form_field):
        html = """
            ......
                        <img id="displayPic" src="/screen_video_feed" 
                        width="100%" height="100%" alt="monitor capture" >
            ......
        """
    ......

    @staticmethod
    def screen_gen():
        while True:
            pil_image = ImageGrab.grab()
            image = cv2.cvtColor(np.asarray(pil_image), cv2.COLOR_RGB2BGR)
            ret, jpeg = cv2.imencode('.jpg', image)
            frame = jpeg.tobytes()
            yield (b'--frame\r\n'
                   b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n\r\n')

    @classmethod
    def screen_video_feed(cls):
        """视频流的路线。将其放在img标记的src属性中。"""
        return Response(cls.screen_gen(),
                        mimetype='multipart/x-mixed-replace; boundary=frame')
    ......
    @classmethod
    def main_run(cls):
        ......
        @app.route('/screen_video_feed')
        def screen_video_feed():
            return cls.screen_video_feed()
        ......

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mengyoufengyu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值