王的机器-盘一盘python系列-debug日志

本文介绍了在Python编程中遇到的一个错误,通过使用pdb进行debug来定位问题。在分析数据格式转换函数时,发现代码由于缩进问题导致程序在特定条件满足后提前返回,而非预期执行。通过PyCharm的调试功能进一步确认了问题所在,并成功解决了问题。

王的机器

盘一盘python系列

Matplotlab

使用pdb debug

写了数据格式转换函数

def data_converter( price_data, code, asset ):
    # convert raw data to datafram
    if asset =='FX':
        #         what's FX
        code = str(code[3:] if code[:3]=='USD' else code) + '=X'
        
        columns = [ 'open', 'close', 'low', 'high' ]
        price_dict = price_data[code]['prices']
        index = [ p['formatted_date'] for p in price_dict ] 
        price = [ [p[c] for c in columns] for p in price_dict ]
        data = pd.DataFrame( price,
                            index=pd.Index(index, name='date'),
                            columns=pd.Index(columns, name='OHLC')
                           )
        return data

之后运行:

NVDA = data_converter( stock_daily, 'NVDA', 'EQ' )
NVDA.head(3).append(NVDA.tail(3))

报错:
在这里插入图片描述
之后使用pdb进行debug

在这里插入图片描述

一直没有读懂这是什么意思,难道是运行了这个 if 之后就直接return了?不应该呀。在pycharm中进行了debug,确定是运行了这一句之后就直接跳出来了。对照原来的代码,是缩进的问题。
在这里插入图片描述

【事件触发一致性】研究多智能体网络如何通过分布式事件驱动控制实现有限时间内的共识(Matlab代码实现)内容概要:本文围绕多智能体网络中的事件触发一致性问题,研究如何通过分布式事件驱动控制实现有限时间内的共识,并提供了相应的Matlab代码实现方案。文中探讨了事件触发机制在降低通信负担、提升系统效率方面的优势,重点分析了多智能体系统在有限时间收敛的一致性控制策略,涉及系统模型构建、触发条件设计、稳定性与收敛性分析等核心技术环节。此外,文档还展示了该技术在航空航天、电力系统、机器人协同、无人机编队等多个前沿领域的潜在应用,体现了其跨学科的研究价值和工程实用性。; 适合人群:具备一定控制理论基础和Matlab编程能力的研究生、科研人员及从事自动化、智能系统、多智能体协同控制等相关领域的工程技术人员。; 使用场景及目标:①用于理解和实现多智能体系统在有限时间内达成一致的分布式控制方法;②为事件触发控制、分布式优化、协同控制等课题提供算法设计与仿真验证的技术参考;③支撑科研项目开发、学术论文复现及工程原型系统搭建; 阅读建议:建议结合文中提供的Matlab代码进行实践操作,重点关注事件触发条件的设计逻辑与系统收敛性证明之间的关系,同时可延伸至其他应用场景进行二次开发与性能优化。
【四旋翼无人机】具备螺旋桨倾斜机构的全驱动四旋翼无人机:建模与控制研究(Matlab代码、Simulink仿真实现)内容概要:本文围绕具备螺旋桨倾斜机构的全驱动四旋翼无人机展开,重点研究其动力学建模与控制系统设计。通过Matlab代码与Simulink仿真实现,详细阐述了该类无人机的运动学与动力学模型构建过程,分析了螺旋桨倾斜机构如何提升无人机的全向机动能力与姿态控制性能,并设计相应的控制策略以实现稳定飞行与精确轨迹跟踪。文中涵盖了从系统建模、控制器设计到仿真验证的完整流程,突出了全驱动结构相较于传统四旋翼在欠驱动问题上的优势。; 适合人群:具备一定控制理论基础和Matlab/Simulink使用经验的自动化、航空航天及相关专业的研究生、科研人员或无人机开发工程师。; 使用场景及目标:①学习全驱动四旋翼无人机的动力学建模方法;②掌握基于Matlab/Simulink的无人机控制系统设计与仿真技术;③深入理解螺旋桨倾斜机构对飞行性能的影响及其控制实现;④为相关课题研究或工程开发提供可复现的技术参考与代码支持。; 阅读建议:建议读者结合提供的Matlab代码与Simulink模型,逐步跟进文档中的建模与控制设计步骤,动手实践仿真过程,以加深对全驱动无人机控制原理的理解,并可根据实际需求对模型与控制器进行修改与优化。
太好了!我们现在将系统升级为 **生产级可维护结构**,加入两个关键功能: --- ## ✅ 本次升级目标 1. ✅ 使用 `.env` 文件管理敏感配置(如用户名、密码) 2. ✅ 引入 `python-dotenv` 加载环境变量 3. ✅ 添加完整的日志记录(Log to file + console) 4. ✅ 保持模块化结构清晰 --- # 🔧 第一步:安装依赖 ```bash pip install python-dotenv ``` 更新你的 `requirements.txt`: ```txt requests==2.31.0 python-dotenv==1.0.1 ``` --- # 🗂 第二步:项目结构调整(新增 `.env` 和 `logs/`) ``` superset-kiosk/ ├── .env # 环境变量文件(禁止提交到 Git) ├── main.py ├── config/ │ ├── __init__.py │ └── settings.py # 从 .env 读取配置 ├── auth/ │ ├── __init__.py │ └── superset_auth.py # 日志 + 认证 ├── api/ │ ├── __init__.py │ └── dashboard_api.py # 带错误日志的 API 调用 ├── kiosk/ │ ├── __init__.py │ └── html_generator.py # 日志输出 ├── utils/ │ ├── __init__.py │ └── helpers.py ├── static/ │ └── superset_kiosk.html ├── logs/ │ └── app.log # 自动创建的日志文件 ├── requirements.txt └── README.md ``` --- # 🛠 第三步:创建 `.env` 文件(务必添加到 `.gitignore`) ### `.env` ```env # Superset 连接信息 SUPERSET_BASE_URL=http://localhost:8088 SUPERSET_USERNAME=admin SUPERSET_PASSWORD=admin SUPERSET_PROVIDER=db # 轮播设置 KIOSK_REFRESH_INTERVAL=10 KIOSK_RANDOM_ORDER=false KIOSK_FILTER_TAG=kiosk KIOSK_DASHBOARD_IDS=1,2,3 # 输出路径 KIOSK_OUTPUT_HTML=../static/superset_kiosk.html ``` > ⚠️ 安全提示:`.env` 不应提交到 Git。请在 `.gitignore` 中添加: > > ```gitignore > .env > __pycache__ > *.log > venv/ > ``` --- # 📄 第四步:更新 `config/settings.py` —— 从 `.env` 读取配置 ```python # config/settings.py import os from dotenv import load_dotenv # 加载 .env 文件(根目录) load_dotenv() # === Superset 配置 === SUPerset_BASE_URL = os.getenv("SUPERSET_BASE_URL") USERNAME = os.getenv("SUPERSET_USERNAME") PASSWORD = os.getenv("SUPERSET_PASSWORD") PROVIDER = os.getenv("SUPERSET_PROVIDER", "db") # === Kiosk 轮播配置 === try: REFRESH_INTERVAL_SEC = int(os.getenv("KIOSK_REFRESH_INTERVAL", 10)) except ValueError: REFRESH_INTERVAL_SEC = 10 RANDOM_ORDER = os.getenv("KIOSK_RANDOM_ORDER", "false").lower() == "true" FILTER_BY_TAG = os.getenv("KIOSK_FILTER_TAG", None) if FILTER_BY_TAG == "None" or FILTER_BY_TAG == "": FILTER_BY_TAG = None # 解析 DASHBOARD_IDS 列表,例如 "1,2,3" -> [1, 2, 3] DASHBOARD_IDS_RAW = os.getenv("KIOSK_DASHBOARD_IDS", "").strip() if DASHBOARD_IDS_RAW: try: DASHBOARD_IDS = [int(x.strip()) for x in DASHBOARD_IDS_RAW.split(",") if x.strip()] except: DASHBOARD_IDS = [] else: DASHBOARD_IDS = [] # 空表示获取所有 # 输出文件路径 OUTPUT_HTML = os.getenv("KIOSK_OUTPUT_HTML", "../static/superset_kiosk.html") ``` --- # 📝 第五步:引入日志系统(统一 logger) ### 创建全局日志工具:`utils/logger.py` ```python # utils/logger.py import logging import os from datetime import datetime def setup_logger(): """创建并返回一个格式化的 logger""" logger = logging.getLogger("superset_kiosk") logger.setLevel(logging.DEBUG) # 防止重复添加 handler if logger.handlers: return logger # 创建 logs 目录 log_dir = os.path.join(os.path.dirname(__file__), "..", "logs") os.makedirs(log_dir, exist_ok=True) log_file = os.path.join(log_dir, f"app_{datetime.now().strftime('%Y%m%d')}.log") # 文件处理器:记录 DEBUG 及以上 file_handler = logging.FileHandler(log_file, encoding='utf-8') file_handler.setLevel(logging.DEBUG) file_formatter = logging.Formatter( '%(asctime)s | %(levelname)-8s | %(name)s:%(lineno)d | %(message)s' ) file_handler.setFormatter(file_formatter) # 控制台处理器:只显示 INFO 及以上 console_handler = logging.StreamHandler() console_handler.setLevel(logging.INFO) console_formatter = logging.Formatter('%(levelname)s: %(message)s') console_handler.setFormatter(console_formatter) # 添加处理器 logger.addHandler(file_handler) logger.addHandler(console_handler) return logger # 全局实例 logger = setup_logger() ``` --- # 🔄 更新各模块以使用 logger 和 .env ## 1. `auth/superset_auth.py` —— 添加日志 ```python # auth/superset_auth.py from utils.logger import logger import requests import json from datetime import datetime, timedelta from config.settings import SUPerset_BASE_URL class SupersetAuth: def __init__(self): self.base_url = SUPerset_BASE_URL self.access_token = None self.refresh_token = None self.token_expires_at = None def login(self): logger.info("正在登录 Superset...") url = f"{self.base_url}/api/v1/security/login" payload = { "username": self.username, "password": self.password, "provider": self.provider } headers = {"Content-Type": "application/json"} try: response = requests.post(url, data=json.dumps(payload), headers=headers) response.raise_for_status() data = response.json() self.access_token = data["access_token"] self.refresh_token = data.get("refresh_token") self.token_expires_at = datetime.now() + timedelta(minutes=29) logger.info("✅ 登录成功") return self.access_token except Exception as e: logger.error(f"❌ 登录失败: {e}") raise def is_token_expired(self): return datetime.now() >= self.token_expires_at def refresh_token(self): if not self.refresh_token: logger.warning("⚠️ 无 refresh_token,重新登录...") return self.login() url = f"{self.base_url}/api/v1/security/refresh" headers = {"Authorization": f"Bearer {self.refresh_token}"} try: response = requests.post(url, headers=headers) if response.status_code == 200: data = response.json() self.access_token = data["access_token"] self.token_expires_at = datetime.now() + timedelta(minutes=29) logger.info("🔄 Token 已刷新") return self.access_token else: logger.warning(f"⚠️ Refresh 失败 ({response.status_code}),重新登录...") return self.login() except Exception as e: logger.error(f"❌ Refresh 异常: {e}") return self.login() def get_access_token(self): if not self.access_token or self.is_token_expired(): return self.refresh_token() return self.access_token ``` --- ## 2. `api/dashboard_api.py` —— 添加日志 ```python # api/dashboard_api.py from utils.logger import logger import requests import json from auth.superset_auth import SupersetAuth from config.settings import SUPerset_BASE_URL, FILTER_BY_TAG class DashboardAPI: def __init__(self, auth_client: SupersetAuth): self.auth = auth_client self.base_url = SUPerset_BASE_URL def get_all_dashboards(self): logger.info("正在获取 Dashboard 列表...") url = f"{self.base_url}/api/v1/dashboard/" headers = {"Authorization": f"Bearer {self.auth.get_access_token()}"} query = {"order_column": "id", "order_direction": "asc"} if FILTER_BY_TAG: query["filters"] = [ { "col": "tags", "opr": "rel_m_m", "value": FILTER_BY_TAG } ] params = {"q": json.dumps(query)} try: response = requests.get(url, headers=headers, params=params) response.raise_for_status() result = response.json()["result"] dashboards = [ { "id": d["id"], "title": d["dashboard_title"], "url": f"{self.base_url}{d['url']}" } for d in result if d["id"] and d["url"] ] logger.info(f"📊 成功获取 {len(dashboards)} 个仪表") return dashboards except Exception as e: logger.error(f"❌ 获取 Dashboard 失败: {e}") raise ``` --- ## 3. `kiosk/html_generator.py` —— 添加日志 ```python # kiosk/html_generator.py from utils.logger import logger import os import json from config.settings import OUTPUT_HTML, REFRESH_INTERVAL_SEC, RANDOM_ORDER class HTMLGenerator: @staticmethod def generate(dashboards): logger.info("生成轮播页面中...") if not dashboards: logger.warning("⚠️ 传入的 dashboard 列表为空") return if RANDOM_ORDER: from random import shuffle shuffle(dashboards) logger.debug("🔀 已启用随机顺序") os.makedirs(os.path.dirname(OUTPUT_HTML), exist_ok=True) # (HTML 内容同前,略) with open(OUTPUT_HTML, "w", encoding="utf-8") as f: f.write(html_content) logger.info(f"✅ 轮播页面已生成: {os.path.abspath(OUTPUT_HTML)}") ``` --- ## 4. `main.py` —— 统一异常捕获与日志 ```python # main.py from utils.logger import logger from auth.superset_auth import SupersetAuth from api.dashboard_api import DashboardAPI from kiosk.html_generator import HTMLGenerator from utils.helpers import open_in_browser from config.settings import DASHBOARD_IDS def main(): try: logger.info("🚀 启动 Superset 轮播系统") auth = SupersetAuth() auth.login() dashboard_api = DashboardAPI(auth) all_dashboards = dashboard_api.get_all_dashboards() if DASHBOARD_IDS: filtered = [d for d in all_dashboards if d["id"] in DASHBOARD_IDS] if not filtered: logger.warning("⚠️ 没有匹配 ID 的 Dashboard,请检查 .env 中 KIOSK_DASHBOARD_IDS 设置") return logger.info(f"✅ 已根据 ID 过滤出 {len(filtered)} 个 Dashboard") else: filtered = all_dashboards logger.info(f"✅ 使用全部 {len(filtered)} 个 Dashboard") HTMLGenerator.generate(filtered) open_in_browser("../static/superset_kiosk.html") logger.info(f"🎯 轮播系统启动完成!每 {REFRESH_INTERVAL_SEC} 秒切换一次") except KeyboardInterrupt: logger.info("👋 用户中断程序") except Exception as e: logger.critical(f"💥 系统运行出错: {e}", exc_info=True) if __name__ == "__main__": main() ``` --- # 📂 日志示例输出(logs/app_20250405.log) ``` 2025-04-05 10:30:15,123 | INFO | superset_kiosk:45 | 🚀 启动 Superset 轮播系统 2025-04-05 10:30:15,124 | INFO | superset_auth:25 | 正在登录 Superset... 2025-04-05 10:30:16,200 | INFO | superset_auth:48 | ✅ 登录成功 2025-04-05 10:30:16,201 | INFO | dashboard_api:15 | 正在获取 Dashboard 列表... 2025-04-05 10:30:17,305 | INFO | dashboard_api:40 | 📊 成功获取 3 个仪表 2025-04-05 10:30:17,306 | INFO | html_generator:12 | 生成轮播页面中... 2025-04-05 10:30:17,310 | INFO | html_generator:30 | ✅ 轮播页面已生成: /home/user/superset-kiosk/static/superset_kiosk.html 2025-04-05 10:30:17,311 | INFO | main:35 | 🎯 轮播系统启动完成!每 10 秒切换一次 ``` --- # ✅ 最终效果 你现在拥有了一个: - ✅ 模块化清晰 - ✅ 配置外置(`.env`) - ✅ 支持日志记录(文件 + 控制台) - ✅ 易于部署和维护 - ✅ 可用于生产环境的大屏轮播系统 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值