200 lines
8.0 KiB
Python
Executable File
200 lines
8.0 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
import json
|
|
import os
|
|
import sys
|
|
from datetime import datetime, timedelta
|
|
from pathlib import Path
|
|
from continuous_uploader import ContinuousUploader
|
|
|
|
class BackgroundUploader(ContinuousUploader):
|
|
def __init__(self, base_url: str = "http://192.168.1.83:5001", interval: int = 300):
|
|
super().__init__(base_url, interval)
|
|
self.progress_file = Path("progress_state.json")
|
|
self.state = self.load_progress()
|
|
|
|
def load_progress(self) -> dict:
|
|
"""加载进度状态"""
|
|
if self.progress_file.exists():
|
|
try:
|
|
with open(self.progress_file, 'r', encoding='utf-8') as f:
|
|
state = json.load(f)
|
|
# 转换时间字符串回datetime对象
|
|
if 'last_completed_time' in state and state['last_completed_time']:
|
|
state['last_completed_time'] = datetime.fromisoformat(state['last_completed_time'])
|
|
if 'start_time' in state and state['start_time']:
|
|
state['start_time'] = datetime.fromisoformat(state['start_time'])
|
|
return state
|
|
except Exception as e:
|
|
self.logger.warning(f"加载进度文件失败: {e}")
|
|
|
|
return {
|
|
'mode': None,
|
|
'total_days': 30,
|
|
'completed_times': [],
|
|
'last_completed_time': None,
|
|
'start_time': None,
|
|
'total_operations': 0,
|
|
'completed_operations': 0
|
|
}
|
|
|
|
def save_progress(self):
|
|
"""保存进度状态"""
|
|
try:
|
|
state_to_save = self.state.copy()
|
|
# 转换datetime对象为字符串
|
|
if state_to_save.get('last_completed_time'):
|
|
state_to_save['last_completed_time'] = state_to_save['last_completed_time'].isoformat()
|
|
if state_to_save.get('start_time'):
|
|
state_to_save['start_time'] = state_to_save['start_time'].isoformat()
|
|
|
|
with open(self.progress_file, 'w', encoding='utf-8') as f:
|
|
json.dump(state_to_save, f, indent=2, ensure_ascii=False)
|
|
except Exception as e:
|
|
self.logger.error(f"保存进度失败: {e}")
|
|
|
|
def resume_historical_upload(self, days: int = 30):
|
|
"""恢复历史数据上传"""
|
|
devices = self.get_devices_with_users()
|
|
if not devices:
|
|
self.logger.error("未找到设备数据")
|
|
return
|
|
|
|
end_time = datetime.now()
|
|
start_time = end_time - timedelta(days=days)
|
|
|
|
# 检查是否有进度可以恢复
|
|
resume_from = None
|
|
if self.state.get('mode') == 'historical' and self.state.get('last_completed_time'):
|
|
resume_from = self.state['last_completed_time'] + timedelta(minutes=5)
|
|
self.logger.info(f"🔄 恢复上传,从时间点: {resume_from}")
|
|
else:
|
|
resume_from = start_time
|
|
self.state.update({
|
|
'mode': 'historical',
|
|
'total_days': days,
|
|
'start_time': datetime.now(),
|
|
'completed_times': [],
|
|
'completed_operations': 0
|
|
})
|
|
self.logger.info(f"🚀 开始新的历史数据上传")
|
|
|
|
# 计算需要处理的时间点
|
|
time_points = []
|
|
current_time = resume_from
|
|
while current_time <= end_time:
|
|
time_points.append(current_time)
|
|
current_time += timedelta(minutes=5)
|
|
|
|
total_operations = len(devices) * len(time_points) * 3
|
|
self.state['total_operations'] = len(devices) * ((end_time - start_time).total_seconds() / 300) * 3
|
|
|
|
self.logger.info(f"剩余时间点: {len(time_points)}")
|
|
self.logger.info(f"剩余操作数: {total_operations}")
|
|
|
|
# 开始处理
|
|
for i, time_point in enumerate(time_points):
|
|
if not self.running:
|
|
break
|
|
|
|
self.logger.info(f"📅 处理时间点: {time_point.strftime('%Y-%m-%d %H:%M:%S')} ({i+1}/{len(time_points)})")
|
|
|
|
for device in devices:
|
|
if not self.running:
|
|
break
|
|
|
|
device_sn = device['device_sn']
|
|
user_name = device['user_name']
|
|
|
|
self.upload_data_for_time(device_sn, user_name, time_point)
|
|
self.state['completed_operations'] += 3
|
|
|
|
# 更新进度
|
|
self.state['last_completed_time'] = time_point
|
|
self.state['completed_times'].append(time_point.isoformat())
|
|
|
|
# 每10个时间点保存一次进度
|
|
if (i + 1) % 10 == 0:
|
|
self.save_progress()
|
|
progress = ((i + 1) / len(time_points)) * 100
|
|
self.logger.info(f"📊 进度: {progress:.1f}% - 已保存检查点")
|
|
|
|
if self.running:
|
|
time.sleep(0.1) # 避免过快请求
|
|
|
|
# 完成后清理进度文件
|
|
if self.running: # 正常完成
|
|
self.state['mode'] = 'completed'
|
|
self.save_progress()
|
|
self.logger.info("🎉 历史数据上传完成!")
|
|
else: # 被中断
|
|
self.save_progress()
|
|
self.logger.info("⏸️ 上传已暂停,进度已保存")
|
|
|
|
def main():
|
|
if len(sys.argv) > 1:
|
|
command = sys.argv[1]
|
|
|
|
if command == "start":
|
|
days = int(sys.argv[2]) if len(sys.argv) > 2 else 30
|
|
print(f"🚀 开始/恢复历史数据上传 ({days}天)")
|
|
|
|
uploader = BackgroundUploader()
|
|
uploader.running = True
|
|
|
|
try:
|
|
uploader.resume_historical_upload(days)
|
|
except KeyboardInterrupt:
|
|
uploader.logger.info("收到中断信号,保存进度...")
|
|
uploader.running = False
|
|
uploader.save_progress()
|
|
|
|
elif command == "status":
|
|
uploader = BackgroundUploader()
|
|
state = uploader.state
|
|
|
|
print("📊 当前状态:")
|
|
print(f"模式: {state.get('mode', 'None')}")
|
|
|
|
if state.get('start_time'):
|
|
print(f"开始时间: {state['start_time']}")
|
|
|
|
if state.get('last_completed_time'):
|
|
print(f"最后完成时间: {state['last_completed_time']}")
|
|
|
|
if state.get('total_operations', 0) > 0:
|
|
completed = state.get('completed_operations', 0)
|
|
total = state['total_operations']
|
|
progress = (completed / total) * 100
|
|
print(f"进度: {completed}/{total} ({progress:.1f}%)")
|
|
|
|
elif command == "reset":
|
|
progress_file = Path("progress_state.json")
|
|
if progress_file.exists():
|
|
progress_file.unlink()
|
|
print("🗑️ 进度已重置")
|
|
else:
|
|
print("ℹ️ 没有进度文件需要重置")
|
|
|
|
elif command == "continuous":
|
|
print("🔄 开始持续上传模式")
|
|
uploader = BackgroundUploader()
|
|
uploader.start_continuous()
|
|
|
|
else:
|
|
print("用法:")
|
|
print(" python background_uploader.py start [天数] - 开始/恢复历史数据上传")
|
|
print(" python background_uploader.py status - 查看进度状态")
|
|
print(" python background_uploader.py reset - 重置进度")
|
|
print(" python background_uploader.py continuous - 持续上传模式")
|
|
|
|
else:
|
|
print("📋 后台数据上传工具")
|
|
print("==================")
|
|
print("用法:")
|
|
print(" python background_uploader.py start [天数] - 开始/恢复历史数据上传")
|
|
print(" python background_uploader.py status - 查看进度状态")
|
|
print(" python background_uploader.py reset - 重置进度")
|
|
print(" python background_uploader.py continuous - 持续上传模式")
|
|
|
|
if __name__ == "__main__":
|
|
main() |