Files
archery/time_sync.py
2026-01-12 11:39:27 +08:00

187 lines
6.3 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
时间同步模块
从4G模块获取时间并同步到系统
"""
import re
import os
from datetime import datetime, timedelta
import config
# from logger_bak import get_logger
from logger_manager import logger_manager
def parse_4g_time(cclk_response, timezone_offset=8):
"""
解析 AT+CCLK? 返回的时间字符串,并转换为本地时间
Args:
cclk_response: AT+CCLK? 的响应字符串
timezone_offset: 时区偏移小时默认8中国时区 UTC+8
Returns:
datetime 对象(已转换为本地时间),如果解析失败返回 None
"""
try:
# 匹配格式: +CCLK: "YY/MM/DD,HH:MM:SS+TZ"
# 时区单位是四分之一小时quarters of an hour
match = re.search(r'\+CCLK:\s*"(\d{2})/(\d{2})/(\d{2}),(\d{2}):(\d{2}):(\d{2})([+-]\d{1,3})?"', cclk_response)
if not match:
return None
yy, mm, dd, hh, MM, ss, tz_str = match.groups()
# 年份处理26 -> 2026
year = 2000 + int(yy)
month = int(mm)
day = int(dd)
hour = int(hh)
minute = int(MM)
second = int(ss)
# 创建 UTC 时间的 datetime 对象
dt_utc = datetime(year, month, day, hour, minute, second)
# 解析时区偏移(单位:四分之一小时)
if tz_str:
try:
# 时区偏移值(四分之一小时)
tz_quarters = int(tz_str)
# 转换为小时除以4
tz_hours = tz_quarters / 4.0
logger = logger_manager.logger
if logger:
logger.info(f"[TIME] 时区偏移: {tz_str} (四分之一小时) = {tz_hours} 小时")
# 转换为本地时间
dt_local = dt_utc + timedelta(hours=tz_hours)
except ValueError:
# 如果时区解析失败,使用默认值
logger = logger_manager.logger
if logger:
logger.warning(f"[TIME] 时区解析失败: {tz_str},使用默认 UTC+{timezone_offset}")
dt_local = dt_utc + timedelta(hours=timezone_offset)
else:
# 没有时区信息,使用默认值
logger = logger_manager.logger
if logger:
logger.info(f"[TIME] 未找到时区信息,使用默认 UTC+{timezone_offset}")
dt_local = dt_utc + timedelta(hours=timezone_offset)
logger = logger_manager.logger
if logger:
logger.info(f"[TIME] UTC时间: {dt_utc.strftime('%Y-%m-%d %H:%M:%S')}")
logger.info(f"[TIME] 本地时间: {dt_local.strftime('%Y-%m-%d %H:%M:%S')}")
return dt_local
except Exception as e:
logger = logger_manager.logger
if logger:
logger.error(f"[TIME] 解析时间失败: {e}, 响应: {cclk_response}")
else:
print(f"[TIME] 解析时间失败: {e}, 响应: {cclk_response}")
return None
def get_time_from_4g(timezone_offset=8):
"""
通过4G模块获取当前时间已转换为本地时间
Args:
timezone_offset: 时区偏移小时默认8中国时区
Returns:
datetime 对象(本地时间),如果获取失败返回 None
"""
try:
# 发送 AT+CCLK? 命令(延迟导入避免循环依赖)
from hardware import hardware_manager
# 检查 at_client 是否已初始化
if hardware_manager.at_client is None:
logger = logger_manager.logger
if logger:
logger.warning("[TIME] ATClient 尚未初始化无法获取4G时间")
else:
print("[TIME] ATClient 尚未初始化无法获取4G时间")
return None
resp = hardware_manager.at_client.send("AT+CCLK?", "OK", 3000)
if not resp or "+CCLK:" not in resp:
logger = logger_manager.logger
if logger:
logger.warning(f"[TIME] 未获取到时间响应: {resp}")
else:
print(f"[TIME] 未获取到时间响应: {resp}")
return None
# 解析并转换时区
dt = parse_4g_time(resp, timezone_offset)
return dt
except Exception as e:
logger = logger_manager.logger
if logger:
logger.error(f"[TIME] 获取4G时间异常: {e}")
else:
print(f"[TIME] 获取4G时间异常: {e}")
return None
def sync_system_time_from_4g(timezone_offset=8):
"""
从4G模块同步时间到系统
Args:
timezone_offset: 时区偏移小时默认8中国时区
Returns:
bool: 是否成功
"""
dt = get_time_from_4g(timezone_offset)
if not dt:
return False
try:
# 转换为系统 date 命令需要的格式
time_str = dt.strftime('%Y-%m-%d %H:%M:%S')
# 设置系统时间
cmd = f'date -s "{time_str}" 2>&1'
result = os.system(cmd)
if result == 0:
logger = logger_manager.logger
if logger:
logger.info(f"[TIME] 系统时间已设置为: {time_str}")
else:
print(f"[TIME] 系统时间已设置为: {time_str}")
# 可选:同步到硬件时钟
try:
os.system('hwclock -w 2>/dev/null')
logger = logger_manager.logger
if logger:
logger.info("[TIME] 已同步到硬件时钟")
except:
pass
return True
else:
logger = logger_manager.logger
if logger:
logger.error(f"[TIME] 设置系统时间失败,退出码: {result}")
else:
print(f"[TIME] 设置系统时间失败,退出码: {result}")
return False
except Exception as e:
logger = logger_manager.logger
if logger:
logger.error(f"[TIME] 同步系统时间异常: {e}")
else:
print(f"[TIME] 同步系统时间异常: {e}")
return False