Coverage for src/unit_cooler/actuator/web_server.py: 92%
60 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-07-23 14:35 +0000
« prev ^ index » next coverage.py v7.9.1, created at 2025-07-23 14:35 +0000
1#!/usr/bin/env python3
2"""
3室外機冷却システムの WebUI サーバーを提供します。
5Usage:
6 web_server.py [-c CONFIG] [-p PORT] [-D]
8Options:
9 -c CONFIG : CONFIG を設定ファイルとして読み込んで実行します。[default: config.yaml]
10 -p PORT : Web サーバーを動作させるポートを指定します。[default: 5000]
11 -D : デバッグモードで動作します。
12"""
14import logging
15import threading
17import flask
18import flask_cors
19import my_lib.webapp.base
20import my_lib.webapp.config
21import my_lib.webapp.event
22import my_lib.webapp.log
23import my_lib.webapp.util
24import werkzeug.serving
26import unit_cooler.actuator.webapi.flow_status
27import unit_cooler.actuator.webapi.valve_status
28import unit_cooler.metrics.webapi.page
29from unit_cooler.metrics import get_metrics_collector
32def create_app(config, event_queue):
33 my_lib.webapp.config.URL_PREFIX = "/unit-cooler"
34 my_lib.webapp.config.init(config["actuator"]["web_server"])
36 # NOTE: アクセスログは無効にする
37 logging.getLogger("werkzeug").setLevel(logging.ERROR)
39 app = flask.Flask("unit-cooler-web")
41 flask_cors.CORS(app)
43 app.config["CONFIG"] = config
44 app.config["CONFIG_FILE_NORMAL"] = "config.yaml" # メトリクス用設定
46 app.json.compat = True
48 app.register_blueprint(my_lib.webapp.log.blueprint, url_prefix=my_lib.webapp.config.URL_PREFIX)
49 app.register_blueprint(my_lib.webapp.event.blueprint, url_prefix=my_lib.webapp.config.URL_PREFIX)
50 app.register_blueprint(my_lib.webapp.util.blueprint, url_prefix=my_lib.webapp.config.URL_PREFIX)
51 app.register_blueprint(
52 unit_cooler.actuator.webapi.valve_status.blueprint, url_prefix=my_lib.webapp.config.URL_PREFIX
53 )
54 app.register_blueprint(
55 unit_cooler.actuator.webapi.flow_status.blueprint, url_prefix=my_lib.webapp.config.URL_PREFIX
56 )
57 app.register_blueprint(
58 unit_cooler.metrics.webapi.page.blueprint, url_prefix=my_lib.webapp.config.URL_PREFIX
59 )
61 my_lib.webapp.config.show_handler_list(app, True)
63 my_lib.webapp.log.init(config)
64 my_lib.webapp.event.start(event_queue)
66 # メトリクスデータベースの初期化
67 metrics_db_path = config["actuator"].get("metrics", {}).get("data", "data/metrics.db")
68 try:
69 metrics_collector = get_metrics_collector(metrics_db_path)
70 logging.info("Metrics database initialized at: %s", metrics_db_path)
71 app.config["METRICS_COLLECTOR"] = metrics_collector
72 except Exception:
73 logging.exception("Failed to initialize metrics database")
75 # app.debug = True
77 return app
80def start(config, event_queue, port):
81 # NOTE: Flask は別のプロセスで実行
82 try:
83 app = create_app(config, event_queue)
84 logging.info("Web app created successfully")
85 except Exception:
86 logging.exception("Failed to create web app")
87 raise
89 server = werkzeug.serving.make_server(
90 "0.0.0.0", # noqa: S104
91 port,
92 app,
93 threaded=True,
94 )
95 thread = threading.Thread(target=server.serve_forever)
97 logging.info("Start web server")
99 thread.start()
101 return {
102 "server": server,
103 "thread": thread,
104 }
107def term(handle):
108 import my_lib.webapp.event
110 logging.warning("Stop web server")
112 my_lib.webapp.event.term()
114 handle["server"].shutdown()
115 handle["server"].server_close()
116 handle["thread"].join()
118 my_lib.webapp.log.term()
121if __name__ == "__main__":
122 # TEST Code
123 import multiprocessing
125 import docopt
126 import my_lib.config
127 import my_lib.logger
128 import my_lib.pretty
130 args = docopt.docopt(__doc__)
132 config_file = args["-c"]
133 port = int(args["-p"])
134 debug_mode = args["-D"]
136 my_lib.logger.init("test", level=logging.DEBUG if debug_mode else logging.INFO)
138 config = my_lib.config.load(config_file)
139 event_queue = multiprocessing.Queue()
141 log_server_handle = start(config, event_queue, port)