From 1d29874c07a274c991a1c1a70baf6c4648a0e126 Mon Sep 17 00:00:00 2001 From: zhu-mengmeng <15588200382@163.com> Date: Tue, 16 Dec 2025 13:24:31 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0=E4=B8=B2=E5=8F=A3?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=EF=BC=8C=E6=96=B0=E5=A2=9E=E7=82=89=E5=8F=B7?= =?UTF-8?q?=E8=AD=A6=E5=91=8A=E5=BC=B9=E6=A1=86=EF=BC=8C=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E6=89=AB=E7=A0=81=E5=99=A8=E6=BF=80=E6=B4=BB=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CLAUDE.md | 195 +++++++++++++++++++++++++++++++++++++++++ config/app_config.json | 6 +- db/jtDB.db | Bin 1507328 -> 1507328 bytes widgets/main_window.py | 186 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 383 insertions(+), 4 deletions(-) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..bc87590 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,195 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +**腾智微丝产线包装系统** is a PyQt6-based industrial automation control system for managing and monitoring the packing process of fine wire production lines. It integrates camera monitoring, weighing, barcode scanning, PLC communication, and multiple serial device support. + +**Key Operating Modes:** +- **standalone**: Complete independent system +- **api**: Acts as an interface component for other systems + +## Building and Running + +### Setup Development Environment + +```bash +# Install dependencies +pip install -r requirements.txt + +# Run the application +python main.py +``` + +### Key Configuration +- Main config file: `config/app_config.json` +- Database: SQLite (default at `db/jtDB.db`), also supports PostgreSQL and MySQL +- Logs: `logs/app_YYYY-MM-DD.log` (auto-rotating daily, kept for 30 days) + +### Database Initialization +If the database doesn't exist, it's automatically created on first run. The database schema is initialized via `utils/init_db.py`. + +## Architecture Overview + +This project follows an **MVC architecture** with clear separation of concerns: + +### Layer Structure + +1. **Model (DAO Layer)** - `dao/` directory + - Data Access Objects for database operations + - Supports SQLite, PostgreSQL, MySQL via `utils/sql_utils.py` + - Key DAOs: `inspection_dao.py`, `user_dao.py`, `electricity_dao.py`, `pallet_type_dao.py` + +2. **View Layer** - `ui/` and `widgets/` directories + - **UI Definitions** (`ui/*.py`): Generated or hand-written UI class definitions + - **Controllers** (`widgets/*.py`): Business logic and signal/slot handling for UI components + - Built with **PySide6** (Qt for Python) + - Key components: + - `widgets/main_window.py`: Core application window + - `widgets/login_widget.py`: User authentication + - `widgets/camera_widget.py`: Camera management + - `widgets/inspection_settings_widget.py`: Inspection configuration + +3. **Utilities Layer** - `utils/` directory + - **Configuration**: `config_loader.py` (singleton pattern, loads from JSON) + - **Serial Communication**: `serial_manager.py` (manages multiple serial ports) + - **Modbus Protocol**: `modbus_utils.py`, `modbus_monitor.py` (PLC communication) + - **Hardware**: + - `electricity_monitor.py` (power consumption tracking, singleton) + - `keyboard_listener.py` (hardware input) + - **Image Processing**: `local_image_player.py` (offline camera simulation) + - **Database**: `sql_utils.py` (connection pooling, multi-database support) + +4. **Hardware Integration** - `camera/` directory + - Hikvision camera SDK integration + - Supports both real camera devices and local image sequence playback + +5. **API Layer** - `apis/` directory + - RESTful API endpoints for api mode: `tary_api.py`, `gc_api.py` + +## Key Design Patterns and Conventions + +### Singleton Pattern +Used for global resources and services: +- `ConfigLoader`: Configuration management +- `ElectricityMonitor`: Power monitoring +- `SerialManager`: Serial port management (when instantiated) + +**Access pattern:** +```python +from utils.config_loader import ConfigLoader +config = ConfigLoader.get_instance() +``` + +### Qt Signal/Slot Communication +- Preferred method for inter-component communication +- Asynchronous event handling +- Avoid direct method calls between widgets + +### Configuration Management +- JSON-based configuration (`config/app_config.json`) +- Accessed via `ConfigLoader.get_value(key_path, default_value)` +- Supports nested keys with dot notation: `'database.sources.sqlite.path'` + +### Database Access +- Use `SQLUtils` for connection management +- All DAOs inherit pattern from existing implementations +- Connection pooling is automatically managed +- Always call `SQLUtils.close_all_connections()` on application exit + +### Error Handling +- Global exception handler in `main.py` catches unhandled exceptions +- Log all errors with context (function name, line number) +- Use try/except in main threads, avoid silent failures + +## Important Implementation Details + +### Serial Port Configuration +- Defined in `config/app_config.json` under `serial.*` +- Each device (cz=weighing, xj=scanner, mdz=moisture sensor) has own config +- `serial_manager.py` handles connections and data parsing +- Note: Weighing device (cz) now retrieves data via Modbus register D11, not serial + +### Modbus Communication +- TCP protocol to PLC at `modbus.host:modbus.port` (default: localhost:5020) +- `modbus_monitor.py` provides continuous monitoring of specific registers +- Test/development: Use `modbus_server.py` to simulate PLC + +### Application Mode Detection +- Check `app.mode` from config to determine standalone vs api mode +- API mode disables certain UI elements and uses REST endpoints instead + +### Threading Considerations +- Use `QThread` for long-running operations (serial reading, Modbus polling) +- Always use signals to communicate between threads +- Avoid blocking the GUI thread + +## File Organization + +``` +. +├── main.py # Application entry point +├── config/app_config.json # Main configuration file +├── requirements.txt # Python dependencies +├── widgets/ # View controllers (UI logic) +├── ui/ # View definitions (UI layouts) +├── dao/ # Data access objects +├── utils/ # Utility modules +│ ├── config_loader.py # Configuration singleton +│ ├── serial_manager.py # Serial port handling +│ ├── modbus_utils.py # Modbus communication +│ ├── sql_utils.py # Database utilities +│ └── ... (other utilities) +├── camera/ # Camera SDK integration +├── apis/ # API endpoints (api mode) +├── db/ # Database files +├── logs/ # Application logs +└── inspection/ # Inspection business logic +``` + +## Common Development Tasks + +### Adding a New Widget/Dialog +1. Create UI definition in `ui/` with suffix `_ui.py` +2. Create controller in `widgets/` with corresponding name +3. Use signals for communication with parent widgets +4. Register in config if it needs configuration options + +### Modifying Serial Device Handling +1. Edit device config in `config/app_config.json` +2. Update parser logic in `serial_manager.py` if data format changes +3. Test with physical device or mock serial data + +### Adding Database Tables +1. Modify schema in `utils/init_db.py` +2. Create new DAO class in `dao/` following existing patterns +3. Add migration if needed for existing databases + +### Debugging Modbus Communication +```bash +# Start Modbus test server +python modbus_server.py + +# Check register values and communication +# Monitor output from modbus_monitor.py in logs +``` + +## Dependencies + +See `requirements.txt` for complete list. Key dependencies: +- **PySide6**: GUI framework +- **pymodbus**: Modbus TCP communication +- **pyserial**: Serial port communication +- **pandas**: Data processing +- **opencv-python**: Image processing (for local image playback) +- **pillow**: Image handling +- **pynput**: Keyboard monitoring + +## Notes for Future Development + +1. **Database Migrations**: When modifying schemas, provide backwards compatibility or migration scripts +2. **Configuration Changes**: Maintain JSON schema compatibility with existing configs +3. **Testing**: Tests directory exists at `tests/` but currently minimal +4. **Logging**: Application logs are verbose by design - check logs when debugging issues +5. **Resource Cleanup**: Always stop services (ElectricityMonitor, SerialManager) on application exit diff --git a/config/app_config.json b/config/app_config.json index 97c20b4..539da36 100644 --- a/config/app_config.json +++ b/config/app_config.json @@ -117,10 +117,10 @@ "code": "scanner", "data_bits": 8, "parity": "N", - "port": "9600", - "ser": "COM3", + "port": "115200", + "ser": "COM1", "stop_bits": 1, - "timeout": 1 + "timeout": 0.5 } }, "electricity": { diff --git a/db/jtDB.db b/db/jtDB.db index 407c866ebe0c3a5c1eb74fc42b4aa416c37a1fe2..a2dbd8db8671c318301305e00b0d8129a4ed7f37 100644 GIT binary patch delta 186 zcmZo@h-qkunIO$LaiWYfhknSz0(6%d)4icfXNDq~{O-f&ZNd&5n!1~mYEYd0kT delta 138 zcmZo@h-qkunIO&BKT*b+v43O2UpvNQ&2jeaarTTr%ml>DK+FQftU$~L#Oy%K0mPg@ z%mu{UK+FTgyg炉号为空或未设置!

请先设置炉号后再进行称重") + msg.setStandardButtons(QMessageBox.Ok) + + # 设置样式,使其更显眼 + msg.setStyleSheet(""" + QMessageBox { + background-color: #ffeeee; + border: 3px solid #ff0000; + font-size: 14px; + } + QLabel { + color: #ff0000; + font-size: 16px; + font-weight: bold; + min-width: 250px; + min-height: 80px; + } + QPushButton { + background-color: #ff6666; + color: white; + font-weight: bold; + min-width: 80px; + min-height: 30px; + } + """) + + # 确保对话框显示在所有窗口之上 + msg.setWindowFlags(Qt.WindowStaysOnTopHint) + + # 强制处理事件,确保UI更新 + QApplication.processEvents() + + # 使用定时器在2秒后自动点击确定按钮 + def auto_close(): + try: + # 查找确定按钮并点击 + for button in msg.buttons(): + if msg.buttonRole(button) == QMessageBox.AcceptRole: + button.click() + logging.info(f"已自动点击确定按钮,弹框显示时长: {time.time() - start_time:.2f}秒") + return + # 如果没有找到确定按钮,直接关闭 + msg.done(QMessageBox.Ok) + logging.info(f"已自动关闭弹框,弹框显示时长: {time.time() - start_time:.2f}秒") + except Exception as e: + logging.error(f"自动关闭弹框失败: {str(e)}") + + # 设置自动关闭定时器 + QTimer.singleShot(2000, auto_close) + + # 显示弹框并阻塞直到用户关闭或自动关闭 + logging.info(f"即将显示炉号警告弹框...") + msg.exec_() # 使用exec_而不是show,确保弹框显示 + + # 记录日志 + logging.info(f"炉号警告弹框已关闭,总显示时长: {time.time() - start_time:.2f}秒") + except Exception as e: + logging.error(f"显示炉号警告弹框失败: {str(e)}", exc_info=True) + # 不需要单独的handle_spack_received方法,我们直接从loading_data中获取spack