Compare commits

...

3 Commits

Author SHA1 Message Date
c27e250fad update log config 2026-06-13 19:47:27 +08:00
66c0b71b89 update ip 2026-06-13 17:35:46 +08:00
5c7b73b7a4 add log 2026-06-13 16:49:29 +08:00
5 changed files with 130 additions and 68 deletions

View File

@@ -157,8 +157,8 @@ class Decoder_main(threading.Thread):
# self.blink_b, self.blink_a = signal.butter(4, [self.l_freq / (self.device_info['sample_rate'] / 2), self.h_freq / (self.device_info['sample_rate'] / 2)], btype='band') # self.blink_b, self.blink_a = signal.butter(4, [self.l_freq / (self.device_info['sample_rate'] / 2), self.h_freq / (self.device_info['sample_rate'] / 2)], btype='band')
def parameter_init(self,bandPass_low,bandPass_high): def parameter_init(self,bandPass_low,bandPass_high):
self.interval_epoch = [int(i * self.device_info['sample_rate']) for i in self.interval_epoch] # epoch截取信息 self.interval_epoch = [int(i * self.device_info['sample_rate']) for i in self.interval_epoch] # epoch截取信息 ssmvep [50, 550]
self.train_epoch = [int(self.interval_epoch[0]), int(self.interval_epoch[1] + 0.1 * self.device_info['sample_rate'])] # 训练样本epoch self.train_epoch = [int(self.interval_epoch[0]), int(self.interval_epoch[1] + 0.1 * self.device_info['sample_rate'])] # 训练样本epoch ssmevep [50, 575]
self.trainData = [] #训练数据 self.trainData = [] #训练数据
self.trainLabel = [] #训练标签 self.trainLabel = [] #训练标签
self.plotData = [] #报告分析数据 self.plotData = [] #报告分析数据
@@ -287,9 +287,9 @@ class Decoder_main(threading.Thread):
if trainTrial.shape[1] == (self.train_epoch[1] - self.train_epoch[0]) and isinstance( if trainTrial.shape[1] == (self.train_epoch[1] - self.train_epoch[0]) and isinstance(
self.trainLabel, list) \ self.trainLabel, list) \
and self.trainLabel.count(self.currentLabel) < self.single_train: and self.trainLabel.count(self.currentLabel) < self.single_train:
algo_log(f"SSMVEP训练集{np.shape(self.trainData)}", level="DEBUG")
self.trainData.append(trainTrial) self.trainData.append(trainTrial)
self.trainLabel.append(self.currentLabel) self.trainLabel.append(self.currentLabel)
algo_log(f"SSMVEP训练集{np.shape(self.trainData)}", level="DEBUG")
else: else:
time.sleep(0.0001) time.sleep(0.0001)
return return

View File

@@ -38,4 +38,6 @@ python upperHost_stimmock/MI_headless.py
# debug log # debug log
## MI ## MI
Epoch采集完成|收到命令: {'method': 'train'|取出的 Epoch采集完成|收到命令: {'method': 'train'|取出的
收到命令: {'method': 'train'|收到命令: {'method': 'train'|收到命令: {'method': 'predict'|事件检测到

View File

@@ -18,11 +18,22 @@ Upper_Port = 8088
Decoder_Host = 127.0.0.1 Decoder_Host = 127.0.0.1
Decoder_Port = 8099 Decoder_Port = 8099
Serial_port = COM44 Serial_port = COM44
algo_log_path = d:/Program Files/64chn_Decoder/logs
algo_log_level = DEBUG
console_output = 1
save_train_data = 0 save_train_data = 0
zmqServer_host = 10.200.27.140 zmqServer_host = 127.0.0.1
[algo_log]
# ========== 文件日志配置 ==========
file_log_enable = true
file_log_level = DEBUG
log_path = exe
retention_days = 3
# ========== 控制台/黑框配置 ==========
console_enable = true
console_show_window = true
console_log_level = DEBUG
; 64 导设备配置 ; 64 导设备配置
[device_type_1] [device_type_1]

View File

@@ -1,122 +1,172 @@
import os import os
import sys
from pathlib import Path from pathlib import Path
from datetime import datetime, timedelta from datetime import datetime, timedelta
import logging import logging
from logging.handlers import RotatingFileHandler from logging.handlers import RotatingFileHandler
import inspect import inspect
try:
import win32gui
import win32con
WIN32_AVAILABLE = True
except ImportError:
WIN32_AVAILABLE = False
from PubLibrary.InifileHelper import IniRead from PubLibrary.InifileHelper import IniRead
# ===================== 新增:获取 EXE 同级目录 =====================
def get_app_root():
"""获取 runDecoder.exe 所在的真实根目录(兼容 onefile / standalone"""
if getattr(sys, 'frozen', False):
# Nuitka / PyInstaller 打包后走这里
app_path = sys.executable
else:
# 本地源码运行时,取当前脚本目录
app_path = os.path.abspath(__file__)
return os.path.dirname(app_path)
# 全局配置 # 程序根目录exe 同级)
console_output = IniRead('system', 'console_output', '1') APP_ROOT = Path(get_app_root())
log_level = IniRead('system', 'algo_log_level', 'INFO') # 日志文件夹名exe 同级下 logs 目录
DEFAULT_LOG_DIR = APP_ROOT / "logs"
# ===================== 读取 [algo_log] 配置 =====================
# 文件日志
FILE_LOG_ENABLE = IniRead("algo_log", "file_log_enable", "true").lower() == "true"
FILE_LOG_LEVEL = IniRead("algo_log", "file_log_level", "DEBUG").upper()
# 优先级:配置文件 > 默认exe同级logs
CFG_LOG_PATH = IniRead("algo_log", "log_path", "").strip()
if CFG_LOG_PATH == "exe":
LOG_DIR = DEFAULT_LOG_DIR
else:
LOG_DIR = Path(CFG_LOG_PATH)
LOG_RETENTION_DAYS = int(IniRead("algo_log", "retention_days", 3))
# 控制台日志 + 黑框控制
CONSOLE_ENABLE = IniRead("algo_log", "console_enable", "true").lower() == "true"
CONSOLE_SHOW_WINDOW = IniRead("algo_log", "console_show_window", "true").lower() == "true"
CONSOLE_LOG_LEVEL = IniRead("algo_log", "console_log_level", "INFO").upper()
# ===================== 全局常量与缓存 =====================
log_once_cache = set() log_once_cache = set()
logger_cache = {} logger_cache = {}
LOG_RETENTION_DAYS = 3
LOG_PATH_STR = IniRead('system', 'algo_log_path', "d:/Program Files/64chn_Decoder/logs")
LOG_DIR = Path(LOG_PATH_STR)
# 自动补全路径分隔符,创建目录(不存在则新建,避免写日志报错)
LOG_DIR.mkdir(parents=True, exist_ok=True)
# 如需字符串格式路径
LOG_DIR_STR = str(LOG_DIR) + "\\"
LOG_FILE_PREFIX = 'algo_log_' LOG_FILE_PREFIX = 'algo_log_'
# 确保日志目录存在
LOG_DIR.mkdir(parents=True, exist_ok=True)
LOG_DIR_STR = str(LOG_DIR) + "\\"
# 日志格式:时间 - 日志器名 - 级别 - 文件名:行号 - 函数名 - 日志内容 # 日志格式
LOG_FORMAT = '%(asctime)s - %(name)s - %(levelname)s - %(message)s' LOG_FORMAT = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
DATE_FORMAT = '%Y-%m-%d %H:%M:%S' DATE_FORMAT = '%Y-%m-%d %H:%M:%S'
# 日志级别映射
LEVEL_MAP = {
"DEBUG": logging.DEBUG,
"INFO": logging.INFO,
"WARNING": logging.WARNING,
"ERROR": logging.ERROR,
"FATAL": logging.FATAL
}
FILE_LOG_LEVEL_INT = LEVEL_MAP.get(FILE_LOG_LEVEL, logging.INFO)
CONSOLE_LOG_LEVEL_INT = LEVEL_MAP.get(CONSOLE_LOG_LEVEL, logging.INFO)
def clean_old_logs(): # ===================== Windows 控制台黑框显示/隐藏 =====================
"""清理超过指定天数的旧日志文件""" def control_console_window():
if not sys.platform.startswith("win") or not WIN32_AVAILABLE:
return
try: try:
if not os.path.exists(LOG_DIR): hwnd = win32gui.GetForegroundWindow()
if CONSOLE_SHOW_WINDOW:
win32gui.ShowWindow(hwnd, win32con.SW_SHOW)
else:
win32gui.ShowWindow(hwnd, win32con.SW_HIDE)
except Exception:
pass
control_console_window()
# ===================== 清理过期日志 =====================
def clean_old_logs():
try:
if not LOG_DIR.exists():
return return
expire_date = datetime.now() - timedelta(days=LOG_RETENTION_DAYS) expire_date = datetime.now() - timedelta(days=LOG_RETENTION_DAYS)
for filename in os.listdir(LOG_DIR): for filename in os.listdir(LOG_DIR):
if not filename.startswith(LOG_FILE_PREFIX) or not filename.endswith('.log'): if not (filename.startswith(LOG_FILE_PREFIX) and filename.endswith('.log')):
continue continue
date_str = filename[len(LOG_FILE_PREFIX):-4] date_str = filename[len(LOG_FILE_PREFIX):-4]
try: try:
file_date = datetime.strptime(date_str, '%Y-%m-%d') file_date = datetime.strptime(date_str, '%Y-%m-%d')
if file_date < expire_date: if file_date < expire_date:
file_path = os.path.join(LOG_DIR, filename) file_path = LOG_DIR / filename
os.remove(file_path) os.remove(file_path)
print(f"清理过期日志: {file_path}")
except ValueError: except ValueError:
continue continue
except Exception as e: except Exception:
print(f"清理旧日志异常: {str(e)}") pass
# ===================== 初始化日志器 =====================
def init_module_logger(logger_name): def init_module_logger(logger_name):
"""初始化日志器 + 清理旧日志"""
os.makedirs(LOG_DIR, exist_ok=True)
clean_old_logs()
current_date = datetime.now().strftime("%Y-%m-%d")
log_file = os.path.join(LOG_DIR, f"{LOG_FILE_PREFIX}{current_date}.log")
if logger_name in logger_cache: if logger_name in logger_cache:
return logger_cache[logger_name] return logger_cache[logger_name]
clean_old_logs()
logger = logging.getLogger(logger_name) logger = logging.getLogger(logger_name)
logger.setLevel(log_level) logger.setLevel(logging.DEBUG)
if logger.handlers: if logger.handlers:
logger_cache[logger_name] = logger logger_cache[logger_name] = logger
return logger return logger
# 文件输出处理器
file_handler = RotatingFileHandler(
log_file,
maxBytes=10 * 1024 * 1024,
backupCount=10,
encoding='utf-8'
)
formatter = logging.Formatter(LOG_FORMAT, datefmt=DATE_FORMAT) formatter = logging.Formatter(LOG_FORMAT, datefmt=DATE_FORMAT)
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
# 控制台输出 # 文件日志
if console_output: if FILE_LOG_ENABLE:
console_handler = logging.StreamHandler() current_date = datetime.now().strftime("%Y-%m-%d")
log_file = LOG_DIR / f"{LOG_FILE_PREFIX}{current_date}.log"
file_handler = RotatingFileHandler(
log_file,
maxBytes=10 * 1024 * 1024,
backupCount=10,
encoding='utf-8'
)
file_handler.setFormatter(formatter)
file_handler.setLevel(FILE_LOG_LEVEL_INT)
logger.addHandler(file_handler)
# 控制台日志
if CONSOLE_ENABLE:
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setFormatter(formatter) console_handler.setFormatter(formatter)
console_handler.setLevel(CONSOLE_LOG_LEVEL_INT)
logger.addHandler(console_handler) logger.addHandler(console_handler)
logger_cache[logger_name] = logger logger_cache[logger_name] = logger
return logger return logger
# ===================== 对外日志入口函数 =====================
def algo_log(content, level="INFO", record_once=False): def algo_log(content, level="INFO", record_once=False):
""" frame = inspect.currentframe()
日志入口函数 if frame:
自动记录:调用文件名、代码行号、所在函数 frame = frame.f_back.f_back
""" file_name = os.path.basename(frame.f_code.co_filename) if frame else "unknown"
# 回溯栈帧,获取真正调用 algo_log 的代码位置
# f_back(1) -> algo_log 自身f_back(2) -> 业务调用处
frame = inspect.currentframe().f_back.f_back
if not frame:
file_name = "unknown"
else:
file_name = os.path.basename(frame.f_code.co_filename)
logger = init_module_logger(file_name) logger = init_module_logger(file_name)
# 单次日志去重
if record_once: if record_once:
log_key = f"{level.upper()}_{content}" log_key = f"{level.upper()}_{content}"
if log_key in log_once_cache: if log_key in log_once_cache:
return return
log_once_cache.add(log_key) log_once_cache.add(log_key)
# 日志级别分发
level_upper = level.upper() level_upper = level.upper()
log_map = { log_func_map = {
"DEBUG": logger.debug, "DEBUG": logger.debug,
"INFO": logger.info,
"WARNING": logger.warning, "WARNING": logger.warning,
"ERROR": logger.error, "ERROR": logger.error,
"FATAL": logger.fatal, "FATAL": logger.fatal
"INFO": logger.info
} }
log_func = log_map.get(level_upper, logger.info) log_func = log_func_map.get(level_upper, logger.info)
log_func(content) log_func(content)

View File

@@ -28,7 +28,6 @@ echo "输出目录:${OUT_DIR}"
python -m nuitka \ python -m nuitka \
--standalone \ --standalone \
--msvc=latest \ --msvc=latest \
--windows-console-mode=force \
--module-parameter=torch-disable-jit=yes \ --module-parameter=torch-disable-jit=yes \
--enable-plugin=no-qt \ --enable-plugin=no-qt \
--include-package=numpy \ --include-package=numpy \