从配置文件读取参数,每周/天定时执行任务:停止程序,备份数据,重启程序。首次运行程序时,会立即执行一次,之后会按照配置文件定时执行任务。
# -*-coding: GBK -*-
import psutil
import os
import datetime
from apscheduler.schedulers.blocking import BlockingScheduler
import shutil
import time
import threading
# 全局变量:配置文件
glob_config = "config_p.txt"
def txt_to_dict(filename):
with open(filename, 'r', encoding='utf-8') as f:
data_dict = {}
for line in f:
# 忽略空行和注释行
if line.strip() == '' or line.startswith('#'):
continue
key, value = line.strip().split('=')
data_dict[key] = value
return data_dict
def txt_to_dict_l(filename):
with open(filename, 'r', encoding='utf-8') as f:
data_l = []
for line in f:
# 忽略空行和注释行
if line.strip() == '' or line.startswith('#'):
continue
key, value = line.strip().split('=')
if 'exe' in value:
data_l.append(value)
return data_l
def check_pid(process_path_pattern):
# 获取所有运行中的进程
all_processes = psutil.process_iter()
# 存放pid
pid_l = []
# 遍历并打印每个进程的信息
for proc in all_processes:
if proc.name() == 'spe_post.exe':
try:
# 获取进程名称、PID和程序位置
process_name = proc.name()
process_id = proc.pid
process_path = proc.exe()
for ppp in process_path_pattern:
# 判断两个路径是否相等,匹配spe程序
if os.path.normcase(process_path) == os.path.normcase(ppp):
# 获取进程id
pid_l.append(process_id)
# 打印进程信息
# print(f"Process Name: {process_name}")
current_time = datetime.datetime.now()
print(f"{current_time}:已匹配到进程PID: {process_id},进程路径: {process_path}")
# print(f"进程路径: {process_path}")
# else:
# print(f"运行中的该spe程序不在配置文件当中")
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
pass # 忽略无法访问或已终止的进程
# print(pid_l)
return pid_l
def kill_process_by_pid(pid):
try:
# 获取进程对象
process = psutil.Process(pid=pid)
# 终止进程
process.kill()
current_time = datetime.datetime.now()
print(f"{current_time}:进程PID: {pid} 已经被终止运行")
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
pass # 忽略无法访问或已终止的进程
def stop_process():
# 读取配置文件,获取spe程序的绝对路径
process_path_pattern = txt_to_dict_l(glob_config)
# 获取运行中的spe程序pid
pid_l = check_pid(process_path_pattern)
if pid_l:
for pid in pid_l:
# 调用函数结束进程
kill_process_by_pid(pid)
# 等待2秒
time.sleep(2)
else:
current_time = datetime.datetime.now()
print(f"{current_time}:当前无匹配的spe程序运行")
# 等待10秒
time.sleep(10)
current_time = datetime.datetime.now()
print(f"{current_time}:终止spe程序,执行完毕")
# return "停止spe程序,执行完毕"
def move_file():
# 读取配置文件,获取spe程序的绝对路径
process_path_pattern = txt_to_dict_l(glob_config)
# spe程序下需要移动的5个文件
move_file = ['data.ssr', 'move.pos', 'move.raw', 'nav.raw', 'out_los.txt']
# 读取配置文件,加载字典
data = txt_to_dict(glob_config)
# 获取特定键值对的值,指定父目录的名称
parent_dir = data.get('parent_dir', 'null')
# 指定子目录的名称
now = datetime.datetime.now()
child_dir = str(now.strftime('%Y%m%d_%H%M%S'))
# 拼接子目录的完整路径
child_dir_path = os.path.join(parent_dir, child_dir)
# 创建子目录
os.makedirs(child_dir_path, exist_ok=True)
# 获取spe程序的上层目录
for path_dir in process_path_pattern:
# 获取文件的文件夹路径
src_path = os.path.dirname(path_dir)
# 重命名备份的文件夹名称
src_path_parts = src_path.split('/')
src_path_parts1 = src_path_parts[-1]
src_path_parts2 = src_path_parts[-2]
cchild_dir = src_path_parts2 + '_' + src_path_parts1
# if "PROD_V2_N" in src_path:
# print("PROD_V2_N环境的SSR文件")
# cchild_dir = "PROD_V2_N"
# elif "PROD_V2" in src_path:
# print("PROD_V2环境的SSR文件")
# cchild_dir = "PROD_V2"
# elif "PROD" in src_path:
# print("PROD环境的SSR文件")
# cchild_dir = "PROD"
# elif "TEST_V2_P" in src_path:
# print("TEST_V2_P环境的SSR文件")
# cchild_dir = "TEST_V2_P"
# elif "TEST_V2" in src_path:
# print("TEST_V2环境的SSR文件")
# cchild_dir = "TEST_V2"
# elif "TEST" in src_path:
# print("TEST环境的SSR文件")
# cchild_dir = "TEST"
# else:
# print("非PROD和TEST环境的SSR文件")
# cchild_dir = "NN"
# 拼接子子目录的完整路径
cchild_dir_path = os.path.join(child_dir_path, cchild_dir)
# 创建子子目录
os.makedirs(cchild_dir_path, exist_ok=True)
for file in move_file:
# 获取文件的完整路径
src_file_path = os.path.join(src_path, file)
# 获取目标路径
dst_file_path = os.path.join(cchild_dir_path, file)
# 判断文件是否存在
if os.path.exists(src_file_path):
# 移动文件
shutil.move(src_file_path, dst_file_path)
# print(f"移动文件完成:", dst_file_path)
time.sleep(5)
# 等待2秒
time.sleep(2)
current_time = datetime.datetime.now()
print(f"{current_time}:移动文件,执行完毕")
# return "移动文件,执行完毕"
def restart_process():
# 读取配置文件,获取spe程序的绝对路径
process_path_pattern = txt_to_dict_l(glob_config)
# 执行可执行程序
for executable_path in process_path_pattern:
# 判断spe是否存在
if os.path.exists(executable_path):
# 获取spe程序的文件夹路径
exe_path = os.path.dirname(executable_path)
# 先切换到spe程序的所在目录,再执行start启动命令
cmd1 = 'cd' + ' ' + exe_path
cmd2 = 'start spe_post.exe'
os.system('%s && %s' % (cmd1, cmd2))
# subprocess.run([executable_path])
current_time = datetime.datetime.now()
print(f"{current_time}:重启{executable_path}程序,执行完毕")
# 等待5秒
time.sleep(5)
else:
current_time = datetime.datetime.now()
print(f"{current_time}:{executable_path}的程序不存在")
# 等待10秒
time.sleep(2)
current_time = datetime.datetime.now()
print(f"{current_time}:重启所有的spe程序,执行完毕")
def run_threading():
# 创建线程并启动停止spe程序、移动文件、重启spe程序
job_stop_process = threading.Thread(target=stop_process)
job_move_file = threading.Thread(target=move_file)
job_restart_process = threading.Thread(target=restart_process)
# 启动线程,等待停止spe程序执行完成后,再移动文件,最后重启spe程序
job_stop_process.start()
job_stop_process.join()
job_move_file.start()
job_move_file.join()
job_restart_process.start()
job_restart_process.join()
# async def main():
# # 等待程序1执行完成后再执行程序2
# result1 = await stop_process()
# result2 = await move_file()
# print(result1)
# print(result2)
#
# # 运行主程序
# asyncio.run(main())
if __name__ == '__main__':
# 读取配置文件,加载字典
data = txt_to_dict(glob_config)
# 获取运行时间
run_time_daily = data.get('run_time_daily', '08:10')
run_time_weekly = data.get('run_time_weekly', 'mon')
run_time_days = data.get('run_time_days', '7')
# 拆分运行时间为小时和分钟
rt = run_time_daily.strip().split(':')
ht, mt = int(rt[0]), int(rt[1])
days = int(run_time_days)
# current_time = datetime.datetime.now()
# print(f"{current_time}:开始执行任务")
# 创建调度器
scheduler_weekly = BlockingScheduler()
# current_time = datetime.datetime.now()
# print(f"{current_time}:每周{run_time_weekly}的{run_time_daily}定时执行任务")
# 添加定时任务,使用cron表达式,表示每周的(mon,tue,wed,thu,fri,stat,sun)的ht:mt时刻执行一次
# scheduler_weekly.add_job(run_threading, 'cron', week='*', day_of_week=run_time_weekly, hour=ht, minute=mt, second=0)
# 添加定时任务,使用interval表达式,表示每间隔X秒执行一次
# scheduler_weekly.add_job(run_threading, 'interval', seconds=600)
# 添加定时任务,使用interval表达式,表示每间隔X分钟执行一次
# scheduler_weekly.add_job(run_threading, 'interval', minutes=60)
current_time = datetime.datetime.now()
print(f"{current_time}:每{run_time_days}天定时执行一次任务")
# 添加定时任务,使用interval表达式,表示每间隔X天执行一次
scheduler_weekly.add_job(run_threading, 'interval', days=days, next_run_time=datetime.datetime.now())
# 开始调度器
scheduler_weekly.start()
config文件如下:
# 每周执行任务(停止spe程序&备份数据&重启spe程序)-北京时间,小时&分钟,默认08:10 run_time_daily=15:01 # 可选mon,tue,wed,thu,fri,stat,sun分别表示星期一、二、三、四、五、六、日,默认mon run_time_weekly=thu # 每X天执行任务,默认7天,与上面的时间互斥 run_time_day=3 # spe程序的路径,若停止监控该环境,只需将该值#注释或者删除,注意spe所在的上层父目录和上上层父父目录不要重名,否则备份的文件会相互覆盖 origin_path_prod=E:/data/PROD/spe_post.exe origin_path_prod_v2=E:/data/PROD_V2/spe_post.exe origin_path_test_v2_p=E:/data/TEST_V2_P/spe_post.exe #origin_path_test_p=E:/DPI/路测/0925版本SPE/spe_post.exe # 备份文件的路径 parent_dir=E:/data/备份
运行的截图: