main.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. """Application entrypoint."""
  2. import logging
  3. import sys
  4. from app_config import load_config
  5. from constants import DEFAULT_BATCH_SIZE
  6. from db import create_connection
  7. from http_value_provider import HttpValueProvider
  8. from logging_config import setup_logging
  9. from modbus_context import ReadonlyHoldingRegisterContext
  10. from modbus_server import run_modbus_server
  11. from point_loader import check_point_exists, load_points, validate_address_overlaps, validate_data_types
  12. from register_store import RegisterStore, initialize_register_store
  13. from value_refresh import ValueRefreshWorker
  14. logger = logging.getLogger(__name__)
  15. def main(config_path: str = "config.yaml") -> int:
  16. config = load_config(config_path)
  17. setup_logging(config.logging.dir, config.logging.retention_days, config.logging.level)
  18. logger.info("正在启动Modbus Server")
  19. logger.info("日志系统初始化完成")
  20. logger.info("配置文件加载完成")
  21. logger.info(
  22. "运行配置: 数据库=%s:%s/%s, Modbus监听=%s:%s, HTTP刷新周期=%s秒, HTTP批量大小=%s",
  23. config.db.host,
  24. config.db.port,
  25. config.db.database,
  26. config.modbus.host,
  27. config.modbus.port,
  28. config.http_provider.interval,
  29. config.http_provider.batch_size,
  30. )
  31. try:
  32. points = _load_and_validate_points(config)
  33. except Exception:
  34. logger.exception("初始化失败")
  35. return 1
  36. store = RegisterStore()
  37. initialize_register_store(points, store)
  38. logger.info("寄存器存储初始化完成,%s", store.describe())
  39. provider = HttpValueProvider(config.http_provider.url, config.http_provider.timeout_seconds)
  40. logger.info("实时值Provider初始化完成,类型=http")
  41. worker = ValueRefreshWorker(points, provider, store, config.http_provider.interval, config.http_provider.batch_size)
  42. logger.info("开始请求初始化实时值")
  43. try:
  44. worker.refresh_once(initial=True)
  45. except Exception:
  46. logger.exception("初始化实时值失败")
  47. return 1
  48. worker.start()
  49. context = ReadonlyHoldingRegisterContext(store)
  50. logger.info("上下文初始化完成(Modbus)")
  51. try:
  52. run_modbus_server(context, config.modbus.host, config.modbus.port)
  53. except Exception:
  54. logger.exception("Modbus TCP服务启动或运行失败")
  55. return 1
  56. return 0
  57. def _load_and_validate_points(config) -> list:
  58. conn = create_connection(config.db)
  59. try:
  60. logger.info("数据库连接成功")
  61. logger.info("开始从modbus_server_point加载全部点位")
  62. points = load_points(conn)
  63. logger.info("点位加载完成,数量=%s", len(points))
  64. if not points:
  65. logger.warning("数据表modbus_server_point没有点位,将启动空Modbus Server")
  66. logger.info("开始校验点位data_type")
  67. type_errors = validate_data_types(points)
  68. if type_errors:
  69. for error in type_errors:
  70. logger.error("点位data_type非法: %s", error)
  71. raise RuntimeError("点位data_type校验失败")
  72. logger.info("开始校验Modbus地址重叠")
  73. overlap_errors = validate_address_overlaps(points)
  74. if overlap_errors:
  75. for error in overlap_errors:
  76. logger.error(error)
  77. raise RuntimeError("Modbus地址重叠校验失败")
  78. logger.info("开始校验pt_point点位是否存在,批量大小=%s", DEFAULT_BATCH_SIZE)
  79. missing_point_ids = check_point_exists(conn, [point.point_id for point in points])
  80. if missing_point_ids:
  81. logger.error("数据表pt_point中缺失以下point_id: %s", missing_point_ids)
  82. raise RuntimeError("pt_point点位存在性校验失败")
  83. return points
  84. finally:
  85. conn.close()
  86. def cli() -> None:
  87. raise SystemExit(main(sys.argv[1] if len(sys.argv) > 1 else "config.yaml"))
  88. if __name__ == "__main__":
  89. cli()