Files
ktouchV2/opt/ktouch/src/kscreen_remap_daemon.py37
T
2026-06-12 16:54:32 +08:00

350 lines
12 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
import time
import logging
import signal
import hashlib
import subprocess
from pathlib import Path
from threading import Thread
from time import sleep
# 配置X11环境变量
os.environ['DISPLAY'] = ':0'
# os.environ['XAUTHORITY'] = '/home/pi/.Xauthority' # 根据实际情况调整路径
# 全局变量
processes = {} # 存储进程对象
monitor_thread = None # 监控线程
running = True # 控制循环运行
# 配置日志
def setup_logging():
log_file = 'touchscreen.log'
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler(log_file, encoding='utf-8'),
logging.StreamHandler(sys.stdout)
]
)
return logging.getLogger(__name__)
logger = setup_logging()
# 信号处理函数
def signal_handler(signum, frame):
global running
logger.info(f"Received signal {signum}, shutting down...")
running = False
cleanup()
def cleanup():
"""清理函数,停止所有子进程"""
logger.info("清理子进程")
for proc_name, proc_info in processes.items():
try:
proc = proc_info['process']
if proc and proc.poll() is None: # 进程还在运行
proc.terminate()
try:
proc.wait(timeout=5)
except subprocess.TimeoutExpired:
proc.kill()
logger.info(f"关闭了进程: {proc_name}")
except Exception as e:
logger.error(f"关闭进程失败 {proc_name}: {e}")
processes.clear()
# 1. 日志文件轮转函数
def rotate_logs():
log_files = ['screen_calibration.log', 'touchscreen.log']
max_size = 10 * 1024 * 1024 # 10MB
max_backups = 7
for log_file in log_files:
if os.path.exists(log_file) and os.path.getsize(log_file) > max_size:
# 删除最旧的备份文件
oldest_backup = f"{log_file}.{max_backups}"
if os.path.exists(oldest_backup):
os.remove(oldest_backup)
logger.info(f"清理过早的日志: {oldest_backup}")
# 重命名现有备份文件
for i in range(max_backups-1, 0, -1):
old_name = f"{log_file}.{i}"
new_name = f"{log_file}.{i+1}"
if os.path.exists(old_name):
os.rename(old_name, new_name)
# logger.info(f"Renamed {old_name} to {new_name}")
# 重命名当前日志文件
os.rename(log_file, f"{log_file}.1")
# logger.info(f"Rotated log file: {log_file} -> {log_file}.1")
# 2. 检查并创建所需文件
def initialize_files():
ktouch_dir = Path("/tmp/ktouch")
files_to_create = [
"screen.txt",
"touch.txt",
"touch_need_update.txt",
"usbadd.txt"
]
# 创建目录
ktouch_dir.mkdir(exist_ok=True)
logger.info("创建状态缓存目录 /tmp/ktouch ")
# 创建或初始化文件
for file_name in files_to_create:
file_path = ktouch_dir / file_name
with open(file_path, 'w') as f:
f.write('1')
logger.info(f"初始化缓存文件 {file_path}: to 1")
# 启动单个后台程序
def start_process(proc_name, proc_args):
"""启动单个进程并添加到监控列表"""
# 设置X11环境
env = os.environ.copy()
env['DISPLAY'] = ':0'
try:
proc = subprocess.Popen(proc_args, env=env)
processes[proc_name] = {
'process': proc,
'args': proc_args,
'start_time': time.time(),
'restart_count': 0
}
logger.info(f"拉起子进程 {proc_name} (PID: {proc.pid})")
return True
except Exception as e:
logger.error(f"进程启动失败 {proc_name}: {e}")
return False
# 3. 启动后台程序(添加X11环境支持)
def start_background_processes():
process_configs = {
'screen_ds': ["/opt/ktouch/screen_ds", "/tmp/ktouch/screen.txt"],
'touch_ds': ["/opt/ktouch/touch_ds", "/tmp/ktouch/touchmap.txt", "/tmp/ktouch/touch_need_update.txt"],
'usb_ds': ["/opt/ktouch/usb_ds", "/tmp/ktouch/usbadd.txt"]
}
for proc_name, proc_args in process_configs.items():
start_process(proc_name, proc_args)
# # 设置X11环境
# env = os.environ.copy()
# env['DISPLAY'] = ':0'
# for proc_args in processes:
# try:
# subprocess.Popen(proc_args, env=env)
# logger.info(f"Started process: {' '.join(proc_args)}")
# except Exception as e:
# logger.error(f"Failed to start process {proc_args[0]}: {e}")
# 监控和重启进程
def monitor_processes():
"""监控进程状态,如果进程退出则重启"""
global running
while running:
try:
for proc_name, proc_info in list(processes.items()):
proc = proc_info['process']
returncode = proc.poll()
if returncode is not None: # 进程已退出
logger.warning(f"进程 {proc_name} (PID: {proc.pid}) 已退出,代码 {returncode}")
# 限制重启次数,避免无限重启
proc_info['restart_count'] += 1
if proc_info['restart_count'] > 128: # 最多重启128次
logger.error(f"启动 {proc_name} 进程被结束超过128次,决定不再重启。请检查日志文件。")
del processes[proc_name]
continue
# 等待一段时间再重启
time.sleep(2)
# 重新启动进程
if start_process(proc_name, proc_info['args']):
logger.info(f"重启进程: {proc_name}")
else:
logger.error(f"重启进程失败: {proc_name}")
# 检查是否有进程需要启动(初始启动失败的情况)
expected_processes = ['screen_ds', 'touch_ds', 'usb_ds']
for proc_name in expected_processes:
if proc_name not in processes:
logger.warning(f"发现 {proc_name} 消失, 准备重新启动")
# 这里需要根据进程名重新构建参数
if proc_name == 'screen_ds':
start_process(proc_name, ["/opt/ktouch/screen_ds", "/tmp/ktouch/screen.txt"])
elif proc_name == 'touch_ds':
start_process(proc_name, ["/opt/ktouch/touch_ds", "/tmp/ktouch/touchmap.txt", "/tmp/ktouch/touch_need_update.txt"])
elif proc_name == 'usb_ds':
start_process(proc_name, ["/opt/ktouch/usb_ds", "/tmp/ktouch/usbadd.txt"])
time.sleep(5) # 每2秒检查一次进程状态
except Exception as e:
logger.error(f"进程监控出现问题: {e}")
time.sleep(5)
# 4. 执行touch_remap并重置文件(添加X11环境支持)
def run_touch_remap():
# 设置X11环境
env = os.environ.copy()
env['DISPLAY'] = ':0'
try:
# 执行touch_remap程序
result = subprocess.run(["/opt/ktouch/touch_set", "/tmp/ktouch/touch.txt"], capture_output=True, text=True, env=env)
if result.returncode == 0:
logger.info("执行映射(touch_set)成功")
else:
logger.error(f"执行映射(touch_set)失败,错误信息为 {result.returncode}: {result.stderr}")
except Exception as e:
logger.error(f"无法执行touch_set映射程序!!!!!: {e}")
# 重置文件为0
ktouch_dir = Path("/tmp/ktouch")
files_to_reset = [
"touch_need_update.txt",
"usbadd.txt"
]
for file_name in files_to_reset:
file_path = ktouch_dir / file_name
try:
with open(file_path, 'w') as f:
f.write('0')
# logger.info(f"Reset {file_path} to 0")
except Exception as e:
# logger.error(f"Failed to reset {file_path}: {e}")
# 5. 监控文件变化
def monitor_files():
# 记录screen.txt的初始状态
screen_file = "/tmp/ktouch/screen.txt"
last_screen_content = ""
if os.path.exists(screen_file):
with open(screen_file, 'r') as f:
last_screen_content = f.read().strip()
logger.info("开始主程序监控")
while running:
# 检查是否需要执行touch_remap
need_update = False
# 检查usbadd.txt和touch_need_update.txt
for file_name in ["/tmp/ktouch/usbadd.txt", "/tmp/ktouch/touch_need_update.txt"]:
try:
with open(file_name, 'r') as f:
content = f.read().strip()
if content == '1':
logger.info(f"文件 {file_name} 内容变为1,触发重映射")
need_update = True
except Exception as e:
logger.error(f"读取文件失败 {file_name}: {e}")
# 检查screen.txt是否发生变化
try:
with open(screen_file, 'r') as f:
current_content = f.read().strip()
if current_content != last_screen_content:
logger.info("文件 screen.txt 内容变化,触发重映射")
need_update = True
last_screen_content = current_content
except Exception as e:
logger.error(f"读取文件失败 {screen_file}: {e}")
# 如果需要更新,执行touch_remap
if need_update:
run_touch_remap()
# 等待一段时间再次检查
time.sleep(1)
# 检查X11环境是否可用
def check_x11_environment():
try:
# 尝试运行一个简单的X11命令来检查环境
result = subprocess.run(['xset', '-q'], capture_output=True, text=True, timeout=5)
if result.returncode == 0:
logger.info("X11 environment is available")
return True
else:
logger.warning("X11 environment check failed")
return False
except Exception as e:
logger.error(f"Error checking X11 environment: {e}")
return False
# 主函数
def main():
global monitor_thread, running
try:
# 注册信号处理
signal.signal(signal.SIGTERM, signal_handler)
signal.signal(signal.SIGINT, signal_handler)
logger.info("kscreen_remap_daemon 程序启动")
# 检查X11环境
if not check_x11_environment():
logger.warning("主程序:X11 environment may not be available, 程序将继续运行但无效果")
# 1. 日志轮转
rotate_logs()
logger.info("主程序:处理日志 OK")
# 2. 初始化文件
initialize_files()
logger.info("主程序:初始化和检查文件 OK")
# 3. 启动后台进程
start_background_processes()
logger.info("主程序:启动子进程 OK")
# 启动进程监控线程
monitor_thread = Thread(target=monitor_processes, daemon=True)
monitor_thread.start()
logger.info("主程序:启动进程监控 OK")
sleep(3)
# 4. 首次执行touch_remap
run_touch_remap()
logger.info("主程序:执行一次映射 OK")
sleep(7)
# 5. 开始监控文件变化
logger.info("主程序:开始监控loop")
monitor_files()
except Exception as e:
logger.error(f"Unexpected error in main: {e}")
running = False
cleanup()
sys.exit(1)
finally:
cleanup()
logger.info("Daemon script stopped")
if __name__ == "__main__":
main()