187 lines
6.3 KiB
Python
187 lines
6.3 KiB
Python
|
|
#!/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
|
|||
|
|
|