feat: 实现初始应用程序结构,包含检查UI、会话管理、数据库和文件系统工具。
This commit is contained in:
commit
b87d94cebd
15
.claude/settings.local.json
Normal file
15
.claude/settings.local.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"mcp__auggie-mcp__codebase-retrieval",
|
||||
"Skill(collaborating-with-codex)",
|
||||
"Skill(collaborating-with-gemini)",
|
||||
"Bash(find:*)",
|
||||
"Bash(source .venv/bin/activate)",
|
||||
"Bash(pip freeze:*)",
|
||||
"Bash(.venv/bin/pip freeze:*)",
|
||||
"Bash(/Users/meng/work/tengzhi/readFileSystem/.venv/bin/pip freeze:*)",
|
||||
"Bash(/Users/meng/work/tengzhi/readFileSystem/.venv/bin/pip list:*)"
|
||||
]
|
||||
}
|
||||
}
|
||||
274
AGENTS.md
Normal file
274
AGENTS.md
Normal file
@ -0,0 +1,274 @@
|
||||
# AGENTS.md
|
||||
|
||||
This file provides coding guidelines and commands for AI agents working in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
This is a **PySide6-based desktop application** for inspection management and file system browsing. The project includes:
|
||||
- GUI components for inspection workflows (incoming/manual inspection)
|
||||
- Database integration with PostgreSQL
|
||||
- XML parsing for element analysis data
|
||||
- File system browser utility
|
||||
|
||||
**Tech Stack**: Python 3.8+, PySide6 6.7.1, PostgreSQL (psycopg2), pywin32
|
||||
|
||||
---
|
||||
|
||||
## Build & Run Commands
|
||||
|
||||
### Installation
|
||||
```bash
|
||||
# Create virtual environment (recommended)
|
||||
python -m venv venv
|
||||
source venv/bin/activate # Windows: venv\Scripts\activate
|
||||
|
||||
# Install dependencies
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
### Running the Application
|
||||
```bash
|
||||
# Main inspection application
|
||||
python run.py
|
||||
|
||||
# File system browser (legacy)
|
||||
python src/main.py
|
||||
```
|
||||
|
||||
### Testing
|
||||
**Note**: This project currently has no test suite. When adding tests:
|
||||
```bash
|
||||
# Install pytest
|
||||
pip install pytest pytest-qt
|
||||
|
||||
# Run all tests
|
||||
pytest
|
||||
|
||||
# Run single test file
|
||||
pytest tests/test_xml_parser.py
|
||||
|
||||
# Run single test function
|
||||
pytest tests/test_xml_parser.py::test_parse_valid_xml
|
||||
|
||||
# Run with verbose output
|
||||
pytest -v
|
||||
|
||||
# Run with coverage
|
||||
pytest --cov=src --cov-report=html
|
||||
```
|
||||
|
||||
### Linting & Formatting
|
||||
**Note**: No linter/formatter is currently configured. Recommended setup:
|
||||
```bash
|
||||
# Install tools
|
||||
pip install black flake8 mypy
|
||||
|
||||
# Format code
|
||||
black src/ ui/ utils/
|
||||
|
||||
# Lint code
|
||||
flake8 src/ ui/ utils/ --max-line-length=120
|
||||
|
||||
# Type checking
|
||||
mypy src/ ui/ utils/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Code Style Guidelines
|
||||
|
||||
### 1. Imports
|
||||
- **Order**: Standard library → Third-party → Local modules
|
||||
- **Style**: Absolute imports preferred; relative imports allowed within packages
|
||||
- **Grouping**: Separate groups with blank lines
|
||||
|
||||
```python
|
||||
# Standard library
|
||||
import os
|
||||
from pathlib import Path
|
||||
from dataclasses import dataclass
|
||||
from typing import List, Dict, Optional
|
||||
from datetime import datetime
|
||||
|
||||
# Third-party
|
||||
from PySide6.QtWidgets import QMainWindow, QWidget
|
||||
from PySide6.QtCore import Qt
|
||||
import psycopg2
|
||||
|
||||
# Local
|
||||
from src.db_manager import DatabaseManager
|
||||
from ui.inspection_card import InspectionCard
|
||||
```
|
||||
|
||||
### 2. Formatting
|
||||
- **Line length**: ~120 characters (flexible, not enforced)
|
||||
- **Indentation**: 4 spaces (no tabs)
|
||||
- **Quotes**: Double quotes for strings (not strictly enforced)
|
||||
- **Blank lines**: 2 between top-level classes/functions, 1 within classes
|
||||
|
||||
### 3. Naming Conventions
|
||||
- **Classes**: `PascalCase` (e.g., `FileSystemReader`, `InspectionWindow`)
|
||||
- **Functions/Methods**: `snake_case` (e.g., `get_files_and_folders`, `parse_file`)
|
||||
- **Variables**: `snake_case` (e.g., `sample_name`, `data_list`)
|
||||
- **Constants**: `UPPER_SNAKE_CASE` (e.g., `MAX_RETRIES`)
|
||||
- **Private members**: Prefix with `_` (e.g., `_get_file_type`)
|
||||
|
||||
### 4. Type Hints
|
||||
- **Usage**: Encouraged but not mandatory
|
||||
- **Style**: Use `typing` module for complex types
|
||||
- **Return types**: Always specify for public methods
|
||||
|
||||
```python
|
||||
from typing import List, Dict, Optional
|
||||
|
||||
def get_files_and_folders(self) -> List[FileInfo]:
|
||||
"""Returns list of file information objects"""
|
||||
pass
|
||||
|
||||
def parse_file(self, file_path: str) -> tuple[Optional[List[Dict]], str]:
|
||||
"""Returns (data, error_message)"""
|
||||
pass
|
||||
```
|
||||
|
||||
### 5. Docstrings
|
||||
- **Style**: Google-style docstrings (simple format)
|
||||
- **Required for**: Public classes, public methods, complex functions
|
||||
- **Not required for**: Simple getters/setters, obvious utility functions
|
||||
|
||||
```python
|
||||
def get_files_and_folders(self) -> List[FileInfo]:
|
||||
"""
|
||||
Read all files and folders from the root path
|
||||
|
||||
Returns:
|
||||
List[FileInfo]: List of file information objects
|
||||
"""
|
||||
pass
|
||||
```
|
||||
|
||||
### 6. Error Handling
|
||||
- **Philosophy**: Fail gracefully, skip inaccessible resources
|
||||
- **Pattern**: Try-except with specific exceptions, return None/empty on failure
|
||||
- **Logging**: Use `print()` for errors (no logging framework configured)
|
||||
|
||||
```python
|
||||
try:
|
||||
conn = psycopg2.connect(**self.config)
|
||||
return conn
|
||||
except Exception as e:
|
||||
print(f"Database connection error: {e}")
|
||||
return None
|
||||
```
|
||||
|
||||
### 7. Data Classes
|
||||
- Use `@dataclass` for simple data containers
|
||||
- Include type hints for all fields
|
||||
- Add `@property` methods for computed values
|
||||
|
||||
```python
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class FileInfo:
|
||||
name: str
|
||||
path: str
|
||||
size: int
|
||||
file_type: str
|
||||
is_dir: bool
|
||||
modified_time: str
|
||||
|
||||
@property
|
||||
def size_readable(self) -> str:
|
||||
"""Convert size to human-readable format"""
|
||||
# Implementation
|
||||
```
|
||||
|
||||
### 8. PySide6 GUI Patterns
|
||||
- **Initialization**: All UI setup in `__init__`
|
||||
- **Layouts**: Use explicit layout objects (QVBoxLayout, QHBoxLayout)
|
||||
- **Styling**: Inline stylesheets for component-specific styles
|
||||
- **Font**: Microsoft YaHei for Chinese text support
|
||||
|
||||
```python
|
||||
class InspectionWindow(QMainWindow):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setWindowTitle("检验管理系统")
|
||||
|
||||
# Setup layouts
|
||||
central_widget = QWidget()
|
||||
self.setCentralWidget(central_widget)
|
||||
main_layout = QVBoxLayout(central_widget)
|
||||
```
|
||||
|
||||
### 9. Database Patterns
|
||||
- **Connection**: Create connection per operation, close immediately
|
||||
- **Queries**: Use parameterized queries (never string interpolation)
|
||||
- **Schema**: Explicitly specify schema with `self.schema` variable
|
||||
- **Cursors**: Use `RealDictCursor` for dict-like results
|
||||
|
||||
```python
|
||||
def get_connection(self):
|
||||
try:
|
||||
conn = psycopg2.connect(**self.config)
|
||||
return conn
|
||||
except Exception as e:
|
||||
print(f"Database connection error: {e}")
|
||||
return None
|
||||
```
|
||||
|
||||
### 10. File Operations
|
||||
- **Path handling**: Use `pathlib.Path` (not `os.path`)
|
||||
- **Existence checks**: Always check before operations
|
||||
- **Encoding**: UTF-8 default (not explicitly specified)
|
||||
|
||||
```python
|
||||
from pathlib import Path
|
||||
|
||||
def parse_file(self, file_path):
|
||||
if not os.path.exists(file_path):
|
||||
return None, f"文件未找到: {file_path}"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Project-Specific Rules
|
||||
|
||||
1. **Minimal Comments**: Code should be self-explanatory; avoid redundant comments
|
||||
2. **Chinese UI Text**: All user-facing strings in Chinese (e.g., "入检", "手检")
|
||||
3. **No Tests**: Currently no test suite; add tests when implementing new features
|
||||
4. **Entry Points**: `run.py` for inspection app, `src/main.py` for file browser
|
||||
5. **Database Credentials**: Hardcoded in `db_manager.py` (not production-ready)
|
||||
6. **Error Messages**: Return tuple `(data, error_message)` pattern for parsers
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Adding a New UI Component
|
||||
1. Create class in `ui/` directory
|
||||
2. Inherit from appropriate PySide6 widget
|
||||
3. Initialize layout in `__init__`
|
||||
4. Use inline stylesheets for custom styling
|
||||
|
||||
### Adding Database Operations
|
||||
1. Add method to `DatabaseManager` class
|
||||
2. Use `get_connection()` to obtain connection
|
||||
3. Always close connection in `finally` block or after operation
|
||||
4. Return tuple `(success: bool, message: str)` for write operations
|
||||
|
||||
### Parsing New XML Structures
|
||||
1. Add method to `XmlParser` class
|
||||
2. Use `xml.etree.ElementTree` for parsing
|
||||
3. Return tuple `(data: Optional[List[Dict]], error: str)`
|
||||
4. Handle missing elements gracefully with `None` defaults
|
||||
|
||||
---
|
||||
|
||||
## Notes for AI Agents
|
||||
|
||||
- **Preserve existing functionality**: Only modify code directly related to the task
|
||||
- **No unnecessary refactoring**: Keep changes minimal and targeted
|
||||
- **Test manually**: No automated tests; verify changes by running the application
|
||||
- **Database access**: Be cautious with database operations; test queries carefully
|
||||
- **Chinese text**: Maintain Chinese for UI elements, English for code/comments
|
||||
83
CLAUDE.md
Normal file
83
CLAUDE.md
Normal file
@ -0,0 +1,83 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
This is a **File System Browser** - a Python desktop application built with PySide6 that displays files and folders from a specified directory in a table format. The default target directory is `C://OEM` (Windows).
|
||||
|
||||
## Architecture
|
||||
|
||||
The application follows a simple two-layer architecture:
|
||||
|
||||
### Layer 1: File System Reading (`src/file_system.py`)
|
||||
- **FileSystemReader**: Core utility class that handles OS-level file operations
|
||||
- Reads directory contents using `pathlib.Path`
|
||||
- Handles permission errors gracefully (skips inaccessible files)
|
||||
- Returns sorted list of `FileInfo` objects (folders first, then alphabetically)
|
||||
- Extracts file metadata: name, size, type, modification timestamp
|
||||
|
||||
- **FileInfo**: Data class that represents a single file/folder entry
|
||||
- Includes `size_readable` property for human-readable file sizes (B, KB, MB, GB)
|
||||
- Distinguishes between folders and files via `is_dir` flag
|
||||
|
||||
### Layer 2: PySide6 GUI (`src/main.py`)
|
||||
- **FileSystemBrowser**: Main window that integrates file reading with UI display
|
||||
- Initializes UI layout with path label, refresh button, table widget, and status bar
|
||||
- Calls `FileSystemReader.get_files_and_folders()` to populate the table
|
||||
- Renders file metadata in 4-column table: Name, Type, Size, Modified
|
||||
- Visual feedback: folders displayed in blue, alternating row colors
|
||||
- Status bar updates with total item count and current path
|
||||
- `load_files()` method handles UI updates with exception handling
|
||||
|
||||
### Entry Point (`run.py`)
|
||||
- Imports and runs the `main()` function from `src/main.py`
|
||||
- Creates a `FileSystemBrowser` instance with default path `C://OEM`
|
||||
|
||||
## Common Commands
|
||||
|
||||
### Running the Application
|
||||
```bash
|
||||
python run.py
|
||||
```
|
||||
|
||||
### Installing Dependencies
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
### Development Setup
|
||||
```bash
|
||||
python -m venv venv
|
||||
source venv/bin/activate # Windows: venv\Scripts\activate
|
||||
pip install -r requirements.txt
|
||||
python run.py
|
||||
```
|
||||
|
||||
## Key Design Decisions
|
||||
|
||||
1. **Path Handling**: Uses `pathlib.Path` for cross-platform compatibility (Windows, macOS, Linux)
|
||||
2. **Error Handling**: Files/folders that cannot be read due to permissions are silently skipped rather than throwing exceptions
|
||||
3. **Sorting**: Folders are displayed first, followed by files, all sorted alphabetically (case-insensitive)
|
||||
4. **File Type Detection**: Uses file extension (suffix) or labels as "File" if no extension; folders labeled as "Folder"
|
||||
5. **Size Display**: Folders show "—" for size since folder size isn't meaningful; files show converted size with unit
|
||||
6. **UI Responsiveness**: `QApplication.processEvents()` called during `load_files()` to prevent UI freezing
|
||||
|
||||
## Modifying the Target Directory
|
||||
|
||||
To browse a different directory, change the `target_path` parameter in [run.py](run.py):
|
||||
```python
|
||||
window = FileSystemBrowser(target_path="Your/Custom/Path")
|
||||
```
|
||||
|
||||
Or modify the default in [src/main.py:14](src/main.py#L14).
|
||||
|
||||
## Dependencies
|
||||
|
||||
- **PySide6 6.7.1**: Official Python bindings for Qt framework (GUI framework)
|
||||
|
||||
## Notes for Future Development
|
||||
|
||||
- The application currently only reads one directory level (non-recursive)
|
||||
- File I/O is synchronous; large directories may cause brief UI pause during initial load
|
||||
- No persistent state is saved between sessions
|
||||
84
GEMINI.md
Normal file
84
GEMINI.md
Normal file
@ -0,0 +1,84 @@
|
||||
# Gemini Project Context
|
||||
|
||||
## Project Overview
|
||||
|
||||
This project is a Python-based desktop application utilizing the **PySide6** framework (Qt for Python).
|
||||
|
||||
Currently, the active application is an **Inspection Management System (检验管理系统)** designed for industrial PDA (handheld) devices. It allows users to view inspection records and initiate new inspections.
|
||||
|
||||
The project also contains legacy code for a **File System Browser**, which is currently inactive but preserved.
|
||||
|
||||
## Key Technologies
|
||||
|
||||
* **Language**: Python 3
|
||||
* **GUI Framework**: PySide6
|
||||
* **Fonts**: Microsoft YaHei (SimHei/微软雅黑) is the standard font.
|
||||
|
||||
## Architecture
|
||||
|
||||
The project is structured to separate business logic from UI components.
|
||||
|
||||
### Directory Structure
|
||||
|
||||
```text
|
||||
readFileSystem/
|
||||
├── src/
|
||||
│ ├── inspection_app.py # MAIN LOGIC: Inspection app setup & mock data
|
||||
│ ├── main.py # Entry hook (imports inspection_app)
|
||||
│ ├── main_old.py # LEGACY: Old file browser entry point
|
||||
│ └── file_system.py # LEGACY: File system reading logic
|
||||
├── ui/
|
||||
│ ├── inspection_window.py # Main Window (List view + Buttons)
|
||||
│ └── inspection_card.py # Component: Single inspection record card
|
||||
├── run.py # Application Entry Point
|
||||
└── requirements.txt # Dependencies
|
||||
```
|
||||
|
||||
### Components (Inspection App)
|
||||
|
||||
* **`src/inspection_app.py`**:
|
||||
* Sets up the `QApplication`.
|
||||
* Applies global fonts.
|
||||
* Generates mock data for demonstration.
|
||||
* Populates the window with `InspectionCard` widgets.
|
||||
* **`ui/inspection_window.py`**:
|
||||
* **Layout**: Vertical layout with a scrollable area for cards and a fixed bottom button bar.
|
||||
* **Resolution**: Default `480x800` to simulate a phone/PDA screen.
|
||||
* **Buttons**: "Incoming Inspection" (入检) and "Manual Inspection" (手检).
|
||||
* **`ui/inspection_card.py`**:
|
||||
* Displays detailed inspection data (Date, Inspector, Batch, Heat, Material, Spec, Weight) in a grid layout with borders.
|
||||
|
||||
## Building and Running
|
||||
|
||||
### Prerequisites
|
||||
|
||||
* Python 3.10+
|
||||
* Install dependencies:
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
### Execution
|
||||
|
||||
To run the application:
|
||||
|
||||
```bash
|
||||
python run.py
|
||||
```
|
||||
|
||||
*Note: This script imports `src/main.py`, which currently launches the Inspection App.*
|
||||
|
||||
## Development Conventions
|
||||
|
||||
* **UI Design**:
|
||||
* **Style**: Simple, high-contrast (Black/White main), Industrial style.
|
||||
* **Buttons**: Blue background (`#0086fa`), White text.
|
||||
* **Font**: Microsoft YaHei.
|
||||
* **Borders**: Explicit black borders for data cards.
|
||||
* **Language**:
|
||||
* **Code**: All variables, comments, and internal logic must be in **English**.
|
||||
* **User Interface**: All visible text (Labels, Buttons, Alerts) must be in **Chinese**.
|
||||
* **Code Organization**:
|
||||
* New UI components must go into `ui/`.
|
||||
* Application logic resides in `src/`.
|
||||
* Do not modify `run.py` logic unless changing the entry point mechanism.
|
||||
78
README.md
Normal file
78
README.md
Normal file
@ -0,0 +1,78 @@
|
||||
# File System Browser
|
||||
|
||||
A Python application using PySide6 to browse and display files and folders from a specified directory (default: C://OEM).
|
||||
|
||||
## Features
|
||||
|
||||
- Display all files and folders in a table format
|
||||
- Shows file name, type, size, and last modified date
|
||||
- Folders are highlighted in blue
|
||||
- Refresh button to reload the directory
|
||||
- Handles permission errors gracefully
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
readFileSystem/
|
||||
├── src/
|
||||
│ ├── __init__.py
|
||||
│ ├── main.py # Main PySide6 application
|
||||
│ └── file_system.py # File system reading module
|
||||
├── run.py # Entry point
|
||||
├── requirements.txt # Python dependencies
|
||||
└── README.md # This file
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
1. Create a virtual environment (optional but recommended):
|
||||
```bash
|
||||
python -m venv venv
|
||||
source venv/bin/activate # On Windows: venv\Scripts\activate
|
||||
```
|
||||
|
||||
2. Install dependencies:
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Run the application:
|
||||
```bash
|
||||
python run.py
|
||||
```
|
||||
|
||||
### Modifying the Target Directory
|
||||
|
||||
To browse a different directory, edit the `target_path` parameter in `src/main.py`:
|
||||
|
||||
```python
|
||||
window = FileSystemBrowser(target_path="C://Your/Path/Here")
|
||||
```
|
||||
|
||||
## Components
|
||||
|
||||
### FileSystemReader (src/file_system.py)
|
||||
- Reads files and folders from a specified path
|
||||
- Provides file information (name, size, type, modified time)
|
||||
- Handles permission errors gracefully
|
||||
- Converts file sizes to human-readable format (B, KB, MB, GB)
|
||||
|
||||
### FileSystemBrowser (src/main.py)
|
||||
- Main GUI window using PySide6
|
||||
- Displays files in a table widget
|
||||
- Shows file details: name, type, size, modified date
|
||||
- Refresh functionality to reload the directory
|
||||
|
||||
## System Requirements
|
||||
|
||||
- Python 3.8+
|
||||
- PySide6 6.7.1+
|
||||
|
||||
## Notes
|
||||
|
||||
- Folders are displayed in blue text for easy identification
|
||||
- Folders appear first in the list, sorted alphabetically
|
||||
- Files that cannot be read due to permissions are skipped
|
||||
- The application works on Windows, macOS, and Linux
|
||||
44
implementation_plan.md
Normal file
44
implementation_plan.md
Normal file
@ -0,0 +1,44 @@
|
||||
# 实施方案 - 同步数值数据左对齐
|
||||
|
||||
## 背景
|
||||
用户要求同步过来的数值数据需要左对齐。目前的 UI 界面中,数值数据(如化学元素含量、批次信息等)在某些地方可能是居中对齐的。
|
||||
|
||||
## 方案步骤
|
||||
|
||||
### 1. 修改 `ui/incoming_inspection_page.py`
|
||||
- 修改 `create_input` 方法,为生成的 `QLineEdit` 设置 `Qt.AlignLeft | Qt.AlignVCenter`。
|
||||
- 修改 `update_ui_with_data` 中的样式设置(如果有影响)。
|
||||
|
||||
### 2. 修改 `ui/manual_inspection_page.py`
|
||||
- 同样修改 `create_input` 方法,设置左对齐。
|
||||
|
||||
### 3. 修改 `ui/inspection_card.py`
|
||||
- 调整 `add_cell` 方法。目前的 `add_cell` 统一设置了 `Qt.AlignCenter`。
|
||||
- 需要区分标头(Header)和内容(Value)。标头可以保持居中,但内容(Value)应设置为左对齐。
|
||||
|
||||
## 修改细节
|
||||
|
||||
### Incoming/Manual Inspection Page
|
||||
```python
|
||||
def create_input(self, key):
|
||||
inp = QLineEdit()
|
||||
inp.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) # 显式设置左对齐
|
||||
...
|
||||
```
|
||||
|
||||
### Inspection Card
|
||||
```python
|
||||
def add_cell(text, row, col, row_span=1, col_span=1, is_header=False):
|
||||
lbl = QLabel(text)
|
||||
if is_header:
|
||||
lbl.setAlignment(Qt.AlignCenter) # 标头居中
|
||||
else:
|
||||
lbl.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) # 数值左对齐
|
||||
...
|
||||
```
|
||||
|
||||
## 验证计划
|
||||
- 启动应用程序。
|
||||
- 进入“入检”和“手检”界面。
|
||||
- 执行同步操作,观察元素数值是否左对齐。
|
||||
- 观察主页面的卡片数据是否左对齐。
|
||||
1571
logs/doris_mcp_server_all.log
Normal file
1571
logs/doris_mcp_server_all.log
Normal file
File diff suppressed because it is too large
Load Diff
0
logs/doris_mcp_server_audit.log
Normal file
0
logs/doris_mcp_server_audit.log
Normal file
0
logs/doris_mcp_server_critical.log
Normal file
0
logs/doris_mcp_server_critical.log
Normal file
358
logs/doris_mcp_server_debug.log
Normal file
358
logs/doris_mcp_server_debug.log
Normal file
@ -0,0 +1,358 @@
|
||||
2026-01-14 15:39:29.704 [DEBUG] mcp.server.lowlevel.server:162 - Initializing server 'doris-mcp-server'
|
||||
2026-01-14 15:39:29.704 [DEBUG] mcp.server.lowlevel.server:301 - Registering handler for ListResourcesRequest
|
||||
2026-01-14 15:39:29.704 [DEBUG] mcp.server.lowlevel.server:336 - Registering handler for ReadResourceRequest
|
||||
2026-01-14 15:39:29.704 [DEBUG] mcp.server.lowlevel.server:432 - Registering handler for ListToolsRequest
|
||||
2026-01-14 15:39:29.704 [DEBUG] mcp.server.lowlevel.server:512 - Registering handler for CallToolRequest
|
||||
2026-01-14 15:39:29.704 [DEBUG] mcp.server.lowlevel.server:263 - Registering handler for PromptListRequest
|
||||
2026-01-14 15:39:29.705 [DEBUG] mcp.server.lowlevel.server:285 - Registering handler for GetPromptRequest
|
||||
2026-01-14 15:40:47.568 [DEBUG] mcp.server.lowlevel.server:162 - Initializing server 'doris-mcp-server'
|
||||
2026-01-14 15:40:47.569 [DEBUG] mcp.server.lowlevel.server:301 - Registering handler for ListResourcesRequest
|
||||
2026-01-14 15:40:47.569 [DEBUG] mcp.server.lowlevel.server:336 - Registering handler for ReadResourceRequest
|
||||
2026-01-14 15:40:47.569 [DEBUG] mcp.server.lowlevel.server:432 - Registering handler for ListToolsRequest
|
||||
2026-01-14 15:40:47.569 [DEBUG] mcp.server.lowlevel.server:512 - Registering handler for CallToolRequest
|
||||
2026-01-14 15:40:47.569 [DEBUG] mcp.server.lowlevel.server:263 - Registering handler for PromptListRequest
|
||||
2026-01-14 15:40:47.569 [DEBUG] mcp.server.lowlevel.server:285 - Registering handler for GetPromptRequest
|
||||
2026-01-14 16:36:35.837 [DEBUG] mcp.server.lowlevel.server:162 - Initializing server 'doris-mcp-server'
|
||||
2026-01-14 16:36:35.838 [DEBUG] mcp.server.lowlevel.server:301 - Registering handler for ListResourcesRequest
|
||||
2026-01-14 16:36:35.839 [DEBUG] mcp.server.lowlevel.server:336 - Registering handler for ReadResourceRequest
|
||||
2026-01-14 16:36:35.839 [DEBUG] mcp.server.lowlevel.server:432 - Registering handler for ListToolsRequest
|
||||
2026-01-14 16:36:35.839 [DEBUG] mcp.server.lowlevel.server:512 - Registering handler for CallToolRequest
|
||||
2026-01-14 16:36:35.839 [DEBUG] mcp.server.lowlevel.server:263 - Registering handler for PromptListRequest
|
||||
2026-01-14 16:36:35.839 [DEBUG] mcp.server.lowlevel.server:285 - Registering handler for GetPromptRequest
|
||||
2026-01-14 16:53:58.019 [DEBUG] mcp.server.lowlevel.server:162 - Initializing server 'doris-mcp-server'
|
||||
2026-01-14 16:53:58.020 [DEBUG] mcp.server.lowlevel.server:301 - Registering handler for ListResourcesRequest
|
||||
2026-01-14 16:53:58.020 [DEBUG] mcp.server.lowlevel.server:336 - Registering handler for ReadResourceRequest
|
||||
2026-01-14 16:53:58.020 [DEBUG] mcp.server.lowlevel.server:432 - Registering handler for ListToolsRequest
|
||||
2026-01-14 16:53:58.020 [DEBUG] mcp.server.lowlevel.server:512 - Registering handler for CallToolRequest
|
||||
2026-01-14 16:53:58.020 [DEBUG] mcp.server.lowlevel.server:263 - Registering handler for PromptListRequest
|
||||
2026-01-14 16:53:58.020 [DEBUG] mcp.server.lowlevel.server:285 - Registering handler for GetPromptRequest
|
||||
2026-01-14 22:32:53.105 [DEBUG] mcp.server.lowlevel.server:162 - Initializing server 'doris-mcp-server'
|
||||
2026-01-14 22:32:53.106 [DEBUG] mcp.server.lowlevel.server:301 - Registering handler for ListResourcesRequest
|
||||
2026-01-14 22:32:53.106 [DEBUG] mcp.server.lowlevel.server:336 - Registering handler for ReadResourceRequest
|
||||
2026-01-14 22:32:53.106 [DEBUG] mcp.server.lowlevel.server:432 - Registering handler for ListToolsRequest
|
||||
2026-01-14 22:32:53.106 [DEBUG] mcp.server.lowlevel.server:512 - Registering handler for CallToolRequest
|
||||
2026-01-14 22:32:53.106 [DEBUG] mcp.server.lowlevel.server:263 - Registering handler for PromptListRequest
|
||||
2026-01-14 22:32:53.106 [DEBUG] mcp.server.lowlevel.server:285 - Registering handler for GetPromptRequest
|
||||
2026-01-14 22:40:11.990 [DEBUG] mcp.server.lowlevel.server:162 - Initializing server 'doris-mcp-server'
|
||||
2026-01-14 22:40:11.991 [DEBUG] mcp.server.lowlevel.server:301 - Registering handler for ListResourcesRequest
|
||||
2026-01-14 22:40:11.991 [DEBUG] mcp.server.lowlevel.server:336 - Registering handler for ReadResourceRequest
|
||||
2026-01-14 22:40:11.991 [DEBUG] mcp.server.lowlevel.server:432 - Registering handler for ListToolsRequest
|
||||
2026-01-14 22:40:11.991 [DEBUG] mcp.server.lowlevel.server:512 - Registering handler for CallToolRequest
|
||||
2026-01-14 22:40:11.991 [DEBUG] mcp.server.lowlevel.server:263 - Registering handler for PromptListRequest
|
||||
2026-01-14 22:40:11.991 [DEBUG] mcp.server.lowlevel.server:285 - Registering handler for GetPromptRequest
|
||||
2026-01-14 22:59:43.883 [DEBUG] mcp.server.lowlevel.server:162 - Initializing server 'doris-mcp-server'
|
||||
2026-01-14 22:59:43.885 [DEBUG] mcp.server.lowlevel.server:301 - Registering handler for ListResourcesRequest
|
||||
2026-01-14 22:59:43.885 [DEBUG] mcp.server.lowlevel.server:336 - Registering handler for ReadResourceRequest
|
||||
2026-01-14 22:59:43.885 [DEBUG] mcp.server.lowlevel.server:432 - Registering handler for ListToolsRequest
|
||||
2026-01-14 22:59:43.885 [DEBUG] mcp.server.lowlevel.server:512 - Registering handler for CallToolRequest
|
||||
2026-01-14 22:59:43.885 [DEBUG] mcp.server.lowlevel.server:263 - Registering handler for PromptListRequest
|
||||
2026-01-14 22:59:43.885 [DEBUG] mcp.server.lowlevel.server:285 - Registering handler for GetPromptRequest
|
||||
2026-01-16 14:23:45.397 [DEBUG] mcp.server.lowlevel.server:162 - Initializing server 'doris-mcp-server'
|
||||
2026-01-16 14:23:45.398 [DEBUG] mcp.server.lowlevel.server:301 - Registering handler for ListResourcesRequest
|
||||
2026-01-16 14:23:45.398 [DEBUG] mcp.server.lowlevel.server:336 - Registering handler for ReadResourceRequest
|
||||
2026-01-16 14:23:45.398 [DEBUG] mcp.server.lowlevel.server:432 - Registering handler for ListToolsRequest
|
||||
2026-01-16 14:23:45.398 [DEBUG] mcp.server.lowlevel.server:512 - Registering handler for CallToolRequest
|
||||
2026-01-16 14:23:45.398 [DEBUG] mcp.server.lowlevel.server:263 - Registering handler for PromptListRequest
|
||||
2026-01-16 14:23:45.398 [DEBUG] mcp.server.lowlevel.server:285 - Registering handler for GetPromptRequest
|
||||
2026-01-16 14:24:23.058 [DEBUG] mcp.server.lowlevel.server:162 - Initializing server 'doris-mcp-server'
|
||||
2026-01-16 14:24:23.059 [DEBUG] mcp.server.lowlevel.server:301 - Registering handler for ListResourcesRequest
|
||||
2026-01-16 14:24:23.059 [DEBUG] mcp.server.lowlevel.server:336 - Registering handler for ReadResourceRequest
|
||||
2026-01-16 14:24:23.059 [DEBUG] mcp.server.lowlevel.server:432 - Registering handler for ListToolsRequest
|
||||
2026-01-16 14:24:23.059 [DEBUG] mcp.server.lowlevel.server:512 - Registering handler for CallToolRequest
|
||||
2026-01-16 14:24:23.059 [DEBUG] mcp.server.lowlevel.server:263 - Registering handler for PromptListRequest
|
||||
2026-01-16 14:24:23.059 [DEBUG] mcp.server.lowlevel.server:285 - Registering handler for GetPromptRequest
|
||||
2026-01-16 14:25:59.439 [DEBUG] mcp.server.lowlevel.server:162 - Initializing server 'doris-mcp-server'
|
||||
2026-01-16 14:25:59.440 [DEBUG] mcp.server.lowlevel.server:301 - Registering handler for ListResourcesRequest
|
||||
2026-01-16 14:25:59.440 [DEBUG] mcp.server.lowlevel.server:336 - Registering handler for ReadResourceRequest
|
||||
2026-01-16 14:25:59.440 [DEBUG] mcp.server.lowlevel.server:432 - Registering handler for ListToolsRequest
|
||||
2026-01-16 14:25:59.440 [DEBUG] mcp.server.lowlevel.server:512 - Registering handler for CallToolRequest
|
||||
2026-01-16 14:25:59.440 [DEBUG] mcp.server.lowlevel.server:263 - Registering handler for PromptListRequest
|
||||
2026-01-16 14:25:59.440 [DEBUG] mcp.server.lowlevel.server:285 - Registering handler for GetPromptRequest
|
||||
2026-01-16 15:25:16.423 [DEBUG] mcp.server.lowlevel.server:162 - Initializing server 'doris-mcp-server'
|
||||
2026-01-16 15:25:16.425 [DEBUG] mcp.server.lowlevel.server:301 - Registering handler for ListResourcesRequest
|
||||
2026-01-16 15:25:16.425 [DEBUG] mcp.server.lowlevel.server:336 - Registering handler for ReadResourceRequest
|
||||
2026-01-16 15:25:16.425 [DEBUG] mcp.server.lowlevel.server:432 - Registering handler for ListToolsRequest
|
||||
2026-01-16 15:25:16.425 [DEBUG] mcp.server.lowlevel.server:512 - Registering handler for CallToolRequest
|
||||
2026-01-16 15:25:16.425 [DEBUG] mcp.server.lowlevel.server:263 - Registering handler for PromptListRequest
|
||||
2026-01-16 15:25:16.425 [DEBUG] mcp.server.lowlevel.server:285 - Registering handler for GetPromptRequest
|
||||
2026-01-17 09:14:54.443 [DEBUG] mcp.server.lowlevel.server:162 - Initializing server 'doris-mcp-server'
|
||||
2026-01-17 09:14:54.444 [DEBUG] mcp.server.lowlevel.server:301 - Registering handler for ListResourcesRequest
|
||||
2026-01-17 09:14:54.444 [DEBUG] mcp.server.lowlevel.server:336 - Registering handler for ReadResourceRequest
|
||||
2026-01-17 09:14:54.444 [DEBUG] mcp.server.lowlevel.server:432 - Registering handler for ListToolsRequest
|
||||
2026-01-17 09:14:54.444 [DEBUG] mcp.server.lowlevel.server:512 - Registering handler for CallToolRequest
|
||||
2026-01-17 09:14:54.444 [DEBUG] mcp.server.lowlevel.server:263 - Registering handler for PromptListRequest
|
||||
2026-01-17 09:14:54.444 [DEBUG] mcp.server.lowlevel.server:285 - Registering handler for GetPromptRequest
|
||||
2026-01-17 09:14:54.534 [DEBUG] mcp.server.lowlevel.server:668 - Received message: root=InitializedNotification(method='notifications/initialized', params=None, jsonrpc='2.0')
|
||||
2026-01-17 09:14:54.535 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x11d323380>
|
||||
2026-01-17 09:14:54.535 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type ListToolsRequest
|
||||
2026-01-17 09:14:54.536 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-17 09:14:54.539 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x11bb98290>
|
||||
2026-01-17 09:14:54.539 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type ListPromptsRequest
|
||||
2026-01-17 09:14:54.541 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-17 09:14:54.545 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x1284b7c20>
|
||||
2026-01-17 09:14:54.545 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-17 09:14:54.546 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x107c603e0>
|
||||
2026-01-17 09:14:54.547 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-17 09:14:54.548 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-17 09:14:54.548 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x11d323380>
|
||||
2026-01-17 09:14:54.549 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-17 09:14:54.550 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x1285ce750>
|
||||
2026-01-17 09:14:54.550 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-17 09:14:54.551 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-17 09:14:54.551 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x1288533e0>
|
||||
2026-01-17 09:14:54.551 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-17 09:14:54.554 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x11d323380>
|
||||
2026-01-17 09:14:54.554 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-17 09:14:54.555 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-17 09:14:54.556 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-17 09:14:54.557 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-17 09:14:54.662 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-17 09:16:18.660 [DEBUG] mcp.server.lowlevel.server:162 - Initializing server 'doris-mcp-server'
|
||||
2026-01-17 09:16:18.662 [DEBUG] mcp.server.lowlevel.server:301 - Registering handler for ListResourcesRequest
|
||||
2026-01-17 09:16:18.662 [DEBUG] mcp.server.lowlevel.server:336 - Registering handler for ReadResourceRequest
|
||||
2026-01-17 09:16:18.662 [DEBUG] mcp.server.lowlevel.server:432 - Registering handler for ListToolsRequest
|
||||
2026-01-17 09:16:18.662 [DEBUG] mcp.server.lowlevel.server:512 - Registering handler for CallToolRequest
|
||||
2026-01-17 09:16:18.662 [DEBUG] mcp.server.lowlevel.server:263 - Registering handler for PromptListRequest
|
||||
2026-01-17 09:16:18.662 [DEBUG] mcp.server.lowlevel.server:285 - Registering handler for GetPromptRequest
|
||||
2026-01-17 09:16:18.726 [DEBUG] mcp.server.lowlevel.server:668 - Received message: root=InitializedNotification(method='notifications/initialized', params=None, jsonrpc='2.0')
|
||||
2026-01-17 09:16:18.726 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x11fb13530>
|
||||
2026-01-17 09:16:18.727 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type ListToolsRequest
|
||||
2026-01-17 09:16:18.727 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-17 09:16:18.731 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x11bb4a690>
|
||||
2026-01-17 09:16:18.732 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type ListPromptsRequest
|
||||
2026-01-17 09:16:18.732 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-17 09:16:18.740 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x1290d92e0>
|
||||
2026-01-17 09:16:18.740 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-17 09:16:18.740 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x11db62990>
|
||||
2026-01-17 09:16:18.741 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-17 09:16:18.742 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-17 09:16:18.744 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x1284229f0>
|
||||
2026-01-17 09:16:18.745 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-17 09:16:18.752 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x11bb49b50>
|
||||
2026-01-17 09:16:18.752 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-17 09:16:18.752 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-17 09:16:18.753 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x129093e90>
|
||||
2026-01-17 09:16:18.754 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-17 09:16:18.755 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x11daf2e10>
|
||||
2026-01-17 09:16:18.755 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-17 09:16:18.755 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-17 09:16:18.756 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-17 09:16:18.758 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-17 09:16:18.830 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-17 09:17:25.154 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x1289108f0>
|
||||
2026-01-17 09:17:25.160 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-17 09:17:25.162 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-17 09:17:25.168 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x12914f4a0>
|
||||
2026-01-17 09:17:25.169 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-17 09:17:25.175 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x129180d10>
|
||||
2026-01-17 09:17:25.176 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-17 09:17:25.177 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-17 09:17:25.179 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x12899c4d0>
|
||||
2026-01-17 09:17:25.179 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-17 09:17:25.189 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x1291bb800>
|
||||
2026-01-17 09:17:25.193 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-17 09:17:25.195 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-17 09:17:25.221 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x128c041a0>
|
||||
2026-01-17 09:17:25.226 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-17 09:17:25.233 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-17 09:17:25.246 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-17 09:17:25.332 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-17 09:19:25.983 [DEBUG] mcp.server.lowlevel.server:162 - Initializing server 'doris-mcp-server'
|
||||
2026-01-17 09:19:25.984 [DEBUG] mcp.server.lowlevel.server:301 - Registering handler for ListResourcesRequest
|
||||
2026-01-17 09:19:25.984 [DEBUG] mcp.server.lowlevel.server:336 - Registering handler for ReadResourceRequest
|
||||
2026-01-17 09:19:25.984 [DEBUG] mcp.server.lowlevel.server:432 - Registering handler for ListToolsRequest
|
||||
2026-01-17 09:19:25.984 [DEBUG] mcp.server.lowlevel.server:512 - Registering handler for CallToolRequest
|
||||
2026-01-17 09:19:25.984 [DEBUG] mcp.server.lowlevel.server:263 - Registering handler for PromptListRequest
|
||||
2026-01-17 09:19:25.984 [DEBUG] mcp.server.lowlevel.server:285 - Registering handler for GetPromptRequest
|
||||
2026-01-17 09:19:26.065 [DEBUG] mcp.server.lowlevel.server:668 - Received message: root=InitializedNotification(method='notifications/initialized', params=None, jsonrpc='2.0')
|
||||
2026-01-17 09:19:26.066 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x107490d70>
|
||||
2026-01-17 09:19:26.066 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type ListToolsRequest
|
||||
2026-01-17 09:19:26.067 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-17 09:19:26.073 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x10a880560>
|
||||
2026-01-17 09:19:26.074 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type ListPromptsRequest
|
||||
2026-01-17 09:19:26.074 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-17 09:19:26.077 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x1074912e0>
|
||||
2026-01-17 09:19:26.077 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-17 09:19:26.078 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x10a9e7770>
|
||||
2026-01-17 09:19:26.078 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-17 09:19:26.079 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-17 09:19:26.079 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x107490d70>
|
||||
2026-01-17 09:19:26.080 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-17 09:19:26.083 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x10aa4f890>
|
||||
2026-01-17 09:19:26.083 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-17 09:19:26.084 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-17 09:19:26.085 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x10a0ff860>
|
||||
2026-01-17 09:19:26.085 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-17 09:19:26.086 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x10aa4fa70>
|
||||
2026-01-17 09:19:26.086 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-17 09:19:26.087 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-17 09:19:26.088 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-17 09:19:26.089 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-17 09:19:26.166 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-17 15:18:06.087 [DEBUG] mcp.server.lowlevel.server:162 - Initializing server 'doris-mcp-server'
|
||||
2026-01-17 15:18:06.088 [DEBUG] mcp.server.lowlevel.server:301 - Registering handler for ListResourcesRequest
|
||||
2026-01-17 15:18:06.088 [DEBUG] mcp.server.lowlevel.server:336 - Registering handler for ReadResourceRequest
|
||||
2026-01-17 15:18:06.088 [DEBUG] mcp.server.lowlevel.server:432 - Registering handler for ListToolsRequest
|
||||
2026-01-17 15:18:06.088 [DEBUG] mcp.server.lowlevel.server:512 - Registering handler for CallToolRequest
|
||||
2026-01-17 15:18:06.088 [DEBUG] mcp.server.lowlevel.server:263 - Registering handler for PromptListRequest
|
||||
2026-01-17 15:18:06.088 [DEBUG] mcp.server.lowlevel.server:285 - Registering handler for GetPromptRequest
|
||||
2026-01-17 15:18:06.139 [DEBUG] mcp.server.lowlevel.server:668 - Received message: root=InitializedNotification(method='notifications/initialized', params=None, jsonrpc='2.0')
|
||||
2026-01-17 15:18:06.140 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x121433410>
|
||||
2026-01-17 15:18:06.141 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type ListToolsRequest
|
||||
2026-01-17 15:18:06.141 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-17 15:18:06.145 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x121e4ec60>
|
||||
2026-01-17 15:18:06.145 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type ListPromptsRequest
|
||||
2026-01-17 15:18:06.146 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-17 15:18:06.150 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x1219e3020>
|
||||
2026-01-17 15:18:06.151 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-17 15:18:06.154 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x1155ada30>
|
||||
2026-01-17 15:18:06.154 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-17 15:18:06.156 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-17 15:18:06.158 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x121e1c980>
|
||||
2026-01-17 15:18:06.158 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-17 15:18:06.159 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x121e4f0e0>
|
||||
2026-01-17 15:18:06.160 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-17 15:18:06.163 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x121e4f680>
|
||||
2026-01-17 15:18:06.163 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-17 15:18:06.163 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-17 15:18:06.165 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x121e4f4d0>
|
||||
2026-01-17 15:18:06.165 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-17 15:18:06.167 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-17 15:18:06.168 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-17 15:18:06.170 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-17 15:18:06.346 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-18 01:04:06.105 [DEBUG] mcp.server.lowlevel.server:162 - Initializing server 'doris-mcp-server'
|
||||
2026-01-18 01:04:06.106 [DEBUG] mcp.server.lowlevel.server:301 - Registering handler for ListResourcesRequest
|
||||
2026-01-18 01:04:06.106 [DEBUG] mcp.server.lowlevel.server:336 - Registering handler for ReadResourceRequest
|
||||
2026-01-18 01:04:06.106 [DEBUG] mcp.server.lowlevel.server:432 - Registering handler for ListToolsRequest
|
||||
2026-01-18 01:04:06.106 [DEBUG] mcp.server.lowlevel.server:512 - Registering handler for CallToolRequest
|
||||
2026-01-18 01:04:06.106 [DEBUG] mcp.server.lowlevel.server:263 - Registering handler for PromptListRequest
|
||||
2026-01-18 01:04:06.106 [DEBUG] mcp.server.lowlevel.server:285 - Registering handler for GetPromptRequest
|
||||
2026-01-18 01:04:06.163 [DEBUG] mcp.server.lowlevel.server:668 - Received message: root=InitializedNotification(method='notifications/initialized', params=None, jsonrpc='2.0')
|
||||
2026-01-18 01:04:06.164 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x10f428890>
|
||||
2026-01-18 01:04:06.165 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type ListToolsRequest
|
||||
2026-01-18 01:04:06.165 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-18 01:04:06.167 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x10f08a990>
|
||||
2026-01-18 01:04:06.168 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type ListPromptsRequest
|
||||
2026-01-18 01:04:06.170 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-18 01:04:06.175 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x10ec2fc20>
|
||||
2026-01-18 01:04:06.175 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-18 01:04:06.179 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x10eb73b30>
|
||||
2026-01-18 01:04:06.179 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-18 01:04:06.182 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-18 01:04:06.183 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x10ea9b320>
|
||||
2026-01-18 01:04:06.183 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-18 01:04:06.186 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x10f54f920>
|
||||
2026-01-18 01:04:06.186 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-18 01:04:06.187 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x10f4d9220>
|
||||
2026-01-18 01:04:06.187 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-18 01:04:06.188 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-18 01:04:06.189 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x10f54f290>
|
||||
2026-01-18 01:04:06.190 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-18 01:04:06.190 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-18 01:04:06.191 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-18 01:04:06.191 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-18 01:04:06.231 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-18 14:49:46.554 [DEBUG] mcp.server.lowlevel.server:162 - Initializing server 'doris-mcp-server'
|
||||
2026-01-18 14:49:46.555 [DEBUG] mcp.server.lowlevel.server:301 - Registering handler for ListResourcesRequest
|
||||
2026-01-18 14:49:46.555 [DEBUG] mcp.server.lowlevel.server:336 - Registering handler for ReadResourceRequest
|
||||
2026-01-18 14:49:46.555 [DEBUG] mcp.server.lowlevel.server:432 - Registering handler for ListToolsRequest
|
||||
2026-01-18 14:49:46.555 [DEBUG] mcp.server.lowlevel.server:512 - Registering handler for CallToolRequest
|
||||
2026-01-18 14:49:46.555 [DEBUG] mcp.server.lowlevel.server:263 - Registering handler for PromptListRequest
|
||||
2026-01-18 14:49:46.555 [DEBUG] mcp.server.lowlevel.server:285 - Registering handler for GetPromptRequest
|
||||
2026-01-18 14:49:46.601 [DEBUG] mcp.server.lowlevel.server:668 - Received message: root=InitializedNotification(method='notifications/initialized', params=None, jsonrpc='2.0')
|
||||
2026-01-18 14:49:46.602 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x1158ca900>
|
||||
2026-01-18 14:49:46.602 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type ListToolsRequest
|
||||
2026-01-18 14:49:46.603 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-18 14:49:46.607 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x116ff17f0>
|
||||
2026-01-18 14:49:46.608 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type ListPromptsRequest
|
||||
2026-01-18 14:49:46.609 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-18 14:49:46.612 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x116f5e3c0>
|
||||
2026-01-18 14:49:46.612 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-18 14:49:46.616 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x112c31430>
|
||||
2026-01-18 14:49:46.616 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-18 14:49:46.618 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-18 14:49:46.618 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x116ff08c0>
|
||||
2026-01-18 14:49:46.618 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-18 14:49:46.619 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x11703f3e0>
|
||||
2026-01-18 14:49:46.619 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-18 14:49:46.621 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x107c0e090>
|
||||
2026-01-18 14:49:46.621 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-18 14:49:46.621 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-18 14:49:46.624 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x11703f6e0>
|
||||
2026-01-18 14:49:46.625 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-18 14:49:46.626 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-18 14:49:46.627 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-18 14:49:46.627 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-18 14:49:46.699 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-18 22:39:37.745 [DEBUG] mcp.server.lowlevel.server:162 - Initializing server 'doris-mcp-server'
|
||||
2026-01-18 22:39:37.746 [DEBUG] mcp.server.lowlevel.server:301 - Registering handler for ListResourcesRequest
|
||||
2026-01-18 22:39:37.746 [DEBUG] mcp.server.lowlevel.server:336 - Registering handler for ReadResourceRequest
|
||||
2026-01-18 22:39:37.746 [DEBUG] mcp.server.lowlevel.server:432 - Registering handler for ListToolsRequest
|
||||
2026-01-18 22:39:37.746 [DEBUG] mcp.server.lowlevel.server:512 - Registering handler for CallToolRequest
|
||||
2026-01-18 22:39:37.746 [DEBUG] mcp.server.lowlevel.server:263 - Registering handler for PromptListRequest
|
||||
2026-01-18 22:39:37.746 [DEBUG] mcp.server.lowlevel.server:285 - Registering handler for GetPromptRequest
|
||||
2026-01-18 22:39:37.833 [DEBUG] mcp.server.lowlevel.server:668 - Received message: root=InitializedNotification(method='notifications/initialized', params=None, jsonrpc='2.0')
|
||||
2026-01-18 22:39:37.834 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x11aed8650>
|
||||
2026-01-18 22:39:37.834 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type ListToolsRequest
|
||||
2026-01-18 22:39:37.835 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-18 22:39:37.837 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x10649af90>
|
||||
2026-01-18 22:39:37.838 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type ListPromptsRequest
|
||||
2026-01-18 22:39:37.838 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-18 22:39:37.842 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x11a8cbe90>
|
||||
2026-01-18 22:39:37.842 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-18 22:39:37.843 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x119f8df10>
|
||||
2026-01-18 22:39:37.843 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-18 22:39:37.844 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-18 22:39:37.844 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x11af1cad0>
|
||||
2026-01-18 22:39:37.845 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-18 22:39:37.845 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x11af4f320>
|
||||
2026-01-18 22:39:37.845 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-18 22:39:37.846 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x11a431d00>
|
||||
2026-01-18 22:39:37.847 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-18 22:39:37.847 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-18 22:39:37.848 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x11af4f5c0>
|
||||
2026-01-18 22:39:37.848 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-18 22:39:37.848 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-18 22:39:37.849 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-18 22:39:37.850 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-18 22:39:38.208 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-19 22:31:25.114 [DEBUG] mcp.server.lowlevel.server:162 - Initializing server 'doris-mcp-server'
|
||||
2026-01-19 22:31:25.115 [DEBUG] mcp.server.lowlevel.server:301 - Registering handler for ListResourcesRequest
|
||||
2026-01-19 22:31:25.115 [DEBUG] mcp.server.lowlevel.server:336 - Registering handler for ReadResourceRequest
|
||||
2026-01-19 22:31:25.116 [DEBUG] mcp.server.lowlevel.server:432 - Registering handler for ListToolsRequest
|
||||
2026-01-19 22:31:25.116 [DEBUG] mcp.server.lowlevel.server:512 - Registering handler for CallToolRequest
|
||||
2026-01-19 22:31:25.116 [DEBUG] mcp.server.lowlevel.server:263 - Registering handler for PromptListRequest
|
||||
2026-01-19 22:31:25.116 [DEBUG] mcp.server.lowlevel.server:285 - Registering handler for GetPromptRequest
|
||||
2026-01-20 23:23:31.865 [DEBUG] mcp.server.lowlevel.server:162 - Initializing server 'doris-mcp-server'
|
||||
2026-01-20 23:23:31.866 [DEBUG] mcp.server.lowlevel.server:301 - Registering handler for ListResourcesRequest
|
||||
2026-01-20 23:23:31.866 [DEBUG] mcp.server.lowlevel.server:336 - Registering handler for ReadResourceRequest
|
||||
2026-01-20 23:23:31.866 [DEBUG] mcp.server.lowlevel.server:432 - Registering handler for ListToolsRequest
|
||||
2026-01-20 23:23:31.866 [DEBUG] mcp.server.lowlevel.server:512 - Registering handler for CallToolRequest
|
||||
2026-01-20 23:23:31.866 [DEBUG] mcp.server.lowlevel.server:263 - Registering handler for PromptListRequest
|
||||
2026-01-20 23:23:31.866 [DEBUG] mcp.server.lowlevel.server:285 - Registering handler for GetPromptRequest
|
||||
2026-01-20 23:23:31.935 [DEBUG] mcp.server.lowlevel.server:668 - Received message: root=InitializedNotification(method='notifications/initialized', params=None, jsonrpc='2.0')
|
||||
2026-01-20 23:23:31.936 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x13079e3f0>
|
||||
2026-01-20 23:23:31.936 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type ListToolsRequest
|
||||
2026-01-20 23:23:31.937 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-20 23:23:31.940 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x1051ee330>
|
||||
2026-01-20 23:23:31.941 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type ListPromptsRequest
|
||||
2026-01-20 23:23:31.941 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-20 23:23:31.945 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x13076cb60>
|
||||
2026-01-20 23:23:31.946 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-20 23:23:31.948 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x1338053a0>
|
||||
2026-01-20 23:23:31.949 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-20 23:23:31.951 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-20 23:23:31.954 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x130949430>
|
||||
2026-01-20 23:23:31.954 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-20 23:23:31.956 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x133853170>
|
||||
2026-01-20 23:23:31.957 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-20 23:23:31.959 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x104c7bbf0>
|
||||
2026-01-20 23:23:31.959 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-20 23:23:31.959 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-20 23:23:31.962 [DEBUG] mcp.server.lowlevel.server:668 - Received message: <mcp.shared.session.RequestResponder object at 0x1338535f0>
|
||||
2026-01-20 23:23:31.963 [DEBUG] mcp.server.lowlevel.server:716 - Dispatching request of type GetPromptRequest
|
||||
2026-01-20 23:23:31.964 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-20 23:23:31.966 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-20 23:23:31.975 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
2026-01-20 23:23:31.990 [DEBUG] mcp.server.lowlevel.server:783 - Response sent
|
||||
175
logs/doris_mcp_server_error.log
Normal file
175
logs/doris_mcp_server_error.log
Normal file
@ -0,0 +1,175 @@
|
||||
2026-01-14 15:39:29.705 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-14 15:39:29.705 [ERROR] doris_mcp_server.utils.db:626 - Failed to initialize connection pool: Connection pool health check failed
|
||||
2026-01-14 15:39:29.705 [ERROR] doris_mcp_server.main.DorisServer:490 - stdio server startup failed: Connection pool health check failed
|
||||
2026-01-14 15:39:29.705 [ERROR] doris_mcp_server.main.DorisServer:491 - Error type: <class 'RuntimeError'>
|
||||
2026-01-14 15:39:29.705 [ERROR] doris_mcp_server.main:929 - Server runtime error: Connection pool health check failed
|
||||
2026-01-14 15:40:47.570 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-14 15:40:47.570 [ERROR] doris_mcp_server.utils.db:626 - Failed to initialize connection pool: Connection pool health check failed
|
||||
2026-01-14 15:40:47.570 [ERROR] doris_mcp_server.main.DorisServer:490 - stdio server startup failed: Connection pool health check failed
|
||||
2026-01-14 15:40:47.570 [ERROR] doris_mcp_server.main.DorisServer:491 - Error type: <class 'RuntimeError'>
|
||||
2026-01-14 15:40:47.570 [ERROR] doris_mcp_server.main:929 - Server runtime error: Connection pool health check failed
|
||||
2026-01-14 16:36:35.839 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-14 16:36:35.840 [ERROR] doris_mcp_server.utils.db:626 - Failed to initialize connection pool: Connection pool health check failed
|
||||
2026-01-14 16:36:35.840 [ERROR] doris_mcp_server.main.DorisServer:490 - stdio server startup failed: Connection pool health check failed
|
||||
2026-01-14 16:36:35.840 [ERROR] doris_mcp_server.main.DorisServer:491 - Error type: <class 'RuntimeError'>
|
||||
2026-01-14 16:36:35.840 [ERROR] doris_mcp_server.main:929 - Server runtime error: Connection pool health check failed
|
||||
2026-01-14 16:53:58.021 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-14 16:53:58.021 [ERROR] doris_mcp_server.utils.db:626 - Failed to initialize connection pool: Connection pool health check failed
|
||||
2026-01-14 16:53:58.021 [ERROR] doris_mcp_server.main.DorisServer:490 - stdio server startup failed: Connection pool health check failed
|
||||
2026-01-14 16:53:58.021 [ERROR] doris_mcp_server.main.DorisServer:491 - Error type: <class 'RuntimeError'>
|
||||
2026-01-14 16:53:58.021 [ERROR] doris_mcp_server.main:929 - Server runtime error: Connection pool health check failed
|
||||
2026-01-14 22:32:53.107 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-14 22:32:53.107 [ERROR] doris_mcp_server.utils.db:626 - Failed to initialize connection pool: Connection pool health check failed
|
||||
2026-01-14 22:32:53.107 [ERROR] doris_mcp_server.main.DorisServer:490 - stdio server startup failed: Connection pool health check failed
|
||||
2026-01-14 22:32:53.107 [ERROR] doris_mcp_server.main.DorisServer:491 - Error type: <class 'RuntimeError'>
|
||||
2026-01-14 22:32:53.107 [ERROR] doris_mcp_server.main:929 - Server runtime error: Connection pool health check failed
|
||||
2026-01-14 22:40:11.992 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-14 22:40:11.992 [ERROR] doris_mcp_server.utils.db:626 - Failed to initialize connection pool: Connection pool health check failed
|
||||
2026-01-14 22:40:11.992 [ERROR] doris_mcp_server.main.DorisServer:490 - stdio server startup failed: Connection pool health check failed
|
||||
2026-01-14 22:40:11.992 [ERROR] doris_mcp_server.main.DorisServer:491 - Error type: <class 'RuntimeError'>
|
||||
2026-01-14 22:40:11.992 [ERROR] doris_mcp_server.main:929 - Server runtime error: Connection pool health check failed
|
||||
2026-01-14 22:59:43.887 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2013, 'Lost connection to MySQL server during query')
|
||||
2026-01-14 22:59:43.887 [ERROR] doris_mcp_server.utils.db:626 - Failed to initialize connection pool: Connection pool health check failed
|
||||
2026-01-14 22:59:43.888 [ERROR] doris_mcp_server.main.DorisServer:490 - stdio server startup failed: Connection pool health check failed
|
||||
2026-01-14 22:59:43.888 [ERROR] doris_mcp_server.main.DorisServer:491 - Error type: <class 'RuntimeError'>
|
||||
2026-01-14 22:59:43.888 [ERROR] doris_mcp_server.main:929 - Server runtime error: Connection pool health check failed
|
||||
2026-01-16 14:23:45.399 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-16 14:23:45.399 [ERROR] doris_mcp_server.utils.db:626 - Failed to initialize connection pool: Connection pool health check failed
|
||||
2026-01-16 14:23:45.399 [ERROR] doris_mcp_server.main.DorisServer:490 - stdio server startup failed: Connection pool health check failed
|
||||
2026-01-16 14:23:45.399 [ERROR] doris_mcp_server.main.DorisServer:491 - Error type: <class 'RuntimeError'>
|
||||
2026-01-16 14:23:45.399 [ERROR] doris_mcp_server.main:929 - Server runtime error: Connection pool health check failed
|
||||
2026-01-16 14:24:23.061 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-16 14:24:23.062 [ERROR] doris_mcp_server.utils.db:626 - Failed to initialize connection pool: Connection pool health check failed
|
||||
2026-01-16 14:24:23.062 [ERROR] doris_mcp_server.main.DorisServer:490 - stdio server startup failed: Connection pool health check failed
|
||||
2026-01-16 14:24:23.062 [ERROR] doris_mcp_server.main.DorisServer:491 - Error type: <class 'RuntimeError'>
|
||||
2026-01-16 14:24:23.062 [ERROR] doris_mcp_server.main:929 - Server runtime error: Connection pool health check failed
|
||||
2026-01-16 14:25:59.441 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-16 14:25:59.441 [ERROR] doris_mcp_server.utils.db:626 - Failed to initialize connection pool: Connection pool health check failed
|
||||
2026-01-16 14:25:59.441 [ERROR] doris_mcp_server.main.DorisServer:490 - stdio server startup failed: Connection pool health check failed
|
||||
2026-01-16 14:25:59.441 [ERROR] doris_mcp_server.main.DorisServer:491 - Error type: <class 'RuntimeError'>
|
||||
2026-01-16 14:25:59.441 [ERROR] doris_mcp_server.main:929 - Server runtime error: Connection pool health check failed
|
||||
2026-01-16 15:25:16.426 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-16 15:25:16.426 [ERROR] doris_mcp_server.utils.db:626 - Failed to initialize connection pool: Connection pool health check failed
|
||||
2026-01-16 15:25:16.426 [ERROR] doris_mcp_server.main.DorisServer:490 - stdio server startup failed: Connection pool health check failed
|
||||
2026-01-16 15:25:16.426 [ERROR] doris_mcp_server.main.DorisServer:491 - Error type: <class 'RuntimeError'>
|
||||
2026-01-16 15:25:16.426 [ERROR] doris_mcp_server.main:929 - Server runtime error: Connection pool health check failed
|
||||
2026-01-17 09:14:54.547 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
2026-01-17 09:14:54.549 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
2026-01-17 09:14:54.550 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
2026-01-17 09:14:54.552 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
2026-01-17 09:14:54.554 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
2026-01-17 09:16:18.741 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
2026-01-17 09:16:18.749 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
2026-01-17 09:16:18.752 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
2026-01-17 09:16:18.754 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
2026-01-17 09:16:18.755 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
2026-01-17 09:17:25.179 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
2026-01-17 09:17:25.194 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
2026-01-17 09:17:25.227 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
2026-01-17 09:19:26.078 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
2026-01-17 09:19:26.080 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
2026-01-17 09:19:26.083 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
2026-01-17 09:19:26.085 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
2026-01-17 09:19:26.087 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
2026-01-17 15:18:06.154 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
2026-01-17 15:18:06.159 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
2026-01-17 15:18:06.160 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
2026-01-17 15:18:06.163 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
2026-01-17 15:18:06.166 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
2026-01-18 01:04:06.180 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
2026-01-18 01:04:06.183 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
2026-01-18 01:04:06.186 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
2026-01-18 01:04:06.188 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
2026-01-18 01:04:06.190 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
2026-01-18 14:49:46.617 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
2026-01-18 14:49:46.619 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
2026-01-18 14:49:46.620 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
2026-01-18 14:49:46.621 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
2026-01-18 14:49:46.625 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
2026-01-18 22:39:37.843 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
2026-01-18 22:39:37.845 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
2026-01-18 22:39:37.846 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
2026-01-18 22:39:37.847 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
2026-01-18 22:39:37.848 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
2026-01-19 10:06:27.229 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-19 10:06:30.236 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-19 10:06:32.243 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-19 10:06:34.245 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-19 10:06:34.250 [ERROR] doris_mcp_server.utils.db:858 - ❌ Pool recovery failed after all attempts
|
||||
2026-01-19 10:07:04.252 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: 'NoneType' object has no attribute 'acquire'
|
||||
2026-01-19 10:07:04.254 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-19 10:07:06.257 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-19 10:07:08.259 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-19 10:07:08.260 [ERROR] doris_mcp_server.utils.db:858 - ❌ Pool recovery failed after all attempts
|
||||
2026-01-19 10:07:26.180 [ERROR] doris_mcp_server.utils.db:767 - Stale connection cleanup error: 'NoneType' object has no attribute 'size'
|
||||
2026-01-19 10:07:38.262 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: 'NoneType' object has no attribute 'acquire'
|
||||
2026-01-19 10:07:38.263 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-19 10:07:40.267 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-19 10:07:42.272 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-19 10:07:42.274 [ERROR] doris_mcp_server.utils.db:858 - ❌ Pool recovery failed after all attempts
|
||||
2026-01-19 10:08:12.277 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: 'NoneType' object has no attribute 'acquire'
|
||||
2026-01-19 10:08:12.283 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-19 10:08:14.285 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-19 10:08:16.288 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-19 10:08:16.288 [ERROR] doris_mcp_server.utils.db:858 - ❌ Pool recovery failed after all attempts
|
||||
2026-01-19 10:08:26.183 [ERROR] doris_mcp_server.utils.db:767 - Stale connection cleanup error: 'NoneType' object has no attribute 'size'
|
||||
2026-01-19 10:08:46.268 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: 'NoneType' object has no attribute 'acquire'
|
||||
2026-01-19 10:08:46.284 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-19 10:08:48.283 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-19 10:08:50.284 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-19 10:08:50.284 [ERROR] doris_mcp_server.utils.db:858 - ❌ Pool recovery failed after all attempts
|
||||
2026-01-19 10:09:20.270 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: 'NoneType' object has no attribute 'acquire'
|
||||
2026-01-19 10:09:20.275 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-19 10:09:22.279 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-19 10:09:24.281 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-19 10:09:24.281 [ERROR] doris_mcp_server.utils.db:858 - ❌ Pool recovery failed after all attempts
|
||||
2026-01-19 10:09:26.139 [ERROR] doris_mcp_server.utils.db:767 - Stale connection cleanup error: 'NoneType' object has no attribute 'size'
|
||||
2026-01-19 10:09:54.286 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: 'NoneType' object has no attribute 'acquire'
|
||||
2026-01-19 10:09:54.290 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-19 10:09:56.292 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-19 10:09:58.298 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-19 10:09:58.298 [ERROR] doris_mcp_server.utils.db:858 - ❌ Pool recovery failed after all attempts
|
||||
2026-01-19 10:10:26.138 [ERROR] doris_mcp_server.utils.db:767 - Stale connection cleanup error: 'NoneType' object has no attribute 'size'
|
||||
2026-01-19 10:10:28.304 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: 'NoneType' object has no attribute 'acquire'
|
||||
2026-01-19 10:10:28.307 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-19 10:10:30.310 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-19 10:10:32.312 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-19 10:10:32.312 [ERROR] doris_mcp_server.utils.db:858 - ❌ Pool recovery failed after all attempts
|
||||
2026-01-19 10:11:02.315 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: 'NoneType' object has no attribute 'acquire'
|
||||
2026-01-19 10:11:02.318 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-19 10:11:04.323 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-19 10:11:06.329 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-19 10:11:06.330 [ERROR] doris_mcp_server.utils.db:858 - ❌ Pool recovery failed after all attempts
|
||||
2026-01-19 10:11:26.140 [ERROR] doris_mcp_server.utils.db:767 - Stale connection cleanup error: 'NoneType' object has no attribute 'size'
|
||||
2026-01-19 10:11:36.332 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: 'NoneType' object has no attribute 'acquire'
|
||||
2026-01-19 10:11:36.333 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-19 10:11:38.336 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-19 10:11:40.337 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-19 10:11:40.338 [ERROR] doris_mcp_server.utils.db:858 - ❌ Pool recovery failed after all attempts
|
||||
2026-01-19 10:12:10.339 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: 'NoneType' object has no attribute 'acquire'
|
||||
2026-01-19 10:12:10.339 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-19 10:12:12.341 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-19 10:12:14.345 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-19 10:12:14.345 [ERROR] doris_mcp_server.utils.db:858 - ❌ Pool recovery failed after all attempts
|
||||
2026-01-19 10:12:26.141 [ERROR] doris_mcp_server.utils.db:767 - Stale connection cleanup error: 'NoneType' object has no attribute 'size'
|
||||
2026-01-19 10:12:44.346 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: 'NoneType' object has no attribute 'acquire'
|
||||
2026-01-19 10:12:44.347 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-19 10:12:46.349 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-19 10:12:48.352 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-19 10:12:48.352 [ERROR] doris_mcp_server.utils.db:858 - ❌ Pool recovery failed after all attempts
|
||||
2026-01-19 10:13:18.353 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: 'NoneType' object has no attribute 'acquire'
|
||||
2026-01-19 10:13:18.361 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-19 10:13:20.362 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-19 10:13:22.364 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-19 10:13:22.366 [ERROR] doris_mcp_server.utils.db:858 - ❌ Pool recovery failed after all attempts
|
||||
2026-01-19 10:13:26.142 [ERROR] doris_mcp_server.utils.db:767 - Stale connection cleanup error: 'NoneType' object has no attribute 'size'
|
||||
2026-01-19 22:31:25.116 [ERROR] doris_mcp_server.utils.db:638 - Pool health test failed: (2003, "Can't connect to MySQL server on '127.0.0.1'")
|
||||
2026-01-19 22:31:25.117 [ERROR] doris_mcp_server.utils.db:626 - Failed to initialize connection pool: Connection pool health check failed
|
||||
2026-01-19 22:31:25.117 [ERROR] doris_mcp_server.main.DorisServer:490 - stdio server startup failed: Connection pool health check failed
|
||||
2026-01-19 22:31:25.117 [ERROR] doris_mcp_server.main.DorisServer:491 - Error type: <class 'RuntimeError'>
|
||||
2026-01-19 22:31:25.117 [ERROR] doris_mcp_server.main:929 - Server runtime error: Connection pool health check failed
|
||||
2026-01-20 23:23:31.949 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
2026-01-20 23:23:31.955 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
2026-01-20 23:23:31.957 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
2026-01-20 23:23:31.960 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
2026-01-20 23:23:31.963 [ERROR] root:137 - Query execution failed: readexactly() called while another coroutine is already waiting for incoming data
|
||||
1343
logs/doris_mcp_server_info.log
Normal file
1343
logs/doris_mcp_server_info.log
Normal file
File diff suppressed because it is too large
Load Diff
53
logs/doris_mcp_server_warning.log
Normal file
53
logs/doris_mcp_server_warning.log
Normal file
@ -0,0 +1,53 @@
|
||||
2026-01-19 10:06:27.232 [WARNING] doris_mcp_server.utils.db:718 - ❌ Pool health check failed, attempting recovery
|
||||
2026-01-19 10:06:30.234 [WARNING] doris_mcp_server.utils.db:795 - Pool close timeout, forcing cleanup
|
||||
2026-01-19 10:06:30.238 [WARNING] doris_mcp_server.utils.db:834 - ❌ Pool recovery health check failed on attempt 1
|
||||
2026-01-19 10:06:32.243 [WARNING] doris_mcp_server.utils.db:834 - ❌ Pool recovery health check failed on attempt 2
|
||||
2026-01-19 10:06:34.246 [WARNING] doris_mcp_server.utils.db:834 - ❌ Pool recovery health check failed on attempt 3
|
||||
2026-01-19 10:07:04.253 [WARNING] doris_mcp_server.utils.db:718 - ❌ Pool health check failed, attempting recovery
|
||||
2026-01-19 10:07:04.254 [WARNING] doris_mcp_server.utils.db:834 - ❌ Pool recovery health check failed on attempt 1
|
||||
2026-01-19 10:07:06.257 [WARNING] doris_mcp_server.utils.db:834 - ❌ Pool recovery health check failed on attempt 2
|
||||
2026-01-19 10:07:08.260 [WARNING] doris_mcp_server.utils.db:834 - ❌ Pool recovery health check failed on attempt 3
|
||||
2026-01-19 10:07:38.262 [WARNING] doris_mcp_server.utils.db:718 - ❌ Pool health check failed, attempting recovery
|
||||
2026-01-19 10:07:38.265 [WARNING] doris_mcp_server.utils.db:834 - ❌ Pool recovery health check failed on attempt 1
|
||||
2026-01-19 10:07:40.268 [WARNING] doris_mcp_server.utils.db:834 - ❌ Pool recovery health check failed on attempt 2
|
||||
2026-01-19 10:07:42.274 [WARNING] doris_mcp_server.utils.db:834 - ❌ Pool recovery health check failed on attempt 3
|
||||
2026-01-19 10:08:12.280 [WARNING] doris_mcp_server.utils.db:718 - ❌ Pool health check failed, attempting recovery
|
||||
2026-01-19 10:08:12.283 [WARNING] doris_mcp_server.utils.db:834 - ❌ Pool recovery health check failed on attempt 1
|
||||
2026-01-19 10:08:14.286 [WARNING] doris_mcp_server.utils.db:834 - ❌ Pool recovery health check failed on attempt 2
|
||||
2026-01-19 10:08:16.288 [WARNING] doris_mcp_server.utils.db:834 - ❌ Pool recovery health check failed on attempt 3
|
||||
2026-01-19 10:08:46.275 [WARNING] doris_mcp_server.utils.db:718 - ❌ Pool health check failed, attempting recovery
|
||||
2026-01-19 10:08:46.284 [WARNING] doris_mcp_server.utils.db:834 - ❌ Pool recovery health check failed on attempt 1
|
||||
2026-01-19 10:08:48.284 [WARNING] doris_mcp_server.utils.db:834 - ❌ Pool recovery health check failed on attempt 2
|
||||
2026-01-19 10:08:50.284 [WARNING] doris_mcp_server.utils.db:834 - ❌ Pool recovery health check failed on attempt 3
|
||||
2026-01-19 10:09:20.273 [WARNING] doris_mcp_server.utils.db:718 - ❌ Pool health check failed, attempting recovery
|
||||
2026-01-19 10:09:20.276 [WARNING] doris_mcp_server.utils.db:834 - ❌ Pool recovery health check failed on attempt 1
|
||||
2026-01-19 10:09:22.279 [WARNING] doris_mcp_server.utils.db:834 - ❌ Pool recovery health check failed on attempt 2
|
||||
2026-01-19 10:09:24.281 [WARNING] doris_mcp_server.utils.db:834 - ❌ Pool recovery health check failed on attempt 3
|
||||
2026-01-19 10:09:54.287 [WARNING] doris_mcp_server.utils.db:718 - ❌ Pool health check failed, attempting recovery
|
||||
2026-01-19 10:09:54.290 [WARNING] doris_mcp_server.utils.db:834 - ❌ Pool recovery health check failed on attempt 1
|
||||
2026-01-19 10:09:56.292 [WARNING] doris_mcp_server.utils.db:834 - ❌ Pool recovery health check failed on attempt 2
|
||||
2026-01-19 10:09:58.298 [WARNING] doris_mcp_server.utils.db:834 - ❌ Pool recovery health check failed on attempt 3
|
||||
2026-01-19 10:10:28.306 [WARNING] doris_mcp_server.utils.db:718 - ❌ Pool health check failed, attempting recovery
|
||||
2026-01-19 10:10:28.308 [WARNING] doris_mcp_server.utils.db:834 - ❌ Pool recovery health check failed on attempt 1
|
||||
2026-01-19 10:10:30.311 [WARNING] doris_mcp_server.utils.db:834 - ❌ Pool recovery health check failed on attempt 2
|
||||
2026-01-19 10:10:32.312 [WARNING] doris_mcp_server.utils.db:834 - ❌ Pool recovery health check failed on attempt 3
|
||||
2026-01-19 10:11:02.317 [WARNING] doris_mcp_server.utils.db:718 - ❌ Pool health check failed, attempting recovery
|
||||
2026-01-19 10:11:02.318 [WARNING] doris_mcp_server.utils.db:834 - ❌ Pool recovery health check failed on attempt 1
|
||||
2026-01-19 10:11:04.323 [WARNING] doris_mcp_server.utils.db:834 - ❌ Pool recovery health check failed on attempt 2
|
||||
2026-01-19 10:11:06.329 [WARNING] doris_mcp_server.utils.db:834 - ❌ Pool recovery health check failed on attempt 3
|
||||
2026-01-19 10:11:36.332 [WARNING] doris_mcp_server.utils.db:718 - ❌ Pool health check failed, attempting recovery
|
||||
2026-01-19 10:11:36.334 [WARNING] doris_mcp_server.utils.db:834 - ❌ Pool recovery health check failed on attempt 1
|
||||
2026-01-19 10:11:38.336 [WARNING] doris_mcp_server.utils.db:834 - ❌ Pool recovery health check failed on attempt 2
|
||||
2026-01-19 10:11:40.338 [WARNING] doris_mcp_server.utils.db:834 - ❌ Pool recovery health check failed on attempt 3
|
||||
2026-01-19 10:12:10.339 [WARNING] doris_mcp_server.utils.db:718 - ❌ Pool health check failed, attempting recovery
|
||||
2026-01-19 10:12:10.339 [WARNING] doris_mcp_server.utils.db:834 - ❌ Pool recovery health check failed on attempt 1
|
||||
2026-01-19 10:12:12.342 [WARNING] doris_mcp_server.utils.db:834 - ❌ Pool recovery health check failed on attempt 2
|
||||
2026-01-19 10:12:14.345 [WARNING] doris_mcp_server.utils.db:834 - ❌ Pool recovery health check failed on attempt 3
|
||||
2026-01-19 10:12:44.346 [WARNING] doris_mcp_server.utils.db:718 - ❌ Pool health check failed, attempting recovery
|
||||
2026-01-19 10:12:44.347 [WARNING] doris_mcp_server.utils.db:834 - ❌ Pool recovery health check failed on attempt 1
|
||||
2026-01-19 10:12:46.350 [WARNING] doris_mcp_server.utils.db:834 - ❌ Pool recovery health check failed on attempt 2
|
||||
2026-01-19 10:12:48.352 [WARNING] doris_mcp_server.utils.db:834 - ❌ Pool recovery health check failed on attempt 3
|
||||
2026-01-19 10:13:18.357 [WARNING] doris_mcp_server.utils.db:718 - ❌ Pool health check failed, attempting recovery
|
||||
2026-01-19 10:13:18.361 [WARNING] doris_mcp_server.utils.db:834 - ❌ Pool recovery health check failed on attempt 1
|
||||
2026-01-19 10:13:20.362 [WARNING] doris_mcp_server.utils.db:834 - ❌ Pool recovery health check failed on attempt 2
|
||||
2026-01-19 10:13:22.365 [WARNING] doris_mcp_server.utils.db:834 - ❌ Pool recovery health check failed on attempt 3
|
||||
4
requirements.txt
Normal file
4
requirements.txt
Normal file
@ -0,0 +1,4 @@
|
||||
PySide6>=6.7.0
|
||||
psycopg2-binary>=2.9.0
|
||||
openpyxl>=3.1.0
|
||||
pywin32
|
||||
0
requirments.txt
Normal file
0
requirments.txt
Normal file
11
run.py
Normal file
11
run.py
Normal file
@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
File System Browser - Entry point
|
||||
Reads files and folders from a specified directory and displays them in a GUI
|
||||
"""
|
||||
|
||||
from src.main import main
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
0
src/__init__.py
Normal file
0
src/__init__.py
Normal file
432
src/db_manager.py
Normal file
432
src/db_manager.py
Normal file
@ -0,0 +1,432 @@
|
||||
import psycopg2
|
||||
from psycopg2.extras import RealDictCursor
|
||||
import datetime
|
||||
import uuid
|
||||
|
||||
class DatabaseManager:
|
||||
def __init__(self):
|
||||
self.config = {
|
||||
"host": "10.10.200.180",
|
||||
"port": "5432",
|
||||
"database": "tz_tzwork",
|
||||
"user": "tzwork",
|
||||
"password": "tzwork",
|
||||
}
|
||||
self.schema = '"tzwork"'
|
||||
|
||||
def get_connection(self):
|
||||
try:
|
||||
conn = psycopg2.connect(**self.config)
|
||||
return conn
|
||||
except Exception as e:
|
||||
print(f"Database connection error: {e}")
|
||||
return None
|
||||
|
||||
def insert_analysis_data(self, data_list):
|
||||
conn = self.get_connection()
|
||||
if not conn:
|
||||
return False, "Database connection failed"
|
||||
|
||||
try:
|
||||
cur = conn.cursor()
|
||||
|
||||
insert_query = f"""
|
||||
INSERT INTO {self.schema}.element_analysis_data (
|
||||
sample_name, sample_grade_id, sample_grade_alias, operator_name, instrument, method_name, sample_type,
|
||||
replicate_no, measure_datetime, measure_duration, check_type, check_status, grade_name, base_element, rsd_check,
|
||||
element_name, element_type, concentration_value, concentration_unit, std_dev_value,
|
||||
uncertainty_abs, uncertainty_rel, lower_acceptance_limit, upper_acceptance_limit,
|
||||
result_status, calibration_status, acceptance_status, is_deleted,
|
||||
xml_version, xml_creation_datetime, data_source
|
||||
) VALUES (
|
||||
%(sample_name)s, %(sample_grade_id)s, %(sample_grade_alias)s, %(operator_name)s, %(instrument)s, %(method_name)s, %(sample_type)s,
|
||||
%(replicate_no)s, %(measure_datetime)s, %(measure_duration)s, %(check_type)s, %(check_status)s, %(grade_name)s, %(base_element)s, %(rsd_check)s,
|
||||
%(element_name)s, %(element_type)s, %(concentration_value)s, %(concentration_unit)s, %(std_dev_value)s,
|
||||
%(uncertainty_abs)s, %(uncertainty_rel)s, %(lower_acceptance_limit)s, %(upper_acceptance_limit)s,
|
||||
%(result_status)s, %(calibration_status)s, %(acceptance_status)s, %(is_deleted)s,
|
||||
%(xml_version)s, %(xml_creation_datetime)s, %(data_source)s
|
||||
)
|
||||
"""
|
||||
|
||||
cur.executemany(insert_query, data_list)
|
||||
conn.commit()
|
||||
cur.close()
|
||||
conn.close()
|
||||
return True, f"Successfully inserted {len(data_list)} records."
|
||||
|
||||
except Exception as e:
|
||||
if conn:
|
||||
conn.rollback()
|
||||
conn.close()
|
||||
return False, str(e)
|
||||
|
||||
def authenticate_user(self, user_id, password_md5):
|
||||
conn = self.get_connection()
|
||||
if not conn:
|
||||
return False, "数据库连接失败"
|
||||
|
||||
try:
|
||||
cur = conn.cursor(cursor_factory=RealDictCursor)
|
||||
|
||||
query = f"""
|
||||
SELECT user_id, nick_name, password, org_id as data_corp, dept_id
|
||||
FROM {self.schema}.sys_user
|
||||
WHERE user_id = %s
|
||||
"""
|
||||
|
||||
cur.execute(query, (user_id,))
|
||||
user = cur.fetchone()
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
if not user:
|
||||
return False, "账号不存在"
|
||||
|
||||
if user['password'] != password_md5:
|
||||
return False, "密码错误"
|
||||
|
||||
return True, {
|
||||
"user_id": user['user_id'],
|
||||
"nick_name": user['nick_name'],
|
||||
"data_corp": user['data_corp'],
|
||||
"dept_id": user['dept_id']
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
print(f"Authentication error: {e}")
|
||||
if conn:
|
||||
conn.close()
|
||||
return False, f"认证失败: {str(e)}"
|
||||
|
||||
def get_latest_sample_data(self):
|
||||
"""
|
||||
Retrieves the most recently measured sample data, flattened.
|
||||
Returns a dictionary where keys are element names and values are concentrations.
|
||||
Also returns metadata.
|
||||
"""
|
||||
conn = self.get_connection()
|
||||
if not conn:
|
||||
return None
|
||||
|
||||
try:
|
||||
cur = conn.cursor(cursor_factory=RealDictCursor)
|
||||
|
||||
query = f"""
|
||||
SELECT *
|
||||
FROM {self.schema}.element_analysis_data
|
||||
WHERE measure_datetime = (
|
||||
SELECT MAX(measure_datetime) FROM {self.schema}.element_analysis_data
|
||||
)
|
||||
"""
|
||||
|
||||
cur.execute(query)
|
||||
rows = cur.fetchall()
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
if not rows:
|
||||
return {}
|
||||
|
||||
result = {
|
||||
"metadata": {
|
||||
"sample_name": rows[0]['sample_name'],
|
||||
"grade_id": rows[0]['sample_grade_id'],
|
||||
"heat_no": rows[0]['sample_name'],
|
||||
"batch_no": rows[0]['sample_grade_id'],
|
||||
},
|
||||
"elements": {}
|
||||
}
|
||||
|
||||
for row in rows:
|
||||
el_name = row['element_name']
|
||||
conc = row['concentration_value']
|
||||
result["elements"][el_name] = conc
|
||||
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
print(f"Query error: {e}")
|
||||
if conn:
|
||||
conn.close()
|
||||
return None
|
||||
|
||||
def _get_check_flag(self, check_information):
|
||||
"""
|
||||
根据检验信息判断检验标志
|
||||
空值、None、'无标准值' 视为合格,其他视为不合格
|
||||
"""
|
||||
if not check_information or check_information == '无标准值':
|
||||
return "合格"
|
||||
return "不合格"
|
||||
|
||||
def insert_inspection_data(self, inspection_data):
|
||||
"""
|
||||
插入检验数据到 e_pz_hjqy_import 表
|
||||
inspection_data 结构: {
|
||||
"gch": "工程号",
|
||||
"heat_number": "炉号1",
|
||||
"heat_number2": "炉号2",
|
||||
"cz": "炉号1材质",
|
||||
"cz2": "炉号2材质",
|
||||
"check_information": "检验信息1",
|
||||
"check_information2": "检验信息2",
|
||||
"cd": "产地",
|
||||
"size": "规格",
|
||||
"elements": {"C": 0.15, "Si": 0.25, ...},
|
||||
"user_id": "用户ID",
|
||||
"dept_id": "部门ID",
|
||||
"data_corp": "所属公司"
|
||||
}
|
||||
"""
|
||||
conn = self.get_connection()
|
||||
if not conn:
|
||||
return False, "数据库连接失败"
|
||||
|
||||
try:
|
||||
cur = conn.cursor()
|
||||
|
||||
# 生成 UUID
|
||||
ksid = str(uuid.uuid4())
|
||||
|
||||
# 获取当前日期和时间
|
||||
current_date = datetime.datetime.now().strftime("%Y-%m-%d")
|
||||
current_datetime = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
# 准备元素数据(转换为小写字段名)
|
||||
elements = inspection_data.get("elements", {})
|
||||
element_values = {
|
||||
"c": elements.get("C"),
|
||||
"si": elements.get("Si"),
|
||||
"mn": elements.get("Mn"),
|
||||
"p": elements.get("P"),
|
||||
"s": elements.get("S"),
|
||||
"cr": elements.get("Cr"),
|
||||
"ni": elements.get("Ni"),
|
||||
"mo": elements.get("Mo"),
|
||||
"cu": elements.get("Cu"),
|
||||
"ti": elements.get("Ti"),
|
||||
"nb": elements.get("Nb"),
|
||||
"v": elements.get("V"),
|
||||
"al": elements.get("Al"),
|
||||
"w": elements.get("W"),
|
||||
"co": elements.get("Co"),
|
||||
"fe": elements.get("Fe"),
|
||||
"n": elements.get("N"),
|
||||
"se": elements.get("Se")
|
||||
}
|
||||
|
||||
# 插入查询
|
||||
insert_query = f"""
|
||||
INSERT INTO {self.schema}.e_pz_hjqy_import (
|
||||
ksid, gch, flag, rq, dept, cd, cz, cz2, size, heat_number, heat_number2,
|
||||
c, si, mn, p, s, cr, ni, mo, cu, ti, nb, v, al, w, co, fe, n, se,
|
||||
check_flag,check_flag2, check_information, check_information2, data_man, data_date, data_corp, stype
|
||||
) VALUES (
|
||||
%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s,
|
||||
%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s,
|
||||
%s, %s, %s, %s, %s, %s, %s, %s
|
||||
)
|
||||
"""
|
||||
|
||||
values = (
|
||||
ksid,
|
||||
inspection_data.get("gch"),
|
||||
'登记',
|
||||
current_date,
|
||||
inspection_data.get("dept_id"),
|
||||
inspection_data.get("cd"),
|
||||
inspection_data.get("cz"),
|
||||
inspection_data.get("cz2"),
|
||||
inspection_data.get("size"),
|
||||
inspection_data.get("heat_number"),
|
||||
inspection_data.get("heat_number2"),
|
||||
element_values["c"],
|
||||
element_values["si"],
|
||||
element_values["mn"],
|
||||
element_values["p"],
|
||||
element_values["s"],
|
||||
element_values["cr"],
|
||||
element_values["ni"],
|
||||
element_values["mo"],
|
||||
element_values["cu"],
|
||||
element_values["ti"],
|
||||
element_values["nb"],
|
||||
element_values["v"],
|
||||
element_values["al"],
|
||||
element_values["w"],
|
||||
element_values["co"],
|
||||
element_values["fe"],
|
||||
element_values["n"],
|
||||
element_values["se"],
|
||||
self._get_check_flag(inspection_data.get("check_information")),
|
||||
self._get_check_flag(inspection_data.get("check_information2")),
|
||||
inspection_data.get("check_information"),
|
||||
inspection_data.get("check_information2"),
|
||||
inspection_data.get("user_id"),
|
||||
current_datetime,
|
||||
inspection_data.get("data_corp"),
|
||||
"首检"
|
||||
)
|
||||
|
||||
cur.execute(insert_query, values)
|
||||
conn.commit()
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
return True, f"成功插入检验数据,记录ID: {ksid}"
|
||||
|
||||
except Exception as e:
|
||||
if conn:
|
||||
conn.rollback()
|
||||
conn.close()
|
||||
return False, f"插入失败: {str(e)}"
|
||||
|
||||
def query_by_gch(self, gch):
|
||||
"""
|
||||
根据工程号查询产地、规格、炉号、材质等信息
|
||||
"""
|
||||
conn = self.get_connection()
|
||||
if not conn:
|
||||
return None, "数据库连接失败"
|
||||
|
||||
try:
|
||||
cur = conn.cursor(cursor_factory=RealDictCursor)
|
||||
|
||||
query = f"""
|
||||
SELECT
|
||||
concat(a.cd,'/',a.cz) as cd,
|
||||
a.tsize as size,
|
||||
coalesce(b.qy_luonot, c.qy_luonot) as heat_number,
|
||||
coalesce(b.qy_luonow, c.qy_luonow) as heat_number2,
|
||||
coalesce(b.qy_czt, c.qy_czt) as qy_czt,
|
||||
coalesce(b.qy_czw, c.qy_czw) as qy_czw
|
||||
FROM {self.schema}.v_sc_gclist a
|
||||
LEFT JOIN {self.schema}.e_pz_hjqy b ON a.bgc = b.qy_gcht
|
||||
LEFT JOIN {self.schema}.e_pz_hjqy c ON a.bgc = c.qy_gchw
|
||||
WHERE a.bgc = %s
|
||||
LIMIT 1
|
||||
"""
|
||||
|
||||
cur.execute(query, (gch,))
|
||||
row = cur.fetchone()
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
if not row:
|
||||
return None, "未找到该工程号"
|
||||
|
||||
return row, None
|
||||
|
||||
except Exception as e:
|
||||
if conn:
|
||||
conn.close()
|
||||
return None, f"查询失败: {str(e)}"
|
||||
|
||||
def get_inspection_list(self, limit=10, offset=0, user_id=''):
|
||||
"""
|
||||
分页获取检验数据列表
|
||||
"""
|
||||
conn = self.get_connection()
|
||||
if not conn:
|
||||
return None, "数据库连接失败"
|
||||
|
||||
try:
|
||||
cur = conn.cursor(cursor_factory=RealDictCursor)
|
||||
|
||||
# 根据是否提供 user_id 构建不同的查询
|
||||
if user_id:
|
||||
query = f"""
|
||||
SELECT
|
||||
to_char(rq,'yyyy-MM-dd') as date,
|
||||
gch as batch_no,
|
||||
data_man,
|
||||
su.nick_name as inspector,
|
||||
heat_number,
|
||||
heat_number2,
|
||||
cz,
|
||||
cz2,
|
||||
size as spec
|
||||
FROM {self.schema}.e_pz_hjqy_import t
|
||||
JOIN {self.schema}.sys_user su ON t.data_man = su.user_id
|
||||
WHERE su.user_id = %s
|
||||
ORDER BY rq DESC
|
||||
LIMIT %s OFFSET %s
|
||||
"""
|
||||
cur.execute(query, (user_id, limit, offset))
|
||||
else:
|
||||
query = f"""
|
||||
SELECT
|
||||
to_char(rq,'yyyy-MM-dd') as date,
|
||||
gch as batch_no,
|
||||
data_man,
|
||||
su.nick_name as inspector,
|
||||
heat_number,
|
||||
heat_number2,
|
||||
cz,
|
||||
cz2,
|
||||
size as spec
|
||||
FROM {self.schema}.e_pz_hjqy_import t
|
||||
LEFT JOIN {self.schema}.sys_user su ON t.data_man = su.user_id
|
||||
ORDER BY rq DESC
|
||||
LIMIT %s OFFSET %s
|
||||
"""
|
||||
cur.execute(query, (limit, offset))
|
||||
|
||||
rows = cur.fetchall()
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
return rows, None
|
||||
|
||||
except Exception as e:
|
||||
if conn:
|
||||
conn.close()
|
||||
return None, f"查询失败: {str(e)}"
|
||||
|
||||
def query_standard_range(self, heat_number, heat_number2):
|
||||
"""
|
||||
根据炉号1(tph)和炉号2(company_ph)查询标准值范围
|
||||
返回两条记录:炉号1的标准范围和炉号2的标准范围
|
||||
"""
|
||||
conn = self.get_connection()
|
||||
if not conn:
|
||||
return None, None, "数据库连接失败"
|
||||
|
||||
try:
|
||||
cur = conn.cursor(cursor_factory=RealDictCursor)
|
||||
|
||||
# 只选取18个元素的范围字段: C, Si, Mn, P, S, Cr, Ni, Mo, Cu, Ti, Nb, V, Al, W, Co, N, Fe, Se
|
||||
select_clause = """
|
||||
SELECT c.tph, c.company_ph,
|
||||
b.cmin, c_max as cmax, simin, simax, mnmin, mnmax, pmin, pmax, smin, smax,
|
||||
crmin, crmax, nimin, nimax, momin, momax, cumin, cumax, timin, timax,
|
||||
nbmin, nbmax, vmin, vmax, almin, almax, wmin, wmax, comin, comax,
|
||||
nmin, nmax, femin, femax, semin, semax
|
||||
FROM {schema}.e_pz_lhcf c,
|
||||
{schema}.e_pz_bzcs b
|
||||
WHERE c.relevancy_note = b.note
|
||||
AND (c.tph = %s OR c.company_ph = %s)
|
||||
""".format(schema=self.schema)
|
||||
|
||||
heat1_range = None
|
||||
heat2_range = None
|
||||
|
||||
# 查询炉号1标准
|
||||
if heat_number:
|
||||
cur.execute(select_clause, (heat_number, heat_number))
|
||||
heat1_range = cur.fetchone()
|
||||
|
||||
# 查询炉号2标准
|
||||
if heat_number2:
|
||||
cur.execute(select_clause, (heat_number2, heat_number2))
|
||||
heat2_range = cur.fetchone()
|
||||
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
return heat1_range, heat2_range, None
|
||||
|
||||
except Exception as e:
|
||||
if conn:
|
||||
conn.close()
|
||||
return None, None, f"查询失败: {str(e)}"
|
||||
84
src/file_system.py
Normal file
84
src/file_system.py
Normal file
@ -0,0 +1,84 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
from dataclasses import dataclass
|
||||
from typing import List
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
@dataclass
|
||||
class FileInfo:
|
||||
"""Data class to represent file information"""
|
||||
name: str
|
||||
path: str
|
||||
size: int
|
||||
file_type: str
|
||||
is_dir: bool
|
||||
modified_time: str
|
||||
|
||||
@property
|
||||
def size_readable(self) -> str:
|
||||
"""Convert size to human-readable format"""
|
||||
for unit in ['B', 'KB', 'MB', 'GB']:
|
||||
if self.size < 1024:
|
||||
return f"{self.size:.2f} {unit}"
|
||||
self.size /= 1024
|
||||
return f"{self.size:.2f} TB"
|
||||
|
||||
|
||||
class FileSystemReader:
|
||||
"""Handles reading file system information"""
|
||||
|
||||
def __init__(self, root_path: str):
|
||||
self.root_path = Path(root_path)
|
||||
|
||||
def get_files_and_folders(self) -> List[FileInfo]:
|
||||
"""
|
||||
Read all files and folders from the root path
|
||||
|
||||
Returns:
|
||||
List[FileInfo]: List of file information objects
|
||||
"""
|
||||
items = []
|
||||
|
||||
if not self.root_path.exists():
|
||||
return items
|
||||
|
||||
try:
|
||||
for item in self.root_path.iterdir():
|
||||
try:
|
||||
stat_info = item.stat()
|
||||
file_info = FileInfo(
|
||||
name=item.name,
|
||||
path=str(item),
|
||||
size=stat_info.st_size,
|
||||
file_type=self._get_file_type(item),
|
||||
is_dir=item.is_dir(),
|
||||
modified_time=datetime.fromtimestamp(
|
||||
stat_info.st_mtime
|
||||
).strftime("%Y-%m-%d %H:%M:%S")
|
||||
)
|
||||
items.append(file_info)
|
||||
except (PermissionError, OSError):
|
||||
# Skip files/folders we can't read
|
||||
continue
|
||||
|
||||
# Sort: folders first, then by name
|
||||
items.sort(key=lambda x: (not x.is_dir, x.name.lower()))
|
||||
return items
|
||||
|
||||
except PermissionError:
|
||||
return items
|
||||
|
||||
def _get_file_type(self, path: Path) -> str:
|
||||
"""
|
||||
Determine file type
|
||||
|
||||
Args:
|
||||
path: Path object
|
||||
|
||||
Returns:
|
||||
str: File type (Folder, or extension)
|
||||
"""
|
||||
if path.is_dir():
|
||||
return "Folder"
|
||||
return path.suffix[1:].upper() if path.suffix else "File"
|
||||
103
src/inspection_app.py
Normal file
103
src/inspection_app.py
Normal file
@ -0,0 +1,103 @@
|
||||
import sys
|
||||
from PySide6.QtWidgets import QApplication
|
||||
from ui.inspection_window import InspectionWindow
|
||||
from ui.login_window import LoginWindow
|
||||
from src.session_manager import SessionManager
|
||||
from datetime import datetime
|
||||
|
||||
def get_mock_data():
|
||||
return [
|
||||
{
|
||||
"date": "2025-12-18",
|
||||
"inspector": "张三",
|
||||
"batch_no": "B20251218-001",
|
||||
"heat_no": "H88291",
|
||||
"material": "Q235B",
|
||||
"spec": "φ20",
|
||||
"weight": "1500kg"
|
||||
},
|
||||
{
|
||||
"date": "2025-12-18",
|
||||
"inspector": "李四",
|
||||
"batch_no": "B20251218-002",
|
||||
"heat_no": "H88292",
|
||||
"material": "HRB400",
|
||||
"spec": "φ25",
|
||||
"weight": "2300kg"
|
||||
},
|
||||
{
|
||||
"date": "2025-12-17",
|
||||
"inspector": "王五",
|
||||
"batch_no": "B20251217-005",
|
||||
"heat_no": "H88280",
|
||||
"material": "Q345",
|
||||
"spec": "10x10",
|
||||
"weight": "500kg"
|
||||
},
|
||||
{
|
||||
"date": "2025-12-16",
|
||||
"inspector": "赵六",
|
||||
"batch_no": "B20251216-010",
|
||||
"heat_no": "H88275",
|
||||
"material": "304",
|
||||
"spec": "2mm",
|
||||
"weight": "120kg"
|
||||
},
|
||||
{
|
||||
"date": "2025-12-16",
|
||||
"inspector": "赵六",
|
||||
"batch_no": "B20251216-010",
|
||||
"heat_no": "H88275",
|
||||
"material": "304",
|
||||
"spec": "2mm",
|
||||
"weight": "120kg"
|
||||
},
|
||||
{
|
||||
"date": "2025-12-16",
|
||||
"inspector": "赵六",
|
||||
"batch_no": "B20251216-010",
|
||||
"heat_no": "H88275",
|
||||
"material": "304",
|
||||
"spec": "2mm",
|
||||
"weight": "120kg"
|
||||
},
|
||||
{
|
||||
"date": "2025-12-16",
|
||||
"inspector": "赵六",
|
||||
"batch_no": "B20251216-010",
|
||||
"heat_no": "H88275",
|
||||
"material": "304",
|
||||
"spec": "2mm",
|
||||
"weight": "120kg"
|
||||
}
|
||||
]
|
||||
|
||||
def main():
|
||||
app = QApplication(sys.argv)
|
||||
|
||||
font = app.font()
|
||||
font.setFamily("Microsoft YaHei")
|
||||
font.setPointSize(10) # Base size
|
||||
app.setFont(font)
|
||||
|
||||
session = SessionManager()
|
||||
|
||||
login_window = LoginWindow()
|
||||
|
||||
def on_login_success(user_info):
|
||||
session.set_current_user(user_info)
|
||||
|
||||
window = InspectionWindow()
|
||||
|
||||
# 使用新的分页加载方式
|
||||
window.load_initial_data()
|
||||
|
||||
window.showMaximized()
|
||||
|
||||
login_window.login_success.connect(on_login_success)
|
||||
login_window.showMaximized()
|
||||
|
||||
sys.exit(app.exec())
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
4
src/main.py
Normal file
4
src/main.py
Normal file
@ -0,0 +1,4 @@
|
||||
from src.inspection_app import main
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
123
src/main_old.py
Normal file
123
src/main_old.py
Normal file
@ -0,0 +1,123 @@
|
||||
import sys
|
||||
from PySide6.QtWidgets import (
|
||||
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
|
||||
QTableWidget, QTableWidgetItem, QLabel, QPushButton, QHeaderView
|
||||
)
|
||||
from PySide6.QtCore import Qt, QTimer
|
||||
from PySide6.QtGui import QIcon, QColor
|
||||
from src.file_system import FileSystemReader, FileInfo
|
||||
|
||||
|
||||
class FileSystemBrowser(QMainWindow):
|
||||
"""Main application window for file system browser"""
|
||||
|
||||
def __init__(self, target_path: str = "C://OEM"):
|
||||
super().__init__()
|
||||
self.target_path = target_path
|
||||
self.file_reader = FileSystemReader(target_path)
|
||||
self.init_ui()
|
||||
self.load_files()
|
||||
|
||||
def init_ui(self):
|
||||
"""Initialize the user interface"""
|
||||
self.setWindowTitle("File System Browser")
|
||||
|
||||
# Central widget
|
||||
central_widget = QWidget()
|
||||
self.setCentralWidget(central_widget)
|
||||
main_layout = QVBoxLayout(central_widget)
|
||||
main_layout.setContentsMargins(10, 10, 10, 10)
|
||||
main_layout.setSpacing(10)
|
||||
|
||||
# Header section
|
||||
header_layout = QHBoxLayout()
|
||||
self.path_label = QLabel(f"Path: {self.target_path}")
|
||||
self.path_label.setStyleSheet("font-weight: bold; font-size: 12px;")
|
||||
header_layout.addWidget(self.path_label)
|
||||
header_layout.addStretch()
|
||||
|
||||
# Refresh button
|
||||
refresh_button = QPushButton("Refresh")
|
||||
refresh_button.clicked.connect(self.load_files)
|
||||
header_layout.addWidget(refresh_button)
|
||||
|
||||
main_layout.addLayout(header_layout)
|
||||
|
||||
# Table widget
|
||||
self.table = QTableWidget()
|
||||
self.table.setColumnCount(4)
|
||||
self.table.setHorizontalHeaderLabels(["Name", "Type", "Size", "Modified"])
|
||||
|
||||
# Set column resize modes for better adaptive layout
|
||||
header = self.table.horizontalHeader()
|
||||
header.setSectionResizeMode(0, QHeaderView.Stretch) # Name column stretches
|
||||
header.setSectionResizeMode(1, QHeaderView.ResizeToContents) # Type column fits content
|
||||
header.setSectionResizeMode(2, QHeaderView.ResizeToContents) # Size column fits content
|
||||
header.setSectionResizeMode(3, QHeaderView.ResizeToContents) # Modified column fits content
|
||||
|
||||
self.table.setAlternatingRowColors(True)
|
||||
self.table.setSelectionBehavior(QTableWidget.SelectRows)
|
||||
main_layout.addWidget(self.table)
|
||||
|
||||
# Status bar
|
||||
self.status_label = QLabel("Ready")
|
||||
self.status_label.setStyleSheet("padding: 5px;")
|
||||
main_layout.addWidget(self.status_label)
|
||||
|
||||
# Set window to maximize on startup
|
||||
self.showMaximized()
|
||||
|
||||
def load_files(self):
|
||||
"""Load and display files and folders"""
|
||||
self.status_label.setText("Loading...")
|
||||
QApplication.processEvents()
|
||||
|
||||
try:
|
||||
files = self.file_reader.get_files_and_folders()
|
||||
|
||||
# Clear existing rows
|
||||
self.table.setRowCount(0)
|
||||
|
||||
# Populate table
|
||||
for file_info in files:
|
||||
row_position = self.table.rowCount()
|
||||
self.table.insertRow(row_position)
|
||||
|
||||
# Name column
|
||||
name_item = QTableWidgetItem(file_info.name)
|
||||
if file_info.is_dir:
|
||||
name_item.setForeground(QColor(0, 0, 255))
|
||||
self.table.setItem(row_position, 0, name_item)
|
||||
|
||||
# Type column
|
||||
type_item = QTableWidgetItem(file_info.file_type)
|
||||
self.table.setItem(row_position, 1, type_item)
|
||||
|
||||
# Size column
|
||||
size_text = "—" if file_info.is_dir else file_info.size_readable
|
||||
size_item = QTableWidgetItem(size_text)
|
||||
size_item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
|
||||
self.table.setItem(row_position, 2, size_item)
|
||||
|
||||
# Modified time column
|
||||
time_item = QTableWidgetItem(file_info.modified_time)
|
||||
self.table.setItem(row_position, 3, time_item)
|
||||
|
||||
self.status_label.setText(
|
||||
f"Total items: {len(files)} | Path: {self.target_path}"
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
self.status_label.setText(f"Error: {str(e)}")
|
||||
|
||||
|
||||
def main():
|
||||
app = QApplication(sys.argv)
|
||||
window = FileSystemBrowser()
|
||||
# Window will be maximized in init_ui, but show() is still needed
|
||||
window.show()
|
||||
sys.exit(app.exec())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
40
src/session_manager.py
Normal file
40
src/session_manager.py
Normal file
@ -0,0 +1,40 @@
|
||||
class SessionManager:
|
||||
_instance = None
|
||||
_current_user = None
|
||||
|
||||
def __new__(cls):
|
||||
if cls._instance is None:
|
||||
cls._instance = super(SessionManager, cls).__new__(cls)
|
||||
return cls._instance
|
||||
|
||||
def set_current_user(self, user_info):
|
||||
self._current_user = user_info
|
||||
|
||||
def get_current_user(self):
|
||||
return self._current_user
|
||||
|
||||
def get_user_id(self):
|
||||
if self._current_user:
|
||||
return self._current_user.get("user_id")
|
||||
return None
|
||||
|
||||
def get_nick_name(self):
|
||||
if self._current_user:
|
||||
return self._current_user.get("nick_name")
|
||||
return None
|
||||
|
||||
def get_dept_id(self):
|
||||
if self._current_user:
|
||||
return self._current_user.get("dept_id")
|
||||
return None
|
||||
|
||||
def get_data_corp(self):
|
||||
if self._current_user:
|
||||
return self._current_user.get("data_corp")
|
||||
return None
|
||||
|
||||
def clear_session(self):
|
||||
self._current_user = None
|
||||
|
||||
def is_logged_in(self):
|
||||
return self._current_user is not None
|
||||
128
src/xml_parser.py
Normal file
128
src/xml_parser.py
Normal file
@ -0,0 +1,128 @@
|
||||
import xml.etree.ElementTree as ET
|
||||
import os
|
||||
|
||||
class XmlParser:
|
||||
def parse_file(self, file_path):
|
||||
if not os.path.exists(file_path):
|
||||
return None, f"文件未找到: {file_path}"
|
||||
|
||||
try:
|
||||
tree = ET.parse(file_path)
|
||||
root = tree.getroot()
|
||||
|
||||
# Metadata from root
|
||||
xml_version = root.get('XMLVersion')
|
||||
xml_creation_datetime = root.get('XMLCreationDateTime')
|
||||
data_source = os.path.basename(file_path)
|
||||
|
||||
# SampleResult (assuming single SampleResult for now based on snippet)
|
||||
sample_result = root.find('SampleResult')
|
||||
if sample_result is None:
|
||||
return None, "没有找到 SampleResult 标签"
|
||||
|
||||
# Common Sample Info
|
||||
sample_info = {
|
||||
"sample_name": sample_result.get('Name'),
|
||||
"operator_name": sample_result.get('OperatorName'),
|
||||
"instrument": sample_result.get('Instrument'),
|
||||
"method_name": sample_result.get('MethodName'),
|
||||
"sample_type": sample_result.get('Type'),
|
||||
"xml_version": xml_version,
|
||||
"xml_creation_datetime": xml_creation_datetime,
|
||||
"data_source": data_source
|
||||
}
|
||||
|
||||
# SampleIDs
|
||||
sample_ids_node = sample_result.find('SampleIDs')
|
||||
if sample_ids_node:
|
||||
for sid in sample_ids_node.findall('SampleID'):
|
||||
id_name = sid.find('IDName').text
|
||||
id_value = sid.find('IDValue').text
|
||||
if id_name == 'Grade ID':
|
||||
sample_info['sample_grade_id'] = id_value
|
||||
elif id_name == 'Grade Alias':
|
||||
sample_info['sample_grade_alias'] = id_value
|
||||
|
||||
extracted_data = []
|
||||
|
||||
# MeasurementReplicates
|
||||
replicates_node = sample_result.find('MeasurementReplicates')
|
||||
if replicates_node:
|
||||
for rep in replicates_node.findall('MeasurementReplicate'):
|
||||
rep_info = sample_info.copy()
|
||||
rep_info['replicate_no'] = rep.get('No')
|
||||
rep_info['measure_datetime'] = rep.get('MeasureDateTime')
|
||||
rep_info['is_deleted'] = rep.get('IsDeleted')
|
||||
|
||||
measurement = rep.find('Measurement')
|
||||
if measurement:
|
||||
rep_info['check_type'] = measurement.get('CheckType')
|
||||
rep_info['check_status'] = measurement.get('CheckStatus')
|
||||
rep_info['grade_name'] = measurement.get('GradeName')
|
||||
rep_info['rsd_check'] = measurement.get('RsdCheck')
|
||||
|
||||
ext_data_meas = measurement.find('ExtData')
|
||||
if ext_data_meas:
|
||||
base_el = ext_data_meas.find('Base')
|
||||
if base_el is not None:
|
||||
rep_info['base_element'] = base_el.text
|
||||
duration_el = ext_data_meas.find('MeasDuration')
|
||||
if duration_el is not None:
|
||||
rep_info['measure_duration'] = duration_el.text
|
||||
|
||||
# Elements
|
||||
elements_node = measurement.find('Elements')
|
||||
if elements_node:
|
||||
for el in elements_node.findall('Element'):
|
||||
el_data = rep_info.copy()
|
||||
el_data['element_name'] = el.get('ElementName')
|
||||
el_data['element_type'] = el.get('Type')
|
||||
el_data['concentration_unit'] = el.get('DefaultConcentrationUnit')
|
||||
|
||||
# Concentration Result
|
||||
conc_res = el.find("./ElementResult[@Kind='Concentration'][@StatType='None']")
|
||||
if conc_res:
|
||||
el_data['concentration_value'] = conc_res.find('ResultValue').text
|
||||
el_data['result_status'] = conc_res.get('Status')
|
||||
el_data['calibration_status'] = conc_res.get('CalibrationStatus')
|
||||
el_data['acceptance_status'] = conc_res.get('AcceptanceStatus')
|
||||
|
||||
# Limits
|
||||
limits = conc_res.find('ResultValueLimits')
|
||||
if limits:
|
||||
lower = limits.find("./ResultValueLimit[@Type='LowerAcceptanceLimit']")
|
||||
upper = limits.find("./ResultValueLimit[@Type='UpperAcceptanceLimit']")
|
||||
el_data['lower_acceptance_limit'] = lower.text if lower is not None else None
|
||||
el_data['upper_acceptance_limit'] = upper.text if upper is not None else None
|
||||
|
||||
# Uncertainty
|
||||
ext_res = conc_res.find('ExtData')
|
||||
if ext_res:
|
||||
unc_abs = ext_res.find("./ResultValue[@SubType='UncertaintyAbs']")
|
||||
unc_rel = ext_res.find("./ResultValue[@SubType='UncertaintyRel']")
|
||||
el_data['uncertainty_abs'] = unc_abs.text if unc_abs is not None else None
|
||||
el_data['uncertainty_rel'] = unc_rel.text if unc_rel is not None else None
|
||||
|
||||
std_res = el.find("./ElementResult[@StatType='StdDev']")
|
||||
if std_res:
|
||||
el_data['std_dev_value'] = std_res.find('ResultValue').text
|
||||
# 如果为空,就用None填充
|
||||
required_keys = [
|
||||
'sample_grade_id', 'sample_grade_alias', 'base_element',
|
||||
'measure_duration', 'check_type', 'check_status', 'grade_name', 'rsd_check',
|
||||
'concentration_value', 'std_dev_value', 'uncertainty_abs',
|
||||
'uncertainty_rel', 'lower_acceptance_limit', 'upper_acceptance_limit',
|
||||
'result_status', 'calibration_status', 'acceptance_status'
|
||||
]
|
||||
for key in required_keys:
|
||||
if key not in el_data:
|
||||
el_data[key] = None
|
||||
|
||||
extracted_data.append(el_data)
|
||||
|
||||
return extracted_data, "Success"
|
||||
|
||||
except Exception as e:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return None, f"Parse error: {str(e)}"
|
||||
11
task.md
Normal file
11
task.md
Normal file
@ -0,0 +1,11 @@
|
||||
# 任务:调整同步数值数据为左对齐
|
||||
## 待完成事项
|
||||
- [x] 修改 `ui/incoming_inspection_page.py` 中的输入框对齐方式为左对齐
|
||||
- [x] 修改 `ui/manual_inspection_page.py` 中的输入框对齐方式为左对齐
|
||||
- [x] 修改 `ui/inspection_card.py` 中的数值显示为左对齐
|
||||
- [x] 验证修改效果
|
||||
- [x] 恢复 `ui/incoming_inspection_page.py` 中的 "检验信息" 标签命名
|
||||
- [x] 调整布局:炉号1后跟检验信息1,炉号2后跟检验信息2,信息输入框跨列显示
|
||||
|
||||
## 当前状态
|
||||
- 入检界面布局已调整为交错式(炉号-信息),且检验信息输入框占据整行剩余空间。
|
||||
0
ui/__init__.py
Normal file
0
ui/__init__.py
Normal file
311
ui/base_page.py
Normal file
311
ui/base_page.py
Normal file
@ -0,0 +1,311 @@
|
||||
from PySide6.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QMessageBox, QDialog, QFileDialog
|
||||
)
|
||||
from PySide6.QtCore import Qt, QSettings
|
||||
import os
|
||||
import glob
|
||||
from src.db_manager import DatabaseManager
|
||||
from src.xml_parser import XmlParser
|
||||
from utils.readMTP import MTPHandler
|
||||
from src.session_manager import SessionManager
|
||||
|
||||
class BaseInspectionPage(QDialog):
|
||||
def __init__(self, title, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setWindowTitle(title)
|
||||
self.setWindowFlags(self.windowFlags() | Qt.Window | Qt.WindowMaximizeButtonHint)
|
||||
|
||||
self.settings = QSettings("Tengzhi", "InspectionApp")
|
||||
self.mtp_handler = MTPHandler()
|
||||
self.session = SessionManager()
|
||||
self.page_type = title # 保存页面类型("入检" 或 "手检")
|
||||
|
||||
self.main_layout = QVBoxLayout(self)
|
||||
self.main_layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.main_layout.setSpacing(0)
|
||||
|
||||
self.create_toolbar()
|
||||
|
||||
# Content Area (to be filled by subclasses)
|
||||
self.content_widget = QWidget()
|
||||
self.content_layout = QVBoxLayout(self.content_widget)
|
||||
self.content_layout.setContentsMargins(20, 20, 20, 20)
|
||||
self.content_layout.setSpacing(15)
|
||||
self.content_layout.setAlignment(Qt.AlignTop)
|
||||
self.main_layout.addWidget(self.content_widget, 1) # Stretch = 1 to fill space
|
||||
|
||||
# Bottom Button Bar
|
||||
self.create_bottom_bar()
|
||||
|
||||
def create_toolbar(self):
|
||||
toolbar = QWidget()
|
||||
toolbar.setStyleSheet("background-color: #f8f8f8; border-bottom: 1px solid #ddd;")
|
||||
layout = QHBoxLayout(toolbar)
|
||||
layout.setContentsMargins(10, 5, 10, 5)
|
||||
|
||||
title_label = QPushButton("⚙ 设置读取路径")
|
||||
title_label.setStyleSheet("""
|
||||
QPushButton {
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
font-family: 'Microsoft YaHei';
|
||||
font-size: 13px;
|
||||
color: #666;
|
||||
text-align: left;
|
||||
}
|
||||
QPushButton:hover {
|
||||
color: #0086fa;
|
||||
}
|
||||
""")
|
||||
title_label.clicked.connect(self.on_settings)
|
||||
|
||||
layout.addWidget(title_label)
|
||||
layout.addStretch()
|
||||
|
||||
self.main_layout.addWidget(toolbar)
|
||||
|
||||
def on_settings(self):
|
||||
from PySide6.QtWidgets import QFormLayout, QLineEdit, QRadioButton, QButtonGroup
|
||||
|
||||
dialog = QDialog(self)
|
||||
dialog.setWindowTitle("同步设置")
|
||||
dialog.setFixedWidth(400)
|
||||
layout = QVBoxLayout(dialog)
|
||||
|
||||
form = QFormLayout()
|
||||
|
||||
# Mode Selection
|
||||
mode_group = QButtonGroup(dialog)
|
||||
current_mode = self.settings.value("sync_mode", "local")
|
||||
|
||||
rb_local = QRadioButton("本地路径 (本地硬盘)")
|
||||
rb_mtp = QRadioButton("MTP设备 (连接的PDA)")
|
||||
mode_group.addButton(rb_local)
|
||||
mode_group.addButton(rb_mtp)
|
||||
|
||||
if current_mode == "local": rb_local.setChecked(True)
|
||||
else: rb_mtp.setChecked(True)
|
||||
|
||||
form.addRow("同步模式:", rb_local)
|
||||
form.addRow("", rb_mtp)
|
||||
|
||||
self.edit_local_path = QLineEdit(self.settings.value("xml_path", os.getcwd()))
|
||||
btn_browse = QPushButton("浏览...")
|
||||
btn_browse.clicked.connect(self.browse_local_path)
|
||||
|
||||
local_path_layout = QHBoxLayout()
|
||||
local_path_layout.addWidget(self.edit_local_path)
|
||||
local_path_layout.addWidget(btn_browse)
|
||||
form.addRow("本地文件夹:", local_path_layout)
|
||||
|
||||
form.addRow(QWidget()) # Spacer
|
||||
|
||||
# MTP Settings
|
||||
self.edit_mtp_device = QLineEdit(self.settings.value("mtp_device", "Trizeps VII"))
|
||||
form.addRow("MTP设备名称:", self.edit_mtp_device)
|
||||
|
||||
self.edit_mtp_path = QLineEdit(self.settings.value("mtp_path", "Storage Card/Program Files/xSort/Results/Analyses"))
|
||||
form.addRow("MTP 内部路径:", self.edit_mtp_path)
|
||||
|
||||
layout.addLayout(form)
|
||||
|
||||
# Save Button
|
||||
btn_save = QPushButton("保存配置")
|
||||
btn_save.setStyleSheet("background-color: #0086fa; color: white; padding: 10px; font-weight: bold;")
|
||||
btn_save.clicked.connect(lambda: self.save_settings(dialog, rb_local.isChecked()))
|
||||
layout.addWidget(btn_save)
|
||||
|
||||
dialog.exec()
|
||||
|
||||
def browse_local_path(self):
|
||||
directory = QFileDialog.getExistingDirectory(self, "选择本地文件夹", self.edit_local_path.text())
|
||||
if directory:
|
||||
self.edit_local_path.setText(directory)
|
||||
|
||||
def save_settings(self, dialog, is_local):
|
||||
self.settings.setValue("sync_mode", "local" if is_local else "mtp")
|
||||
self.settings.setValue("xml_path", self.edit_local_path.text())
|
||||
self.settings.setValue("mtp_device", self.edit_mtp_device.text())
|
||||
self.settings.setValue("mtp_path", self.edit_mtp_path.text())
|
||||
self.show_info("设置成功", "配置已更新。")
|
||||
dialog.accept()
|
||||
|
||||
def create_bottom_bar(self):
|
||||
button_bar = QWidget()
|
||||
button_bar.setStyleSheet("background-color: #e0e0e0; border-top: 1px solid #999;")
|
||||
button_layout = QHBoxLayout(button_bar)
|
||||
button_layout.setContentsMargins(10, 10, 10, 10)
|
||||
button_layout.setSpacing(10)
|
||||
|
||||
btn_style = """
|
||||
QPushButton {
|
||||
background-color: #0086fa;
|
||||
color: white;
|
||||
border-radius: 5px;
|
||||
padding: 15px;
|
||||
font-family: 'Microsoft YaHei';
|
||||
font-size: 19px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #006bbd;
|
||||
}
|
||||
"""
|
||||
|
||||
# Buttons: [Sync, Submit, Print, Close]
|
||||
self.btn_sync = QPushButton("同步")
|
||||
self.btn_sync.setStyleSheet(btn_style)
|
||||
self.btn_sync.clicked.connect(self.on_sync)
|
||||
|
||||
self.btn_submit = QPushButton("提交")
|
||||
self.btn_submit.setStyleSheet(btn_style)
|
||||
self.btn_submit.clicked.connect(self.on_submit)
|
||||
|
||||
self.btn_close = QPushButton("关闭")
|
||||
self.btn_close.setStyleSheet(btn_style)
|
||||
self.btn_close.clicked.connect(self.close)
|
||||
|
||||
button_layout.addWidget(self.btn_sync)
|
||||
button_layout.addWidget(self.btn_submit)
|
||||
button_layout.addWidget(self.btn_close)
|
||||
|
||||
self.main_layout.addWidget(button_bar)
|
||||
|
||||
def on_sync(self):
|
||||
sync_mode = self.settings.value("sync_mode", "local")
|
||||
|
||||
xml_path = None
|
||||
target_xml_name = ""
|
||||
|
||||
if sync_mode == "local":
|
||||
xml_dir = self.settings.value("xml_path", "xml_data")
|
||||
if not os.path.exists(xml_dir):
|
||||
self.show_info("同步失败", f"本地路径不存在: {xml_dir}")
|
||||
return
|
||||
xml_files = glob.glob(os.path.join(xml_dir, "*.xml"))
|
||||
if not xml_files:
|
||||
self.show_info("同步失败", "本地目录下未找到 .xml 文件")
|
||||
return
|
||||
latest_xml = max(xml_files, key=os.path.getmtime)
|
||||
xml_path = latest_xml
|
||||
target_xml_name = os.path.basename(latest_xml)
|
||||
else:
|
||||
device_name = self.settings.value("mtp_device", "Trizeps VII")
|
||||
remote_path = self.settings.value("mtp_path", "")
|
||||
|
||||
self.show_info("正在同步", f"正在从设备 [{device_name}] 读取最新数据...")
|
||||
|
||||
local_cache, filename = self.mtp_handler.get_latest_xml_windows(device_name, remote_path)
|
||||
if not local_cache:
|
||||
self.show_info("同步失败", f"MTP同步错误: {filename}")
|
||||
return
|
||||
|
||||
xml_path = local_cache
|
||||
target_xml_name = filename
|
||||
|
||||
# 1.解析xml
|
||||
parser = XmlParser()
|
||||
parsed_data, msg = parser.parse_file(xml_path)
|
||||
|
||||
if not parsed_data:
|
||||
self.show_info("同步失败", f"XML解析错误: {msg}")
|
||||
return
|
||||
|
||||
# 2. 写入数据
|
||||
db = DatabaseManager()
|
||||
success, db_msg = db.insert_analysis_data(parsed_data)
|
||||
|
||||
if not success:
|
||||
self.show_info("数据库错误", f"插入失败: {db_msg}\n(请检查数据库连接)")
|
||||
return
|
||||
|
||||
latest_data = db.get_latest_sample_data()
|
||||
if latest_data:
|
||||
self.update_ui_with_data(latest_data)
|
||||
self.show_info("同步成功", f"已同步 {len(parsed_data)} 条元素数据。\n源文件: {target_xml_name}")
|
||||
else:
|
||||
self.show_info("同步异常", "数据插入成功但无法读取回显。")
|
||||
|
||||
def update_ui_with_data(self, data):
|
||||
### 更新UI界面,如果不符合,对不符合的元素字体标红,并且
|
||||
pass
|
||||
|
||||
def clear_data(self):
|
||||
"""清空界面所有数据(子类实现)"""
|
||||
pass
|
||||
|
||||
def on_submit(self):
|
||||
"""提交检验数据到 e_pz_hjqy_import 表"""
|
||||
try:
|
||||
# 1. 收集表单数据
|
||||
gch = self.inputs.get("batch_no").text().strip() if self.inputs.get("batch_no") else ""
|
||||
heat_number = self.inputs.get("material1").text().strip() if self.inputs.get("material1") else ""
|
||||
heat_number2 = self.inputs.get("info1").text().strip() if self.inputs.get("info1") else ""
|
||||
cz = self.inputs.get("spec1").text().strip() if self.inputs.get("spec1") else ""
|
||||
cz2 = self.inputs.get("spec2").text().strip() if self.inputs.get("spec2") else ""
|
||||
check_information = self.inputs.get("insp_info_1").text().strip() if self.inputs.get("insp_info_1") else ""
|
||||
check_information2 = self.inputs.get("insp_info_2").text().strip() if self.inputs.get("insp_info_2") else ""
|
||||
# 产地/材质 拼接显示时,提交只需要产地 (cd)
|
||||
display_cd = self.inputs.get("material").text().strip() if self.inputs.get("material") else ""
|
||||
cd = display_cd.split('/')[0] if '/' in display_cd else display_cd
|
||||
size = self.inputs.get("spec").text().strip() if self.inputs.get("spec") else ""
|
||||
|
||||
# 2. 验证必填字段
|
||||
if not gch:
|
||||
self.show_info("提交失败", "工程号不能为空")
|
||||
return
|
||||
|
||||
# 3. 收集所有元素数据
|
||||
element_names = ["C", "Si", "Mn", "P", "S", "Cr", "Ni", "Mo", "Cu", "Ti", "Nb", "V", "Al", "W", "Co", "N", "Fe", "Se"]
|
||||
elements = {}
|
||||
|
||||
for elem in element_names:
|
||||
if elem in self.inputs:
|
||||
value_str = self.inputs[elem].text().strip()
|
||||
if value_str:
|
||||
try:
|
||||
elements[elem] = float(value_str)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# 4. 取当前用户信息
|
||||
user_id = self.session.get_user_id()
|
||||
dept_id = self.session.get_dept_id()
|
||||
data_corp = self.session.get_data_corp()
|
||||
|
||||
if not user_id:
|
||||
self.show_info("提交失败", "未获取到用户信息,请重新登录")
|
||||
return
|
||||
|
||||
# 5. 构建提交数据
|
||||
inspection_data = {
|
||||
"gch": gch,
|
||||
"heat_number": heat_number,
|
||||
"heat_number2": heat_number2,
|
||||
"cz": cz,
|
||||
"cz2": cz2,
|
||||
"check_information": check_information,
|
||||
"check_information2": check_information2,
|
||||
"cd": cd,
|
||||
"size": size,
|
||||
"elements": elements,
|
||||
"user_id": user_id,
|
||||
"dept_id": dept_id,
|
||||
"data_corp": data_corp
|
||||
}
|
||||
|
||||
# 6. 插入数据库
|
||||
db = DatabaseManager()
|
||||
success, message = db.insert_inspection_data(inspection_data)
|
||||
|
||||
if success:
|
||||
self.show_info("提交成功", message)
|
||||
self.clear_data()
|
||||
else:
|
||||
self.show_info("提交失败", message)
|
||||
|
||||
except Exception as e:
|
||||
self.show_info("提交异常", f"发生错误: {str(e)}")
|
||||
|
||||
def show_info(self, title, message):
|
||||
QMessageBox.information(self, title, message)
|
||||
352
ui/incoming_inspection_page.py
Normal file
352
ui/incoming_inspection_page.py
Normal file
@ -0,0 +1,352 @@
|
||||
from PySide6.QtWidgets import (
|
||||
QWidget, QTableWidget, QTableWidgetItem, QHeaderView, QLineEdit, QAbstractItemView
|
||||
)
|
||||
from PySide6.QtCore import Qt, QTimer
|
||||
from openpyxl.styles.builtins import headline_1
|
||||
|
||||
from ui.base_page import BaseInspectionPage
|
||||
from src.db_manager import DatabaseManager
|
||||
|
||||
class IncomingInspectionPage(BaseInspectionPage):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__("入检", parent)
|
||||
self.inputs = {}
|
||||
self.db_manager = DatabaseManager()
|
||||
self.scan_timer = QTimer()
|
||||
self.scan_timer.setSingleShot(True)
|
||||
self.scan_timer.timeout.connect(self.query_data)
|
||||
self.last_gch = ""
|
||||
self.init_ui()
|
||||
|
||||
def init_ui(self):
|
||||
self.content_layout.setContentsMargins(5, 5, 5, 5)
|
||||
|
||||
# Initialize Table - 使用4列
|
||||
self.table = QTableWidget()
|
||||
self.table.setColumnCount(4)
|
||||
self.table.verticalHeader().setVisible(False)
|
||||
self.table.horizontalHeader().setVisible(False)
|
||||
|
||||
# Style
|
||||
self.table.setStyleSheet("""
|
||||
QTableWidget {
|
||||
border: 1px solid #999;
|
||||
gridline-color: #999;
|
||||
background-color: white;
|
||||
font-family: 'Microsoft YaHei';
|
||||
font-size: 18px;
|
||||
}
|
||||
QTableWidget::item {
|
||||
padding: 5px;
|
||||
}
|
||||
QLineEdit {
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
font-family: 'Microsoft YaHei';
|
||||
font-size: 18px;
|
||||
}
|
||||
""")
|
||||
|
||||
header = self.table.horizontalHeader()
|
||||
header.setSectionResizeMode(QHeaderView.Stretch)
|
||||
|
||||
self.table.setSelectionMode(QAbstractItemView.NoSelection)
|
||||
self.table.setFocusPolicy(Qt.NoFocus)
|
||||
self.content_layout.addWidget(self.table)
|
||||
self.render_form()
|
||||
|
||||
def render_form(self):
|
||||
# 1. 第一行 - 工程号
|
||||
self.add_row_label_input("工程号", "batch_no", span_input=3)
|
||||
self.inputs["batch_no"].setPlaceholderText("扫描/输入")
|
||||
self.inputs["batch_no"].returnPressed.connect(self.query_data)
|
||||
self.inputs["batch_no"].textChanged.connect(self.on_text_changed)
|
||||
|
||||
# 2. 第二行 - 产地和规格
|
||||
row = self.table.rowCount()
|
||||
self.table.insertRow(row)
|
||||
self.table.setRowHeight(row, 50)
|
||||
self.set_label_cell(row, 0, "产地")
|
||||
self.table.setCellWidget(row, 1, self.create_input("material"))
|
||||
self.set_label_cell(row, 2, "规格")
|
||||
self.table.setCellWidget(row, 3, self.create_input("spec"))
|
||||
|
||||
# 3. 分组标题 - 第一组
|
||||
self.add_group_header("━ 第一组 ━")
|
||||
|
||||
# 4. 炉号和材质(第一组)
|
||||
row = self.table.rowCount()
|
||||
self.table.insertRow(row)
|
||||
self.table.setRowHeight(row, 50)
|
||||
self.set_label_cell(row, 0, "炉号", align_left=True)
|
||||
inp1 = self.create_input("material1")
|
||||
self.table.setCellWidget(row, 1, inp1)
|
||||
self.set_label_cell(row, 2, "材质")
|
||||
inp2 = self.create_input("spec1")
|
||||
self.table.setCellWidget(row, 3, inp2)
|
||||
|
||||
# 5. 检验信息(第一组)
|
||||
self.add_row_label_input("检验信息", "insp_info_1", span_input=3, align_left=True)
|
||||
|
||||
# 6. 分组标题 - 第二组
|
||||
self.add_group_header("━ 第二组 ━")
|
||||
|
||||
# 7. 炉号和材质(第二组)
|
||||
row = self.table.rowCount()
|
||||
self.table.insertRow(row)
|
||||
self.table.setRowHeight(row, 50)
|
||||
self.set_label_cell(row, 0, "炉号", align_left=True)
|
||||
inp3 = self.create_input("info1")
|
||||
self.table.setCellWidget(row, 1, inp3)
|
||||
self.set_label_cell(row, 2, "材质")
|
||||
inp4 = self.create_input("spec2")
|
||||
self.table.setCellWidget(row, 3, inp4)
|
||||
|
||||
# 8. 检验信息(第二组)
|
||||
self.add_row_label_input("检验信息", "insp_info_2", span_input=3, align_left=True)
|
||||
|
||||
self.add_group_header("━ 检验值 ━")
|
||||
# 9. 元素行 - 固定顺序显示所有18个元素
|
||||
element_order = ["C", "Si", "Mn", "P", "S", "Cr", "Ni", "Mo", "Cu", "Ti", "Nb", "V", "Al", "W", "Co", "N", "Fe", "Se"]
|
||||
for i in range(0, len(element_order), 2):
|
||||
name1 = element_order[i]
|
||||
name2 = element_order[i+1] if i + 1 < len(element_order) else None
|
||||
|
||||
row = self.table.rowCount()
|
||||
self.table.insertRow(row)
|
||||
self.table.setRowHeight(row, 50)
|
||||
|
||||
self.set_label_cell(row, 0, name1)
|
||||
self.table.setCellWidget(row, 1, self.create_input(name1))
|
||||
|
||||
if name2:
|
||||
self.set_label_cell(row, 2, name2)
|
||||
self.table.setCellWidget(row, 3, self.create_input(name2))
|
||||
|
||||
def add_row_label_input(self, label_text, key, span_input=1, bg_color=None, group_col=True, align_left=False):
|
||||
row = self.table.rowCount()
|
||||
self.table.insertRow(row)
|
||||
self.table.setRowHeight(row, 50)
|
||||
|
||||
if not group_col:
|
||||
# 不需要分组列,第一列空白
|
||||
self.set_empty_cell(row, 0)
|
||||
self.set_label_cell(row, 1, label_text, bg_color=bg_color, align_left=align_left)
|
||||
inp = self.create_input(key)
|
||||
self.table.setCellWidget(row, 2, inp)
|
||||
if span_input > 1:
|
||||
self.table.setSpan(row, 2, 1, span_input)
|
||||
else:
|
||||
self.set_label_cell(row, 0, label_text, bg_color=bg_color, align_left=align_left)
|
||||
inp = self.create_input(key)
|
||||
self.table.setCellWidget(row, 1, inp)
|
||||
if span_input > 1:
|
||||
self.table.setSpan(row, 1, 1, span_input)
|
||||
|
||||
if bg_color:
|
||||
col_start = 2 if not group_col else 1
|
||||
for col in range(col_start, col_start + span_input):
|
||||
self.set_cell_background(row, col, bg_color)
|
||||
|
||||
def set_group_cell(self, row, col, text, rowspan=1, bg_color=None):
|
||||
"""设置分组标识单元格"""
|
||||
item = QTableWidgetItem(text)
|
||||
item.setTextAlignment(Qt.AlignCenter)
|
||||
item.setFlags(Qt.ItemIsEnabled)
|
||||
from PySide6.QtGui import QColor, QBrush, QFont
|
||||
if bg_color:
|
||||
brush = QBrush(QColor(bg_color))
|
||||
else:
|
||||
brush = QBrush(QColor("#f5f5f5"))
|
||||
item.setBackground(brush)
|
||||
font = QFont()
|
||||
font.setBold(True)
|
||||
font.setPointSize(16)
|
||||
item.setFont(font)
|
||||
self.table.setItem(row, col, item)
|
||||
if rowspan > 1:
|
||||
self.table.setSpan(row, col, rowspan, 1)
|
||||
|
||||
def set_empty_cell(self, row, col):
|
||||
"""设置空白单元格"""
|
||||
item = QTableWidgetItem("")
|
||||
item.setFlags(Qt.ItemIsEnabled)
|
||||
self.table.setItem(row, col, item)
|
||||
|
||||
def add_group_header(self, text):
|
||||
"""添加分组标题行"""
|
||||
row = self.table.rowCount()
|
||||
self.table.insertRow(row)
|
||||
self.table.setRowHeight(row, 40)
|
||||
|
||||
item = QTableWidgetItem(text)
|
||||
item.setTextAlignment(Qt.AlignCenter)
|
||||
item.setFlags(Qt.ItemIsEnabled)
|
||||
from PySide6.QtGui import QColor, QBrush, QFont
|
||||
brush = QBrush(QColor("#f0f0f0"))
|
||||
item.setBackground(brush)
|
||||
font = QFont()
|
||||
font.setBold(True)
|
||||
font.setPointSize(14)
|
||||
item.setFont(font)
|
||||
self.table.setItem(row, 0, item)
|
||||
self.table.setSpan(row, 0, 1, 4)
|
||||
|
||||
def set_cell_background(self, row, col, color):
|
||||
"""设置单元格背景色"""
|
||||
item = self.table.item(row, col)
|
||||
if not item:
|
||||
item = QTableWidgetItem("")
|
||||
self.table.setItem(row, col, item)
|
||||
from PySide6.QtGui import QColor, QBrush
|
||||
brush = QBrush(QColor(color))
|
||||
item.setBackground(brush)
|
||||
|
||||
def set_label_cell(self, row, col, text, bg_color=None, align_left=False):
|
||||
item = QTableWidgetItem(text)
|
||||
# 根据参数设置对齐方式
|
||||
if align_left:
|
||||
item.setTextAlignment(Qt.AlignLeft | Qt.AlignVCenter)
|
||||
else:
|
||||
item.setTextAlignment(Qt.AlignCenter)
|
||||
item.setFlags(Qt.ItemIsEnabled)
|
||||
from PySide6.QtGui import QColor, QBrush
|
||||
if bg_color:
|
||||
brush = QBrush(QColor(bg_color))
|
||||
else:
|
||||
brush = QBrush(QColor("#f5f5f5"))
|
||||
item.setBackground(brush)
|
||||
self.table.setItem(row, col, item)
|
||||
|
||||
def create_input(self, key):
|
||||
inp = QLineEdit()
|
||||
if key in self.inputs:
|
||||
key = f"{key}_2"
|
||||
self.inputs[key] = inp
|
||||
return inp
|
||||
|
||||
def on_text_changed(self, text):
|
||||
"""PDA扫码输入检测,延时300ms后触发查询"""
|
||||
self.scan_timer.stop()
|
||||
if text.strip() and text.strip() != self.last_gch:
|
||||
self.scan_timer.start(300)
|
||||
|
||||
def query_data(self):
|
||||
self.scan_timer.stop()
|
||||
gch = self.inputs["batch_no"].text().strip()
|
||||
if not gch or gch == self.last_gch:
|
||||
return
|
||||
|
||||
self.last_gch = gch
|
||||
data, err = self.db_manager.query_by_gch(gch)
|
||||
if err:
|
||||
self.show_info("查询失败", err)
|
||||
return
|
||||
|
||||
self.inputs["material"].setText(data.get("cd") or "")
|
||||
self.inputs["spec"].setText(data.get("size") or "")
|
||||
self.inputs["material1"].setText(data.get("heat_number") or "")
|
||||
self.inputs["spec1"].setText(data.get("qy_czt") or "")
|
||||
self.inputs["info1"].setText(data.get("heat_number2") or "")
|
||||
self.inputs["spec2"].setText(data.get("qy_czw") or "")
|
||||
|
||||
def update_ui_with_data(self, data):
|
||||
"""
|
||||
根据数据库数据更新UI。
|
||||
"""
|
||||
elements = data.get("elements", {})
|
||||
|
||||
# 固定元素顺序
|
||||
element_order = ["C", "Si", "Mn", "P", "S", "Cr", "Ni", "Mo", "Cu", "Ti", "Nb", "V", "Al", "W", "Co", "N", "Fe", "Se"]
|
||||
|
||||
# 更新所有元素值,没有的显示为0
|
||||
for el in element_order:
|
||||
if el in self.inputs:
|
||||
value = "0" if (v := float(elements.get(el, 0))) == 0 else f"{v:.6f}"
|
||||
self.inputs[el].setText(str(value))
|
||||
|
||||
# 比对标准值范围
|
||||
self.validate_elements(elements)
|
||||
|
||||
def clear_data(self):
|
||||
"""清空所有输入框数据"""
|
||||
for inp in self.inputs.values():
|
||||
inp.clear()
|
||||
self.last_gch = ""
|
||||
|
||||
def validate_elements(self, elements):
|
||||
"""比对元素值与标准范围,不符合的写入检验信息框"""
|
||||
heat_number = self.inputs.get("material1")
|
||||
heat_number2 = self.inputs.get("info1")
|
||||
|
||||
if not heat_number or not heat_number2:
|
||||
return
|
||||
|
||||
hn1 = heat_number.text().strip()
|
||||
hn2 = heat_number2.text().strip()
|
||||
|
||||
if not hn1 and not hn2:
|
||||
return
|
||||
|
||||
heat1_range, heat2_range, err = self.db_manager.query_standard_range(hn1, hn2)
|
||||
if err:
|
||||
self.show_info("标准值查询失败", err)
|
||||
return
|
||||
|
||||
# 元素名到数据库字段的映射 (18个元素)
|
||||
element_map = {
|
||||
"C": "c", "Si": "si", "Mn": "mn", "P": "p", "S": "s",
|
||||
"Cr": "cr", "Ni": "ni", "Mo": "mo", "Cu": "cu", "Ti": "ti",
|
||||
"Nb": "nb", "V": "v", "Al": "al", "W": "w", "Co": "co",
|
||||
"N": "n", "Fe": "fe", "Se": "se"
|
||||
}
|
||||
|
||||
def check_range(std_range, elements_data):
|
||||
"""检查元素是否在范围内,返回不符合的列表"""
|
||||
out_of_range = []
|
||||
if not std_range:
|
||||
return out_of_range
|
||||
|
||||
for el_name, db_field in element_map.items():
|
||||
if el_name not in elements_data:
|
||||
continue
|
||||
|
||||
try:
|
||||
value = float(elements_data[el_name])
|
||||
except (ValueError, TypeError):
|
||||
continue
|
||||
|
||||
min_key = f"{db_field}min"
|
||||
max_key = f"{db_field}max"
|
||||
min_val = std_range.get(min_key)
|
||||
max_val = std_range.get(max_key)
|
||||
|
||||
if min_val is None and max_val is None:
|
||||
continue
|
||||
try:
|
||||
|
||||
min_val = float(min_val) if min_val is not None and min_val != '' else None
|
||||
max_val = float(max_val) if max_val is not None and max_val != '' else None
|
||||
except (ValueError, TypeError) as e:
|
||||
print(e)
|
||||
is_out = False
|
||||
if min_val is not None and value < min_val:
|
||||
is_out = True
|
||||
if max_val is not None and value > max_val:
|
||||
is_out = True
|
||||
|
||||
if is_out:
|
||||
min_str = str(min_val) if min_val is not None else ""
|
||||
max_str = str(max_val) if max_val is not None else ""
|
||||
out_of_range.append(f"{el_name}({min_str}-{max_str})")
|
||||
|
||||
return out_of_range
|
||||
|
||||
def get_info_text(std_range):
|
||||
if not std_range:
|
||||
return "无标准值"
|
||||
out = check_range(std_range, elements)
|
||||
return "/".join(out) + "/" if out else ""
|
||||
|
||||
self.inputs["insp_info_1"].setText(get_info_text(heat1_range))
|
||||
self.inputs["insp_info_2"].setText(get_info_text(heat2_range))
|
||||
90
ui/inspection_card.py
Normal file
90
ui/inspection_card.py
Normal file
@ -0,0 +1,90 @@
|
||||
from PySide6.QtWidgets import QFrame, QLabel, QGridLayout
|
||||
from PySide6.QtCore import Qt
|
||||
from PySide6.QtGui import QFont
|
||||
|
||||
class InspectionCard(QFrame):
|
||||
def __init__(self, data, parent=None):
|
||||
super().__init__(parent)
|
||||
self.data = data
|
||||
self.init_ui()
|
||||
|
||||
def init_ui(self):
|
||||
self.setFrameShape(QFrame.Box)
|
||||
self.setLineWidth(1)
|
||||
# Black border
|
||||
self.setStyleSheet("""
|
||||
InspectionCard {
|
||||
background-color: white;
|
||||
border: 1px solid black;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
QLabel {
|
||||
font-family: 'Microsoft YaHei';
|
||||
font-size: 16px;
|
||||
padding-left: 8px;
|
||||
padding-right: 5px;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
color: black;
|
||||
qproperty-alignment: 'AlignLeft | AlignVCenter';
|
||||
}
|
||||
.HeaderLabel {
|
||||
font-weight: bold;
|
||||
qproperty-alignment: 'AlignCenter';
|
||||
padding-left: 5px;
|
||||
}
|
||||
""")
|
||||
|
||||
layout = QGridLayout(self)
|
||||
layout.setSpacing(0)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
cell_style = "border: 1px solid black; padding: 4px;"
|
||||
|
||||
# Function to add cell
|
||||
def add_cell(text, row, col, row_span=1, col_span=1, is_header=False):
|
||||
lbl = QLabel(text)
|
||||
if is_header:
|
||||
lbl.setAlignment(Qt.AlignCenter)
|
||||
lbl.setProperty("class", "HeaderLabel")
|
||||
lbl.setStyleSheet(cell_style + "font-weight: bold;")
|
||||
else:
|
||||
lbl.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
|
||||
lbl.setStyleSheet(cell_style)
|
||||
layout.addWidget(lbl, row, col, row_span, col_span)
|
||||
|
||||
add_cell("日期", 0, 0, 1, 1, True)
|
||||
add_cell(self.data.get("date", ""), 0, 1, 1, 3)
|
||||
add_cell("检验员", 0, 4, 1, 1, True)
|
||||
add_cell(self.data.get("inspector", ""), 0, 5, 1, 1)
|
||||
|
||||
add_cell("工程号", 1, 0, 1, 1, True)
|
||||
add_cell(self.data.get("batch_no", ""), 1, 1, 1, 3)
|
||||
add_cell("规格", 1, 4, 1, 1, True)
|
||||
add_cell(self.data.get("spec", ""), 1, 5, 1, 1)
|
||||
|
||||
add_cell("炉号1", 2, 0, 1, 1, True)
|
||||
add_cell(self.data.get("heat_number", ""), 2, 1, 1, 2)
|
||||
add_cell("材质1", 2, 3, 1, 1, True)
|
||||
add_cell(self.data.get("cz", ""), 2, 4, 1, 2)
|
||||
|
||||
add_cell("炉号2", 3, 0, 1, 1, True)
|
||||
add_cell(self.data.get("heat_number2", ""), 3, 1, 1, 2)
|
||||
add_cell("材质2", 3, 3, 1, 1, True)
|
||||
add_cell(self.data.get("cz2", ""), 3, 4, 1, 2)
|
||||
|
||||
layout.setSpacing(0)
|
||||
|
||||
for i in range(layout.count()):
|
||||
widget = layout.itemAt(i).widget()
|
||||
r, c, rs, cs = layout.getItemPosition(i)
|
||||
|
||||
style = "border-right: 1px solid black; border-bottom: 1px solid black; padding: 4px;"
|
||||
if r == 0:
|
||||
style += "border-top: 1px solid black;"
|
||||
if c == 0:
|
||||
style += "border-left: 1px solid black;"
|
||||
|
||||
if widget.property("class") == "HeaderLabel":
|
||||
style += "font-weight: bold;"
|
||||
|
||||
widget.setStyleSheet(style)
|
||||
172
ui/inspection_window.py
Normal file
172
ui/inspection_window.py
Normal file
@ -0,0 +1,172 @@
|
||||
from PySide6.QtWidgets import (
|
||||
QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
|
||||
QScrollArea, QPushButton, QMessageBox, QLabel, QScroller
|
||||
)
|
||||
from PySide6.QtCore import Qt, QTimer
|
||||
from ui.inspection_card import InspectionCard
|
||||
from ui.incoming_inspection_page import IncomingInspectionPage
|
||||
from ui.manual_inspection_page import ManualInspectionPage
|
||||
from src.db_manager import DatabaseManager
|
||||
from src.session_manager import SessionManager
|
||||
|
||||
class InspectionWindow(QMainWindow):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setWindowTitle("检验管理系统")
|
||||
|
||||
# 初始化数据库管理器和分页参数
|
||||
self.db_manager = DatabaseManager()
|
||||
self.session_manager = SessionManager()
|
||||
self.current_page = 0
|
||||
self.page_size = 10
|
||||
self.is_loading = False
|
||||
self.has_more_data = True
|
||||
|
||||
# Central Widget
|
||||
central_widget = QWidget()
|
||||
self.setCentralWidget(central_widget)
|
||||
|
||||
# Main Layout
|
||||
main_layout = QVBoxLayout(central_widget)
|
||||
main_layout.setContentsMargins(0, 0, 0, 0)
|
||||
main_layout.setSpacing(0)
|
||||
|
||||
# Scroll Area for Cards
|
||||
self.scroll_area = QScrollArea()
|
||||
self.scroll_area.setWidgetResizable(True)
|
||||
self.scroll_area.setStyleSheet("background-color: #f0f0f0; border: none;")
|
||||
|
||||
self.cards_container = QWidget()
|
||||
self.cards_container.setStyleSheet("background-color: white;")
|
||||
self.cards_layout = QVBoxLayout(self.cards_container)
|
||||
self.cards_layout.setContentsMargins(10, 10, 10, 10)
|
||||
self.cards_layout.setSpacing(15)
|
||||
|
||||
# 加载提示标签
|
||||
self.loading_label = QLabel("加载中...")
|
||||
self.loading_label.setAlignment(Qt.AlignCenter)
|
||||
self.loading_label.setStyleSheet("color: #666; padding: 10px;")
|
||||
self.loading_label.hide()
|
||||
self.cards_layout.addWidget(self.loading_label)
|
||||
|
||||
self.cards_layout.addStretch() # Push items to top
|
||||
|
||||
self.scroll_area.setWidget(self.cards_container)
|
||||
|
||||
# 启用触摸滚动
|
||||
QScroller.grabGesture(self.scroll_area.viewport(), QScroller.LeftMouseButtonGesture)
|
||||
|
||||
# 连接滚动条信号
|
||||
self.scroll_area.verticalScrollBar().valueChanged.connect(self.on_scroll)
|
||||
|
||||
main_layout.addWidget(self.scroll_area)
|
||||
|
||||
button_bar = QWidget()
|
||||
button_bar.setStyleSheet("background-color: #e0e0e0; border-top: 1px solid #999;")
|
||||
button_layout = QHBoxLayout(button_bar)
|
||||
button_layout.setContentsMargins(20, 15, 20, 15)
|
||||
button_layout.setSpacing(20)
|
||||
|
||||
# 按钮样式
|
||||
btn_style = """
|
||||
QPushButton {
|
||||
background-color: #0086fa;
|
||||
color: white;
|
||||
border-radius: 5px;
|
||||
padding: 15px;
|
||||
font-family: 'Microsoft YaHei';
|
||||
font-size: 19px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #006bbd;
|
||||
}
|
||||
"""
|
||||
|
||||
self.btn_incoming = QPushButton("入检")
|
||||
self.btn_incoming.setStyleSheet(btn_style)
|
||||
self.btn_incoming.clicked.connect(lambda: self.show_dialog("入检"))
|
||||
|
||||
self.btn_manual = QPushButton("手检")
|
||||
self.btn_manual.setStyleSheet(btn_style)
|
||||
self.btn_manual.clicked.connect(lambda: self.show_dialog("手检"))
|
||||
|
||||
button_layout.addWidget(self.btn_incoming)
|
||||
button_layout.addWidget(self.btn_manual)
|
||||
|
||||
main_layout.addWidget(button_bar)
|
||||
|
||||
def add_card(self, data):
|
||||
card = InspectionCard(data)
|
||||
count = self.cards_layout.count()
|
||||
# 插入到 loading_label 之前
|
||||
self.cards_layout.insertWidget(count - 2, card)
|
||||
|
||||
def on_scroll(self, value):
|
||||
"""滚动条事件处理"""
|
||||
scrollbar = self.scroll_area.verticalScrollBar()
|
||||
# 当滚动到底部附近时触发加载
|
||||
if value >= scrollbar.maximum() - 50 and not self.is_loading and self.has_more_data:
|
||||
self.load_more_data()
|
||||
|
||||
def load_more_data(self):
|
||||
"""加载更多数据"""
|
||||
if self.is_loading or not self.has_more_data:
|
||||
return
|
||||
|
||||
self.is_loading = True
|
||||
self.loading_label.show()
|
||||
|
||||
# 使用 QTimer 异步加载,避免阻塞 UI
|
||||
QTimer.singleShot(100, self._fetch_data)
|
||||
|
||||
def _fetch_data(self):
|
||||
"""从数据库获取数据"""
|
||||
offset = self.current_page * self.page_size
|
||||
user_id = self.session_manager.get_user_id() or ''
|
||||
rows, error = self.db_manager.get_inspection_list(self.page_size, offset, user_id=user_id)
|
||||
|
||||
if error:
|
||||
self.loading_label.setText(f"加载失败: {error}")
|
||||
self.is_loading = False
|
||||
QTimer.singleShot(2000, lambda: self.loading_label.hide())
|
||||
return
|
||||
|
||||
if not rows or len(rows) == 0:
|
||||
self.has_more_data = False
|
||||
self.loading_label.setText("没有更多数据")
|
||||
QTimer.singleShot(1000, lambda: self.loading_label.hide())
|
||||
else:
|
||||
for row in rows:
|
||||
self.add_card(dict(row))
|
||||
|
||||
self.current_page += 1
|
||||
|
||||
if len(rows) < self.page_size:
|
||||
self.has_more_data = False
|
||||
|
||||
self.loading_label.hide()
|
||||
|
||||
self.is_loading = False
|
||||
|
||||
def load_initial_data(self):
|
||||
"""加载初始数据"""
|
||||
self.current_page = 0
|
||||
self.has_more_data = True
|
||||
self.load_more_data()
|
||||
|
||||
def show_dialog(self, title):
|
||||
if title == "入检":
|
||||
page = IncomingInspectionPage(self)
|
||||
page.showMaximized()
|
||||
page.exec()
|
||||
elif title == "手检":
|
||||
page = ManualInspectionPage(self)
|
||||
page.showMaximized()
|
||||
page.exec()
|
||||
else:
|
||||
msg = QMessageBox(self)
|
||||
msg.setWindowTitle(title)
|
||||
msg.setText(f"{title} - 功能开发中")
|
||||
msg.setStandardButtons(QMessageBox.Ok)
|
||||
msg.exec()
|
||||
128
ui/login_window.py
Normal file
128
ui/login_window.py
Normal file
@ -0,0 +1,128 @@
|
||||
from PySide6.QtWidgets import (
|
||||
QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
|
||||
QLabel, QLineEdit, QPushButton, QMessageBox
|
||||
)
|
||||
from PySide6.QtCore import Qt, Signal
|
||||
from PySide6.QtGui import QFont
|
||||
import hashlib
|
||||
from src.db_manager import DatabaseManager
|
||||
|
||||
|
||||
class LoginWindow(QMainWindow):
|
||||
login_success = Signal(dict)
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setWindowTitle("检验管理系统 - 登录")
|
||||
self.db = DatabaseManager()
|
||||
self.init_ui()
|
||||
|
||||
def init_ui(self):
|
||||
central_widget = QWidget()
|
||||
self.setCentralWidget(central_widget)
|
||||
|
||||
main_layout = QVBoxLayout(central_widget)
|
||||
main_layout.setContentsMargins(60, 80, 60, 80)
|
||||
main_layout.setSpacing(40)
|
||||
|
||||
title_label = QLabel("检验管理系统")
|
||||
title_label.setAlignment(Qt.AlignCenter)
|
||||
title_label.setStyleSheet("""
|
||||
font-family: 'Microsoft YaHei';
|
||||
font-size: 32px;
|
||||
font-weight: bold;
|
||||
color: #0086fa;
|
||||
padding: 20px;
|
||||
""")
|
||||
main_layout.addWidget(title_label)
|
||||
|
||||
form_layout = QVBoxLayout()
|
||||
form_layout.setSpacing(15)
|
||||
|
||||
account_label = QLabel("账号")
|
||||
account_label.setStyleSheet("font-family: 'Microsoft YaHei'; font-size: 18px; color: #333; padding: 5px;")
|
||||
self.account_input = QLineEdit()
|
||||
self.account_input.setPlaceholderText("请输入账号")
|
||||
self.account_input.setMinimumHeight(50)
|
||||
self.account_input.setStyleSheet("""
|
||||
QLineEdit {
|
||||
font-family: 'Microsoft YaHei';
|
||||
font-size: 16px;
|
||||
padding: 12px;
|
||||
border: 2px solid #ccc;
|
||||
border-radius: 5px;
|
||||
}
|
||||
QLineEdit:focus {
|
||||
border: 2px solid #0086fa;
|
||||
}
|
||||
""")
|
||||
|
||||
password_label = QLabel("密码")
|
||||
password_label.setStyleSheet("font-family: 'Microsoft YaHei'; font-size: 18px; color: #333; padding: 5px;")
|
||||
self.password_input = QLineEdit()
|
||||
self.password_input.setPlaceholderText("请输入密码")
|
||||
self.password_input.setEchoMode(QLineEdit.Password)
|
||||
self.password_input.setMinimumHeight(50)
|
||||
self.password_input.setStyleSheet("""
|
||||
QLineEdit {
|
||||
font-family: 'Microsoft YaHei';
|
||||
font-size: 16px;
|
||||
padding: 12px;
|
||||
border: 2px solid #ccc;
|
||||
border-radius: 5px;
|
||||
}
|
||||
QLineEdit:focus {
|
||||
border: 2px solid #0086fa;
|
||||
}
|
||||
""")
|
||||
self.password_input.returnPressed.connect(self.on_login)
|
||||
|
||||
form_layout.addWidget(account_label)
|
||||
form_layout.addWidget(self.account_input)
|
||||
form_layout.addSpacing(10)
|
||||
form_layout.addWidget(password_label)
|
||||
form_layout.addWidget(self.password_input)
|
||||
|
||||
main_layout.addLayout(form_layout)
|
||||
|
||||
self.login_button = QPushButton("登录")
|
||||
self.login_button.setMinimumHeight(55)
|
||||
self.login_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #0086fa;
|
||||
color: white;
|
||||
border-radius: 5px;
|
||||
padding: 15px;
|
||||
font-family: 'Microsoft YaHei';
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #0077e6;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #006bbd;
|
||||
}
|
||||
""")
|
||||
self.login_button.clicked.connect(self.on_login)
|
||||
main_layout.addWidget(self.login_button)
|
||||
|
||||
main_layout.addStretch()
|
||||
|
||||
def on_login(self):
|
||||
user_id = self.account_input.text().strip()
|
||||
password = self.password_input.text().strip()
|
||||
|
||||
if not user_id or not password:
|
||||
QMessageBox.warning(self, "登录失败", "请输入账号和密码")
|
||||
return
|
||||
|
||||
password_md5 = hashlib.md5(password.encode()).hexdigest()
|
||||
success, user_info = self.db.authenticate_user(user_id, password_md5)
|
||||
if success:
|
||||
self.login_success.emit(user_info)
|
||||
self.close()
|
||||
else:
|
||||
QMessageBox.warning(self, "登录失败", user_info)
|
||||
self.password_input.clear()
|
||||
self.password_input.setFocus()
|
||||
184
ui/manual_inspection_page.py
Normal file
184
ui/manual_inspection_page.py
Normal file
@ -0,0 +1,184 @@
|
||||
from PySide6.QtWidgets import (
|
||||
QWidget, QTableWidget, QTableWidgetItem, QHeaderView, QLineEdit, QAbstractItemView
|
||||
)
|
||||
from PySide6.QtCore import Qt
|
||||
from PySide6.QtGui import QColor, QBrush
|
||||
from ui.base_page import BaseInspectionPage
|
||||
|
||||
class ManualInspectionPage(BaseInspectionPage):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__("手检", parent)
|
||||
self.inputs = {}
|
||||
self.init_ui()
|
||||
|
||||
def init_ui(self):
|
||||
self.content_layout.setContentsMargins(5, 5, 5, 5)
|
||||
|
||||
# Initialize Table
|
||||
self.table = QTableWidget()
|
||||
self.table.setColumnCount(4)
|
||||
self.table.verticalHeader().setVisible(False)
|
||||
self.table.horizontalHeader().setVisible(False)
|
||||
|
||||
# Style
|
||||
self.table.setStyleSheet("""
|
||||
QTableWidget {
|
||||
border: 1px solid #999;
|
||||
gridline-color: #999;
|
||||
background-color: white;
|
||||
font-family: 'Microsoft YaHei';
|
||||
font-size: 18px;
|
||||
}
|
||||
QTableWidget::item {
|
||||
padding: 0px;
|
||||
}
|
||||
QLineEdit {
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
font-family: 'Microsoft YaHei';
|
||||
font-size: 18px;
|
||||
padding-left: 8px;
|
||||
qproperty-alignment: 'AlignLeft | AlignVCenter';
|
||||
}
|
||||
""")
|
||||
|
||||
header = self.table.horizontalHeader()
|
||||
header.setSectionResizeMode(QHeaderView.Stretch)
|
||||
|
||||
self.table.setSelectionMode(QAbstractItemView.NoSelection)
|
||||
self.table.setFocusPolicy(Qt.NoFocus)
|
||||
|
||||
self.content_layout.addWidget(self.table)
|
||||
|
||||
self.render_form()
|
||||
|
||||
def render_form(self):
|
||||
# 1. 第一行 - 工程号
|
||||
self.add_row_label_input("工程号", "batch_no", span_input=3)
|
||||
|
||||
# 2. 第二行 - 产地和规格
|
||||
row = self.table.rowCount()
|
||||
self.table.insertRow(row)
|
||||
self.table.setRowHeight(row, 50)
|
||||
self.set_label_cell(row, 0, "产地")
|
||||
self.table.setCellWidget(row, 1, self.create_input("material"))
|
||||
self.set_label_cell(row, 2, "规格")
|
||||
self.table.setCellWidget(row, 3, self.create_input("spec"))
|
||||
|
||||
# 3. 分组标题 - 第一组
|
||||
self.add_group_header("━ 第一组 ━")
|
||||
|
||||
# 4. 炉号和材质(第一组)
|
||||
row = self.table.rowCount()
|
||||
self.table.insertRow(row)
|
||||
self.table.setRowHeight(row, 50)
|
||||
self.set_label_cell(row, 0, "炉号", align_left=True)
|
||||
self.table.setCellWidget(row, 1, self.create_input("material1"))
|
||||
self.set_label_cell(row, 2, "材质")
|
||||
self.table.setCellWidget(row, 3, self.create_input("spec1"))
|
||||
|
||||
# 5. 检验信息(第一组)
|
||||
self.add_row_label_input("检验信息", "insp_info_1", span_input=3, align_left=True)
|
||||
|
||||
# 6. 分组标题 - 第二组
|
||||
self.add_group_header("━ 第二组 ━")
|
||||
|
||||
# 7. 炉号和材质(第二组)
|
||||
row = self.table.rowCount()
|
||||
self.table.insertRow(row)
|
||||
self.table.setRowHeight(row, 50)
|
||||
self.set_label_cell(row, 0, "炉号", align_left=True)
|
||||
self.table.setCellWidget(row, 1, self.create_input("info1"))
|
||||
self.set_label_cell(row, 2, "材质")
|
||||
self.table.setCellWidget(row, 3, self.create_input("spec2"))
|
||||
|
||||
# 8. 检验信息(第二组)
|
||||
self.add_row_label_input("检验信息", "insp_info_2", span_input=3, align_left=True)
|
||||
|
||||
# 9. 分组标题 - 检验值
|
||||
self.add_group_header("━ 检验值 ━")
|
||||
|
||||
# 10. 元素行 - 固定顺序显示所有18个元素
|
||||
element_order = ["C", "Si", "Mn", "P", "S", "Cr", "Ni", "Mo", "Cu", "Ti", "Nb", "V", "Al", "W", "Co", "N", "Fe", "Se"]
|
||||
for i in range(0, len(element_order), 2):
|
||||
name1 = element_order[i]
|
||||
name2 = element_order[i+1] if i + 1 < len(element_order) else None
|
||||
|
||||
row = self.table.rowCount()
|
||||
self.table.insertRow(row)
|
||||
self.table.setRowHeight(row, 50)
|
||||
|
||||
self.set_label_cell(row, 0, name1)
|
||||
self.table.setCellWidget(row, 1, self.create_input(name1))
|
||||
|
||||
if name2:
|
||||
self.set_label_cell(row, 2, name2)
|
||||
self.table.setCellWidget(row, 3, self.create_input(name2))
|
||||
|
||||
def add_row_label_input(self, label_text, key, span_input=1, align_left=False):
|
||||
row = self.table.rowCount()
|
||||
self.table.insertRow(row)
|
||||
self.table.setRowHeight(row, 50)
|
||||
|
||||
self.set_label_cell(row, 0, label_text, align_left=align_left)
|
||||
|
||||
inp = self.create_input(key)
|
||||
self.table.setCellWidget(row, 1, inp)
|
||||
|
||||
if span_input > 1:
|
||||
self.table.setSpan(row, 1, 1, span_input)
|
||||
|
||||
def set_label_cell(self, row, col, text, align_left=False):
|
||||
item = QTableWidgetItem(text)
|
||||
# 根据参数设置对齐方式
|
||||
if align_left:
|
||||
item.setTextAlignment(Qt.AlignLeft | Qt.AlignVCenter)
|
||||
else:
|
||||
item.setTextAlignment(Qt.AlignCenter)
|
||||
item.setFlags(Qt.ItemIsEnabled)
|
||||
brush = QBrush(QColor("#f5f5f5"))
|
||||
item.setBackground(brush)
|
||||
self.table.setItem(row, col, item)
|
||||
|
||||
def add_group_header(self, text):
|
||||
"""添加分组标题行"""
|
||||
row = self.table.rowCount()
|
||||
self.table.insertRow(row)
|
||||
self.table.setRowHeight(row, 40)
|
||||
|
||||
item = QTableWidgetItem(text)
|
||||
item.setTextAlignment(Qt.AlignCenter)
|
||||
item.setFlags(Qt.ItemIsEnabled)
|
||||
brush = QBrush(QColor("#f0f0f0"))
|
||||
item.setBackground(brush)
|
||||
from PySide6.QtGui import QFont
|
||||
font = QFont()
|
||||
font.setBold(True)
|
||||
font.setPointSize(14)
|
||||
item.setFont(font)
|
||||
self.table.setItem(row, 0, item)
|
||||
self.table.setSpan(row, 0, 1, 4)
|
||||
|
||||
def create_input(self, key):
|
||||
inp = QLineEdit()
|
||||
inp.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
|
||||
self.inputs[key] = inp
|
||||
return inp
|
||||
|
||||
def clear_data(self):
|
||||
"""清空所有输入框数据"""
|
||||
for inp in self.inputs.values():
|
||||
inp.clear()
|
||||
|
||||
def update_ui_with_data(self, data):
|
||||
"""根据数据库数据动态更新UI"""
|
||||
elements = data.get("elements", {})
|
||||
|
||||
# 固定元素顺序
|
||||
element_order = ["C", "Si", "Mn", "P", "S", "Cr", "Ni", "Mo", "Cu", "Ti", "Nb", "V", "Al", "W", "Co", "N", "Fe", "Se"]
|
||||
|
||||
# 更新所有元素值,没有的显示为0
|
||||
for el in element_order:
|
||||
if el in self.inputs:
|
||||
value = elements.get(el, 0)
|
||||
self.inputs[el].setText(str(value))
|
||||
199
utils/readMTP.py
Normal file
199
utils/readMTP.py
Normal file
@ -0,0 +1,199 @@
|
||||
import os
|
||||
import time
|
||||
|
||||
try:
|
||||
import win32com.client
|
||||
import pythoncom
|
||||
except ImportError:
|
||||
win32com = None
|
||||
|
||||
class MTPHandler:
|
||||
|
||||
def __init__(self):
|
||||
self.cache_dir = os.path.join(os.getcwd(), "mtp_cache")
|
||||
if not os.path.exists(self.cache_dir):
|
||||
os.makedirs(self.cache_dir)
|
||||
|
||||
def get_latest_xml_windows(self, device_name, path_str):
|
||||
"""
|
||||
在 Windows MTP 设备上寻找并复制最新的 XML 文件。
|
||||
"""
|
||||
print(f"\n[MTP] >>> 开始同步工作: 设备={device_name}")
|
||||
if win32com is None:
|
||||
return None, "系统未安装 pywin32。"
|
||||
|
||||
try:
|
||||
pythoncom.CoInitialize()
|
||||
shell = win32com.client.Dispatch("Shell.Application")
|
||||
computer = shell.NameSpace(17) # 此电脑
|
||||
|
||||
# 1. 寻找设备 (增加容错:忽略首尾空格和大小写)
|
||||
device_item = None
|
||||
target_name = device_name.strip().lower()
|
||||
|
||||
print(f"[MTP] 正在检索设备,目标名称: '{target_name}'")
|
||||
items = computer.Items()
|
||||
available_devices = []
|
||||
|
||||
for item in items:
|
||||
available_devices.append(item.Name)
|
||||
if item.Name.strip().lower() == target_name:
|
||||
device_item = item
|
||||
print(f"[MTP] 匹配成功: '{item.Name}'")
|
||||
break
|
||||
|
||||
if not device_item:
|
||||
print(f"[MTP] 未找到设备: '{device_name}'")
|
||||
print(f"[MTP] 当前电脑识别到的设备列表: {available_devices}")
|
||||
return None, f"未找到 MTP 设备: {device_name} (请检查名称)"
|
||||
|
||||
current_folder = device_item.GetFolder
|
||||
|
||||
# 2. 深入路径
|
||||
path_chain = [p.strip() for p in path_str.replace('\\', '/').split('/') if p.strip()]
|
||||
for folder_name in path_chain:
|
||||
found = False
|
||||
for item in current_folder.Items():
|
||||
if item.Name.lower() == folder_name.lower() and item.IsFolder:
|
||||
current_folder = item.GetFolder
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
print(f"[MTP] 路径中断,找不到文件夹: '{folder_name}'")
|
||||
# 打印一下当前目录下有的东西,帮用户排查
|
||||
content = [i.Name for i in current_folder.Items()]
|
||||
print(f"[MTP] 当前所在位置 [ {current_folder.Title} ] 下的可选内容: {content}")
|
||||
return None, f"路径点不存在: {folder_name}"
|
||||
|
||||
# 3. 扫描文件并识别“最新”
|
||||
print(f"[MTP] 已到达目标目录: [ {current_folder.Title} ]")
|
||||
print(f"[MTP] 正在列出该目录下所有项目...")
|
||||
print("-" * 40)
|
||||
|
||||
xml_items = []
|
||||
all_items = current_folder.Items()
|
||||
print(f"[MTP] 目录内总项数: {all_items.Count}")
|
||||
|
||||
for i in range(all_items.Count):
|
||||
try:
|
||||
item = all_items.Item(i)
|
||||
name = str(item.Name)
|
||||
is_folder = item.IsFolder
|
||||
|
||||
# 打印每一个文件的详细信息用于排查
|
||||
# 尝试打印前 6 个详情列,看看哪一列是类型,哪一列是时间
|
||||
details = [str(current_folder.GetDetailsOf(item, j)) for j in range(6)]
|
||||
if i == 0: print(f"[MTP] 属性列参考(0-5): {details}")
|
||||
|
||||
item_type = details[2]
|
||||
# 识别逻辑:
|
||||
# 1. 文件名明确以 .xml 结尾
|
||||
# 2. 或者类型描述中包含 "XML" (遍历 2, 3, 4 列)
|
||||
# 3. 或者针对您的 PDA 特效:文件名以 "Result_" 开头且名字里没有点 (认为后缀被隐藏了)
|
||||
is_xml_name = name.lower().endswith(".xml")
|
||||
is_xml_type = any("xml" in details[j].lower() for j in [2, 3, 4, 5])
|
||||
is_pda_result = name.startswith("Result_") and "." not in name
|
||||
|
||||
if not is_folder and (is_xml_name or is_xml_type or is_pda_result):
|
||||
# 尝试获取最可信的时间
|
||||
mtime = self._get_best_time(current_folder, item)
|
||||
# 如果文件名包含 Result_YYYYMMDD_HHMMSS,提取它作为辅助排序依据
|
||||
name_ts = self._extract_time_from_name(name)
|
||||
|
||||
xml_items.append({
|
||||
'item': item,
|
||||
'name': name if is_xml_name else f"{name}.xml",
|
||||
'time': mtime,
|
||||
'name_ts': name_ts
|
||||
})
|
||||
print(f"[MTP] 识别 XML: '{name}' | 创建/修改时间: {mtime} | 文件名时间: {name_ts}")
|
||||
else:
|
||||
pass
|
||||
except Exception as e:
|
||||
print(f"[MTP] 读取第 {i} 个项目时出错: {e}")
|
||||
|
||||
print("-" * 40)
|
||||
|
||||
if not xml_items:
|
||||
print(f"[MTP] 报错: 在目录 [ {current_folder.Title} ] 中没有找到任何以 .xml 结尾的文件")
|
||||
return None, "该目录中没有 XML 文件"
|
||||
|
||||
# 排序逻辑优化:
|
||||
# 1. 优先按照文件名里的时间戳排序 (Result_YYYYMMDD_HHMMSS)
|
||||
# 2. 其次按照系统返回的时间属性排序
|
||||
# 3. 最后按全名排序
|
||||
xml_items.sort(key=lambda x: (x['name_ts'], str(x['time']), x['name']), reverse=True)
|
||||
|
||||
latest = xml_items[0]
|
||||
latest_item = latest['item']
|
||||
filename = latest['name']
|
||||
|
||||
print(f"[MTP] 选定最新文件: {filename}")
|
||||
print(f"[MTP] - 属性时间: {latest['time']}")
|
||||
print(f"[MTP] - 名称日期: {latest['name_ts']}")
|
||||
|
||||
# 4. 复制到本地
|
||||
local_path = os.path.abspath(os.path.join(self.cache_dir, filename))
|
||||
|
||||
# 清理旧缓存
|
||||
for f in os.listdir(self.cache_dir):
|
||||
try: os.remove(os.path.join(self.cache_dir, f))
|
||||
except: pass
|
||||
|
||||
print(f"[MTP] 正在拉取文件到本地缓存...")
|
||||
dest_shell_folder = shell.NameSpace(os.path.abspath(self.cache_dir))
|
||||
dest_shell_folder.CopyHere(latest_item, 4 | 16)
|
||||
|
||||
# 等待
|
||||
success = False
|
||||
for i in range(20):
|
||||
if os.path.exists(local_path) and os.path.getsize(local_path) > 0:
|
||||
success = True
|
||||
break
|
||||
time.sleep(0.5)
|
||||
|
||||
if success:
|
||||
print(f"[MTP] ✨ 同步成功: {filename}")
|
||||
return local_path, filename
|
||||
else:
|
||||
return None, "同步超时"
|
||||
|
||||
except Exception as e:
|
||||
print(f"[MTP] 异常: {e}")
|
||||
return None, str(e)
|
||||
finally:
|
||||
pythoncom.CoUninitialize()
|
||||
|
||||
def _extract_time_from_name(self, name):
|
||||
"""从文件名 Result_20251222_124145 中提取日期字符串用于排序"""
|
||||
import re
|
||||
# 匹配 8位数字_6位数字 (YYYYMMDD_HHMMSS)
|
||||
match = re.search(r'(\d{8}_\d{6})', name)
|
||||
if match:
|
||||
return match.group(1)
|
||||
# 如果没匹配到,试试只匹配 8位日期
|
||||
match = re.search(r'(\d{8})', name)
|
||||
if match:
|
||||
return match.group(1)
|
||||
return ""
|
||||
|
||||
def _get_best_time(self, folder_obj, item_obj):
|
||||
"""尝试从 Shell 属性中获取文件时间"""
|
||||
# 索引 3 通常是修改时间,索引 4 通常是创建时间
|
||||
for idx in [3, 4, 5]:
|
||||
try:
|
||||
t = folder_obj.GetDetailsOf(item_obj, idx)
|
||||
if t and len(t) > 5: # 简单过滤无效短字符串
|
||||
return t
|
||||
except: pass
|
||||
|
||||
# 兜底尝试标准属性
|
||||
try:
|
||||
return str(item_obj.ModifyDate)
|
||||
except: return ""
|
||||
|
||||
def clear_cache(self):
|
||||
if os.path.exists(self.cache_dir):
|
||||
import shutil
|
||||
shutil.rmtree(self.cache_dir)
|
||||
os.makedirs(self.cache_dir)
|
||||
19
walkthrough.md
Normal file
19
walkthrough.md
Normal file
@ -0,0 +1,19 @@
|
||||
# 修改记录 - 调整入检界面布局顺序
|
||||
|
||||
## 1. 修改内容
|
||||
|
||||
### `ui/incoming_inspection_page.py`
|
||||
- **布局重组**:调整了控件的显示顺序,采用了交错式布局以符合业务逻辑。
|
||||
- **组1**:炉号1 (Row) -> 检验信息1 (Row, 跨3列)
|
||||
- **组2**:炉号2 (Row) -> 检验信息2 (Row, 跨3列)
|
||||
- **输入框优化**:使用 `add_row_label_input(..., span_input=3)` 方法,使“检验信息”输入框占据行内剩余空间,方便输入长文本。
|
||||
- **逻辑修正**:同步更新了 `update_ui_with_data` 中的 `fixed_rows` 常量为 **6**。
|
||||
- **计算方式**:0:批次, 1:产地/规格, 2:炉号1, 3:信息1, 4:炉号2, 5:信息2。
|
||||
|
||||
## 2. 变更影响
|
||||
- **UI 呈现**:不仅恢复了独立的 "检验信息" 行,而且将其紧跟在对应的 "炉号" 之后,逻辑上更清晰(炉号1对应信息1,炉号2对应信息2)。
|
||||
- **数据回显**:重新计算了固定行数,确保在扫描查询后,这些自定义输入框不会被错误清除。
|
||||
|
||||
## 3. 验证
|
||||
- 静态代码检查通过。
|
||||
- 逻辑检查:`fixed_rows=6` 覆盖了所有静态插入的行,动态元素将正确追加在第 7 行之后。
|
||||
123
xml.sql
Normal file
123
xml.sql
Normal file
@ -0,0 +1,123 @@
|
||||
|
||||
CREATE TABLE element_analysis_data (
|
||||
-- ========== 主键 ==========
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
|
||||
-- ========== 样品基本信息 ==========
|
||||
sample_name VARCHAR(255),
|
||||
sample_grade_id VARCHAR(100),
|
||||
sample_grade_alias VARCHAR(255),
|
||||
operator_name VARCHAR(100),
|
||||
instrument VARCHAR(100),
|
||||
method_name VARCHAR(255),
|
||||
sample_type VARCHAR(50),
|
||||
|
||||
-- ========== 测量信息 ==========
|
||||
replicate_no VARCHAR(100),
|
||||
measure_datetime VARCHAR(100),
|
||||
measure_duration VARCHAR(100),
|
||||
check_type VARCHAR(50),
|
||||
check_status VARCHAR(50),
|
||||
grade_name VARCHAR(100),
|
||||
base_element VARCHAR(10),
|
||||
rsd_check VARCHAR(100),
|
||||
|
||||
-- ========== 元素信息 ==========
|
||||
element_name VARCHAR(10) NOT NULL,
|
||||
element_type VARCHAR(50),
|
||||
|
||||
-- ========== 测量结果(浓度值) ==========
|
||||
concentration_value VARCHAR(100),
|
||||
concentration_unit VARCHAR(20),
|
||||
std_dev_value VARCHAR(100),
|
||||
|
||||
-- ========== 不确定度信息 ==========
|
||||
uncertainty_abs VARCHAR(100),
|
||||
uncertainty_rel VARCHAR(100),
|
||||
|
||||
-- ========== 验收限值 ==========
|
||||
lower_acceptance_limit VARCHAR(100),
|
||||
upper_acceptance_limit VARCHAR(100),
|
||||
|
||||
-- ========== 状态信息 ==========
|
||||
result_status VARCHAR(50),
|
||||
calibration_status VARCHAR(50),
|
||||
acceptance_status VARCHAR(50),
|
||||
is_deleted VARCHAR(100) DEFAULT 'false',
|
||||
|
||||
-- ========== 计算字段(便于查询) ==========
|
||||
is_within_limits VARCHAR(100),
|
||||
deviation_from_center VARCHAR(100),
|
||||
|
||||
-- ========== XML元数据 ==========
|
||||
xml_version VARCHAR(20),
|
||||
xml_creation_datetime VARCHAR(100),
|
||||
|
||||
-- ========== 系统字段 ==========
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
data_source VARCHAR(255)
|
||||
);
|
||||
|
||||
-- ========== 索引设计 ==========
|
||||
CREATE INDEX idx_element_name ON element_analysis_data(element_name);
|
||||
CREATE INDEX idx_sample_grade ON element_analysis_data(sample_grade_id);
|
||||
CREATE INDEX idx_measure_datetime ON element_analysis_data(measure_datetime);
|
||||
CREATE INDEX idx_calibration_status ON element_analysis_data(calibration_status);
|
||||
CREATE INDEX idx_acceptance_status ON element_analysis_data(acceptance_status);
|
||||
CREATE INDEX idx_operator ON element_analysis_data(operator_name);
|
||||
CREATE INDEX idx_instrument ON element_analysis_data(instrument);
|
||||
CREATE INDEX idx_composite_element_sample ON element_analysis_data(element_name, sample_grade_id, measure_datetime);
|
||||
|
||||
|
||||
-- ============================================
|
||||
-- 表和字段注释(使用单独的COMMENT语句)
|
||||
-- ============================================
|
||||
|
||||
COMMENT ON TABLE element_analysis_data IS '元素分析数据扁平化表 - 存储所有元素检测结果,每行代表一个元素的一次测量';
|
||||
|
||||
COMMENT ON COLUMN element_analysis_data.id IS '主键ID,自增';
|
||||
COMMENT ON COLUMN element_analysis_data.sample_name IS '样品名称';
|
||||
COMMENT ON COLUMN element_analysis_data.sample_grade_id IS '样品材料等级标识,如SS-321';
|
||||
COMMENT ON COLUMN element_analysis_data.sample_grade_alias IS '样品材料等级别名,如SA-213 Grade TP 321';
|
||||
COMMENT ON COLUMN element_analysis_data.operator_name IS '执行测量的操作员姓名';
|
||||
COMMENT ON COLUMN element_analysis_data.instrument IS '使用的检测仪器编号';
|
||||
COMMENT ON COLUMN element_analysis_data.method_name IS '使用的检测方法名称';
|
||||
COMMENT ON COLUMN element_analysis_data.sample_type IS '样品类型:Calibration(校准)、Measurement(测量)等';
|
||||
COMMENT ON COLUMN element_analysis_data.replicate_no IS '重复测量的序号,从0开始';
|
||||
COMMENT ON COLUMN element_analysis_data.measure_datetime IS '执行测量的日期和时间';
|
||||
COMMENT ON COLUMN element_analysis_data.measure_duration IS '测量持续时间,单位:秒';
|
||||
COMMENT ON COLUMN element_analysis_data.check_type IS '检查类型,如GradeAcceptance(等级验收)';
|
||||
COMMENT ON COLUMN element_analysis_data.check_status IS '检查状态,如Ok(通过)';
|
||||
COMMENT ON COLUMN element_analysis_data.grade_name IS '检测使用的材料等级名称';
|
||||
COMMENT ON COLUMN element_analysis_data.base_element IS '基础元素符号,用于计算其他元素的相对含量';
|
||||
COMMENT ON COLUMN element_analysis_data.rsd_check IS '是否进行相对标准偏差(RSD)检查';
|
||||
COMMENT ON COLUMN element_analysis_data.element_name IS '化学元素符号,如Fe(铁)、Cr(铬)、Ni(镍)、Mn(锰)等';
|
||||
COMMENT ON COLUMN element_analysis_data.element_type IS '元素类型,通常为Element';
|
||||
COMMENT ON COLUMN element_analysis_data.concentration_value IS '元素浓度的测量值';
|
||||
COMMENT ON COLUMN element_analysis_data.concentration_unit IS '浓度单位,通常为%(百分比)';
|
||||
COMMENT ON COLUMN element_analysis_data.std_dev_value IS '标准偏差值,表示测量的离散程度';
|
||||
COMMENT ON COLUMN element_analysis_data.uncertainty_abs IS '绝对不确定度,表示测量的绝对误差范围';
|
||||
COMMENT ON COLUMN element_analysis_data.uncertainty_rel IS '相对不确定度(百分比),表示测量的相对误差';
|
||||
COMMENT ON COLUMN element_analysis_data.lower_acceptance_limit IS '验收判定的下限值';
|
||||
COMMENT ON COLUMN element_analysis_data.upper_acceptance_limit IS '验收判定的上限值';
|
||||
COMMENT ON COLUMN element_analysis_data.result_status IS '结果状态,如OK(正常)';
|
||||
COMMENT ON COLUMN element_analysis_data.calibration_status IS '校准状态:InRange(在范围内)、UnderRange(低于范围)、OverRange(超出范围)';
|
||||
COMMENT ON COLUMN element_analysis_data.acceptance_status IS '验收状态:InRange(合格)、OverRange(超标)、NotUsed(不参与验收)';
|
||||
COMMENT ON COLUMN element_analysis_data.is_deleted IS '该测量结果是否已被标记为删除';
|
||||
COMMENT ON COLUMN element_analysis_data.is_within_limits IS '测量值是否在验收范围内(自动计算)';
|
||||
COMMENT ON COLUMN element_analysis_data.deviation_from_center IS '测量值偏离验收范围中心值的程度';
|
||||
COMMENT ON COLUMN element_analysis_data.xml_version IS 'XML文件版本号';
|
||||
COMMENT ON COLUMN element_analysis_data.xml_creation_datetime IS 'XML文件创建时间';
|
||||
COMMENT ON COLUMN element_analysis_data.created_at IS '数据库记录创建时间';
|
||||
COMMENT ON COLUMN element_analysis_data.updated_at IS '数据库记录最后更新时间';
|
||||
COMMENT ON COLUMN element_analysis_data.data_source IS '数据来源的XML文件名';
|
||||
|
||||
COMMENT ON INDEX idx_element_name IS '元素名称索引,用于按元素查询';
|
||||
COMMENT ON INDEX idx_sample_grade IS '样品等级索引,用于按材料等级查询';
|
||||
COMMENT ON INDEX idx_measure_datetime IS '测量时间索引,用于时间范围查询';
|
||||
COMMENT ON INDEX idx_calibration_status IS '校准状态索引,用于筛选校准状态';
|
||||
COMMENT ON INDEX idx_acceptance_status IS '验收状态索引,用于筛选合格/不合格数据';
|
||||
COMMENT ON INDEX idx_operator IS '操作员索引,用于按操作员查询';
|
||||
COMMENT ON INDEX idx_instrument IS '仪器索引,用于按仪器查询';
|
||||
COMMENT ON INDEX idx_composite_element_sample IS '复合索引,用于元素-样品-时间的组合查询';
|
||||
Loading…
Reference in New Issue
Block a user