Compare commits
2 Commits
de82ddf7cd
...
7ef023842b
Author | SHA1 | Date | |
---|---|---|---|
7ef023842b | |||
1185f25fd3 |
169
CLAUDE.md
Normal file
169
CLAUDE.md
Normal file
@ -0,0 +1,169 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
VWED Task Module is a FastAPI-based Python system for managing and executing task workflows for AMR (Autonomous Mobile Robot) scheduling systems. The system provides a low-code configuration tool that allows users to design and configure complex robot task flows through a visual interface.
|
||||
|
||||
## Development Commands
|
||||
|
||||
### Application Startup
|
||||
```bash
|
||||
# Primary method - run the main application
|
||||
python app.py
|
||||
|
||||
# Alternative method - using uvicorn directly
|
||||
uvicorn app:app --host 0.0.0.0 --port 8000 --reload
|
||||
```
|
||||
|
||||
### Database Operations
|
||||
```bash
|
||||
# Run database migrations
|
||||
python scripts/run_migration.py
|
||||
|
||||
# Generate new migrations
|
||||
python scripts/generate_migration.py
|
||||
|
||||
# Initialize database
|
||||
python scripts/init_db.py
|
||||
```
|
||||
|
||||
### Docker Deployment
|
||||
```bash
|
||||
# Build and run with Docker
|
||||
docker build -t vwed-task:latest .
|
||||
docker run -d -p 8000:8000 --name vwed-task-container vwed-task:latest
|
||||
|
||||
# Or use Docker Compose (recommended)
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
The system follows a layered architecture with clear separation of concerns:
|
||||
|
||||
### Core Layers
|
||||
- **API Layer (`routes/`)**: FastAPI REST endpoints for client communication
|
||||
- **Middleware Layer (`middlewares/`)**: Request logging, error handling, and cross-cutting concerns
|
||||
- **Business Logic Layer (`services/`)**: Core business logic including task execution and scheduling
|
||||
- **Data Layer (`data/`)**: ORM models, database session management, and data persistence
|
||||
- **Component Layer (`components/`)**: Extensible component system for task workflow building
|
||||
- **Configuration Layer (`config/`)**: System settings, database configuration, and error mappings
|
||||
|
||||
### Key Services
|
||||
- **Task Execution Engine (`services/execution/`)**: Handles task lifecycle and execution
|
||||
- `task_executor.py`: Main task execution controller
|
||||
- `block_executor.py`: Individual task block execution
|
||||
- `task_context.py`: Execution context and variable management
|
||||
- `handlers/`: Specific handlers for different component types
|
||||
- **Enhanced Scheduler (`services/enhanced_scheduler/`)**: High-performance async task scheduling
|
||||
- **Intelligence Layer (`services/intelligence/`)**: AI-powered features (partial implementation)
|
||||
|
||||
### Data Models
|
||||
Key models in `data/models/`:
|
||||
- `taskdef.py`: Task definition and configuration
|
||||
- `taskrecord.py`: Task execution records
|
||||
- `blockrecord.py`: Individual block execution records
|
||||
- `tasktemplate.py`: Reusable task templates
|
||||
- `calldevice.py`: Device integration configurations
|
||||
|
||||
## Key Features
|
||||
|
||||
### Task Management
|
||||
- Visual task flow designer with drag-and-drop interface
|
||||
- Version control for task configurations
|
||||
- Task templates for reusability
|
||||
- Real-time execution monitoring
|
||||
|
||||
### Component System
|
||||
Components are registered in `config/components/` and include:
|
||||
- Foundation components (basic operations)
|
||||
- Robot scheduling components
|
||||
- HTTP request components
|
||||
- Storage location management
|
||||
- Progress tracking components
|
||||
- Script execution components
|
||||
|
||||
### Device Integration
|
||||
- Modbus protocol support (`modbusconfig.py`)
|
||||
- Generic device calling interface (`calldevice.py`)
|
||||
- Extensible communication protocols
|
||||
|
||||
## Development Guidelines
|
||||
|
||||
### Database Schema Changes
|
||||
1. Modify models in `data/models/`
|
||||
2. Generate migration: `python scripts/generate_migration.py`
|
||||
3. Apply migration: `python scripts/run_migration.py`
|
||||
|
||||
### Adding New Components
|
||||
1. Define component configuration in `config/components/`
|
||||
2. Implement handler in `services/execution/handlers/`
|
||||
3. Register component in the component system
|
||||
|
||||
### Task Execution Flow
|
||||
1. Task definition stored in `taskdef` table
|
||||
2. Execution creates `taskrecord` entry
|
||||
3. Individual blocks create `blockrecord` entries
|
||||
4. Context variables managed through `task_context.py`
|
||||
5. Component handlers execute specific business logic
|
||||
|
||||
### Configuration Management
|
||||
- Environment-specific settings in `config/settings.py`
|
||||
- Database configuration in `config/database_config.py`
|
||||
- Error messages centralized in `config/error_messages.py`
|
||||
|
||||
## Important Technical Details
|
||||
|
||||
### Async Task Scheduling
|
||||
The system uses a custom enhanced scheduler (`services/enhanced_scheduler/`) that:
|
||||
- Maintains worker pools for concurrent task execution
|
||||
- Provides priority-based task queuing
|
||||
- Handles task persistence and recovery
|
||||
- Manages worker lifecycle
|
||||
|
||||
### Component Architecture
|
||||
Components follow a registry pattern:
|
||||
- Each component type has a handler class
|
||||
- Handlers implement standard execution interface
|
||||
- Components are configurable through JSON definitions
|
||||
- Extensible for new component types
|
||||
|
||||
### Database Session Management
|
||||
- Uses SQLAlchemy ORM with session management in `data/session.py`
|
||||
- Supports both sync and async database operations
|
||||
- Automatic connection pooling and cleanup
|
||||
|
||||
## Testing and Debugging
|
||||
|
||||
### Test Files
|
||||
- Basic tests in `tests/` directory
|
||||
- Test data and fixtures available
|
||||
- Integration tests for API endpoints
|
||||
|
||||
### Logging
|
||||
- Centralized logging configuration in `utils/logger.py`
|
||||
- Application logs stored in `logs/` directory
|
||||
- Structured logging for debugging task execution
|
||||
|
||||
### API Documentation
|
||||
- Swagger UI available at `http://localhost:8000/docs`
|
||||
- Comprehensive API documentation in `VWED任务模块接口文档/`
|
||||
|
||||
## Common Troubleshooting
|
||||
|
||||
### Database Issues
|
||||
- Check database connection in `config/database_config.py`
|
||||
- Verify database migrations are up to date
|
||||
- Review logs in `logs/app.log`
|
||||
|
||||
### Task Execution Problems
|
||||
- Monitor task status through API endpoints
|
||||
- Check execution logs for specific error messages
|
||||
- Verify component configurations are correct
|
||||
|
||||
### Performance Optimization
|
||||
- Adjust scheduler worker counts in settings
|
||||
- Monitor database connection pool usage
|
||||
- Review task complexity and component efficiency
|
@ -40,7 +40,7 @@ RUN mkdir -p logs && chmod -R 755 logs
|
||||
COPY . .
|
||||
|
||||
# 暴露端口
|
||||
EXPOSE 8000
|
||||
EXPOSE 8001
|
||||
|
||||
# 启动命令
|
||||
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8001"]
|
@ -565,6 +565,9 @@ ws://your-domain/ws/storage-location-broadcast/{scene_id}
|
||||
|
||||
##### 心跳检测
|
||||
|
||||
支持两种格式的心跳检测:
|
||||
|
||||
**JSON格式心跳:**
|
||||
```json
|
||||
{
|
||||
"type": "ping",
|
||||
@ -572,10 +575,105 @@ ws://your-domain/ws/storage-location-broadcast/{scene_id}
|
||||
}
|
||||
```
|
||||
|
||||
**字符串格式心跳:**
|
||||
```
|
||||
ping
|
||||
```
|
||||
|
||||
#### 服务器消息格式
|
||||
|
||||
与库位状态实时推送接口相同,参见上述文档。
|
||||
|
||||
##### 心跳响应
|
||||
|
||||
对于JSON格式心跳请求,服务器返回JSON格式响应:
|
||||
```json
|
||||
{
|
||||
"type": "pong",
|
||||
"timestamp": "2025-06-11T12:00:00.000Z"
|
||||
}
|
||||
```
|
||||
|
||||
对于字符串格式心跳请求,服务器返回字符串响应:
|
||||
```
|
||||
pong
|
||||
```
|
||||
|
||||
#### 连接示例
|
||||
|
||||
##### JavaScript客户端示例
|
||||
|
||||
```javascript
|
||||
// 建立WebSocket连接
|
||||
const sceneId = "your-scene-id";
|
||||
const wsUrl = `ws://localhost:8000/ws/storage-location-broadcast/${sceneId}`;
|
||||
|
||||
const websocket = new WebSocket(wsUrl);
|
||||
|
||||
// 连接建立
|
||||
websocket.onopen = function(event) {
|
||||
console.log("库位状态广播WebSocket连接已建立");
|
||||
|
||||
// 发送JSON格式心跳包
|
||||
websocket.send(JSON.stringify({
|
||||
type: "ping",
|
||||
timestamp: new Date().toISOString()
|
||||
}));
|
||||
|
||||
// 或者发送字符串格式心跳包
|
||||
// websocket.send("ping");
|
||||
};
|
||||
|
||||
// 接收消息
|
||||
websocket.onmessage = function(event) {
|
||||
try {
|
||||
// 尝试解析JSON格式响应
|
||||
const data = JSON.parse(event.data);
|
||||
|
||||
switch(data.type) {
|
||||
case "storage_location_update":
|
||||
console.log("库位状态更新:", data.data);
|
||||
break;
|
||||
case "storage_location_status_change":
|
||||
console.log("库位状态变化:", data.layer_name, data.action);
|
||||
break;
|
||||
case "pong":
|
||||
console.log("JSON格式心跳响应:", data.timestamp);
|
||||
break;
|
||||
case "error":
|
||||
console.error("服务器错误:", data.message);
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
// 处理字符串格式响应
|
||||
if (event.data === "pong") {
|
||||
console.log("字符串格式心跳响应: pong");
|
||||
} else {
|
||||
console.log("收到未知消息:", event.data);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 定期发送心跳
|
||||
setInterval(() => {
|
||||
if (websocket.readyState === WebSocket.OPEN) {
|
||||
// 可以选择JSON格式或字符串格式
|
||||
websocket.send("ping"); // 字符串格式
|
||||
// websocket.send(JSON.stringify({type: "ping", timestamp: new Date().toISOString()})); // JSON格式
|
||||
}
|
||||
}, 30000); // 每30秒发送一次心跳
|
||||
|
||||
// 连接关闭
|
||||
websocket.onclose = function(event) {
|
||||
console.log("库位状态广播WebSocket连接已关闭");
|
||||
};
|
||||
|
||||
// 连接错误
|
||||
websocket.onerror = function(error) {
|
||||
console.error("库位状态广播WebSocket连接错误:", error);
|
||||
};
|
||||
```
|
||||
|
||||
#### 使用场景
|
||||
|
||||
1. **监控面板**:多个监控客户端同时监听库位状态变化
|
||||
@ -636,4 +734,5 @@ ws://your-domain/ws/storage-location-broadcast/{scene_id}
|
||||
| 版本 | 日期 | 更新内容 |
|
||||
| --- | --- | --- |
|
||||
| 1.0.0 | 2025-06-11 | 初始版本,支持任务执行结果实时推送和广播功能 |
|
||||
| 1.1.0 | 2025-06-11 | 新增库位状态实时推送和广播功能,支持多种过滤条件和状态变化通知 |
|
||||
| 1.1.0 | 2025-06-11 | 新增库位状态实时推送和广播功能,支持多种过滤条件和状态变化通知 |
|
||||
| 1.2.0 | 2025-07-18 | 更新心跳检测机制,所有WebSocket接口现在支持JSON和字符串两种格式的心跳检测 |
|
@ -95,14 +95,12 @@
|
||||
{
|
||||
"station_name": "STATION-A-001",
|
||||
"area_name": "一般存储区B",
|
||||
"max_layers": 2,
|
||||
"max_layers": 1,
|
||||
"layers": [
|
||||
{
|
||||
"layer_name": "1-1" // 库位名称
|
||||
},
|
||||
{
|
||||
"layer_name": "1-2" // 库位名称
|
||||
"layer_name": "DSA_2_1_1" // 库位名称
|
||||
}
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -111,32 +109,28 @@
|
||||
"max_layers": 1,
|
||||
"layers": [
|
||||
{
|
||||
"layer_name": "2-1" //库位名称
|
||||
"layer_name": "DSA_2_1_2" //库位名称
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"station_name": "STATION-B-004",
|
||||
"area_name": "一般存储区C",
|
||||
"max_layers": 3,
|
||||
"area_name": "一般存储区B",
|
||||
"max_layers": 1,
|
||||
"layers": [
|
||||
{
|
||||
"layer_name": "4-1" //库位名称
|
||||
},
|
||||
{
|
||||
"layer_name": "4-2" // 库位名称
|
||||
},
|
||||
{
|
||||
"layer_name": "4-3"// 库位名称
|
||||
"layer_name": "DSA_2_1_3" //库位名称
|
||||
}
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
"station_name": "STATION-B-003",
|
||||
"area_name": "一般存储区B",
|
||||
"max_layers": 1,
|
||||
"layers": [
|
||||
{
|
||||
"layer_name": "3-1" //库位名称
|
||||
"layer_name": "DSA_2_1_4" //库位名称
|
||||
}
|
||||
]
|
||||
}
|
||||
|
242
VWED任务模块接口文档/外部任务接口文档.md
Normal file
242
VWED任务模块接口文档/外部任务接口文档.md
Normal file
@ -0,0 +1,242 @@
|
||||
# 外部任务接口文档
|
||||
|
||||
## 外部任务模块接口
|
||||
|
||||
### 1. 创建新任务 (create_new_task)
|
||||
|
||||
#### 接口描述
|
||||
根据任务类型自动选择对应的任务模板并执行任务,用于外部系统调用创建和启动任务。
|
||||
|
||||
#### 请求方式
|
||||
- **HTTP方法**: POST
|
||||
- **接口路径**: `/newTask`
|
||||
|
||||
#### 请求参数
|
||||
|
||||
| 参数名 | 类型 | 必填 | 描述 |
|
||||
|-------|------|-----|------|
|
||||
| ReqCode | String | 是 | 请求唯一标识码 |
|
||||
| SourceID | String | 否 | 来源ID |
|
||||
| TargetID | String | 是 | 目标ID |
|
||||
| TaskType | TaskTypeEnum | 是 | 任务类型 |
|
||||
|
||||
#### 任务类型枚举 (TaskTypeEnum)
|
||||
|
||||
| 枚举值 | 描述 | 模板ID |
|
||||
|-------|------|-------|
|
||||
| GG2MP | 高柜到MP | template_gg2mp_id |
|
||||
| GGFK2MP | 高柜发库到MP | template_ggfk2mp_id |
|
||||
| GT2MP | 高台到MP | 571985c1-cfa5-4186-8acd-6e3868a5e08c |
|
||||
| GTFK2MP | 高台发库到MP | template_gtfk2mp_id |
|
||||
| ZG2MP | 中柜到MP | template_zg2mp_id |
|
||||
| QZ2MP | 清洗到MP | template_qz2mp_id |
|
||||
| LG2MP | 料柜到MP | template_lg2mp_id |
|
||||
| PHZ2MP | 配货站到MP | template_phz2mp_id |
|
||||
| MP2GG | MP到高柜 | template_mp2gg_id |
|
||||
| MP2GGFK | MP到高柜发库 | template_mp2ggfk_id |
|
||||
| MP2GT | MP到高台 | template_mp2gt_id |
|
||||
| MP2GTFK | MP到高台发库 | template_mp2gtfk_id |
|
||||
| MP2ZG | MP到中柜 | template_mp2zg_id |
|
||||
| MP2QZ | MP到清洗 | template_mp2qz_id |
|
||||
| MP2LG | MP到料柜 | template_mp2lg_id |
|
||||
| MP2PHZ | MP到配货站 | template_mp2phz_id |
|
||||
|
||||
#### 请求示例
|
||||
|
||||
```json
|
||||
{
|
||||
"ReqCode": "123e4567-e89b-12d3-a456-426614174000",
|
||||
"SourceID": "WH-A-001",
|
||||
"TargetID": "WH-B-002",
|
||||
"TaskType": "GT2MP"
|
||||
}
|
||||
```
|
||||
|
||||
#### 响应参数
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"reqCode": "123e4567-e89b-12d3-a456-426614174000",
|
||||
"message": "成功",
|
||||
"rowCount": 1
|
||||
}
|
||||
```
|
||||
|
||||
#### 错误响应
|
||||
|
||||
1. 不支持的任务类型时:
|
||||
```json
|
||||
{
|
||||
"code": 400,
|
||||
"reqCode": "123e4567-e89b-12d3-a456-426614174000",
|
||||
"message": "不支持的任务类型: UNKNOWN_TYPE",
|
||||
"rowCount": 0
|
||||
}
|
||||
```
|
||||
|
||||
2. 任务启动失败时:
|
||||
```json
|
||||
{
|
||||
"code": 500,
|
||||
"reqCode": "123e4567-e89b-12d3-a456-426614174000",
|
||||
"message": "任务启动失败",
|
||||
"rowCount": 0
|
||||
}
|
||||
```
|
||||
|
||||
3. 系统异常时:
|
||||
```json
|
||||
{
|
||||
"code": 500,
|
||||
"reqCode": "123e4567-e89b-12d3-a456-426614174000",
|
||||
"message": "创建任务失败: [详细错误信息]",
|
||||
"rowCount": 0
|
||||
}
|
||||
```
|
||||
|
||||
### 2. AGV调度任务 (GenAgvSchedulingTask)
|
||||
|
||||
#### 接口描述
|
||||
用于生成AGV调度任务,支持更丰富的调度参数配置,包括位置路径、安全密钥验证等。
|
||||
|
||||
#### 请求方式
|
||||
- **HTTP方法**: POST
|
||||
- **接口路径**: `/GenAgvSchedulingTask`
|
||||
|
||||
#### 请求参数
|
||||
|
||||
| 参数名 | 类型 | 必填 | 描述 |
|
||||
|-------|------|-----|------|
|
||||
| ReqCode | String | 是 | 请求唯一标识码 |
|
||||
| TaskTyp | String | 是 | 任务类型(使用TaskTypeEnum中的值) |
|
||||
| SecurityKey | String | 是 | 安全密钥 |
|
||||
| Type | String | 是 | 类型标识 |
|
||||
| TaskCode | String | 是 | 任务代码 |
|
||||
| SubType | String | 是 | 子类型标识 |
|
||||
| AreaPositonCode | String | 是 | 区域位置代码 |
|
||||
| AreaPositonName | String | 是 | 区域位置名称 |
|
||||
| PositionCodePath | Array<PositionCodePath> | 是 | 位置代码路径 |
|
||||
| ClientCode | String | 否 | 客户端代码 |
|
||||
| TokenCode | String | 否 | 令牌代码 |
|
||||
|
||||
#### PositionCodePath 参数结构
|
||||
|
||||
| 参数名 | 类型 | 必填 | 描述 |
|
||||
|-------|------|-----|------|
|
||||
| PositionCode | String | 是 | 位置代码 |
|
||||
| Type | String | 是 | 类型 |
|
||||
|
||||
#### 请求示例
|
||||
|
||||
```json
|
||||
{
|
||||
"ReqCode": "a98334b940af48328290389a64b71bcc",
|
||||
"TaskTyp": "MP2GGFK",
|
||||
"SecurityKey": "2JN4YW5IJSQRXDEPBT3YQKEGJMT2GS1X",
|
||||
"Type": "1",
|
||||
"TaskCode": "d074b178e9c54a72a484a782d8955aa3",
|
||||
"SubType": "3",
|
||||
"AreaPositonCode": "1-1",
|
||||
"AreaPositonName": "1-1,ZK",
|
||||
"PositionCodePath": [
|
||||
{
|
||||
"PositionCode": "AP190",
|
||||
"Type": "00"
|
||||
},
|
||||
{
|
||||
"PositionCode": "AP313",
|
||||
"Type": "04"
|
||||
}
|
||||
],
|
||||
"ClientCode": "",
|
||||
"TokenCode": ""
|
||||
}
|
||||
```
|
||||
|
||||
#### 响应参数
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"reqCode": "a98334b940af48328290389a64b71bcc",
|
||||
"message": "成功",
|
||||
"rowCount": 0
|
||||
}
|
||||
```
|
||||
|
||||
#### 错误响应
|
||||
|
||||
1. 安全密钥为空时:
|
||||
```json
|
||||
{
|
||||
"code": 400,
|
||||
"reqCode": "a98334b940af48328290389a64b71bcc",
|
||||
"message": "安全密钥不能为空",
|
||||
"rowCount": 0
|
||||
}
|
||||
```
|
||||
|
||||
2. 不支持的任务类型时:
|
||||
```json
|
||||
{
|
||||
"code": 400,
|
||||
"reqCode": "a98334b940af48328290389a64b71bcc",
|
||||
"message": "不支持的任务类型: UNKNOWN_TYPE",
|
||||
"rowCount": 0
|
||||
}
|
||||
```
|
||||
|
||||
3. 任务启动失败时:
|
||||
```json
|
||||
{
|
||||
"code": 500,
|
||||
"reqCode": "a98334b940af48328290389a64b71bcc",
|
||||
"message": "任务启动失败",
|
||||
"rowCount": 0
|
||||
}
|
||||
```
|
||||
|
||||
4. 系统异常时:
|
||||
```json
|
||||
{
|
||||
"code": 500,
|
||||
"reqCode": "a98334b940af48328290389a64b71bcc",
|
||||
"message": "创建任务失败: [详细错误信息]",
|
||||
"rowCount": 0
|
||||
}
|
||||
```
|
||||
|
||||
## 通用说明
|
||||
|
||||
### 响应码说明
|
||||
|
||||
| 响应码 | 描述 |
|
||||
|-------|------|
|
||||
| 0 | 成功 |
|
||||
| 400 | 请求参数错误 |
|
||||
| 404 | 资源不存在 |
|
||||
| 409 | 冲突(如任务已在运行) |
|
||||
| 500 | 服务器内部错误 |
|
||||
|
||||
### 安全说明
|
||||
|
||||
1. 所有接口都需要提供有效的请求标识码(ReqCode)
|
||||
2. AGV调度任务接口需要提供有效的安全密钥(SecurityKey)
|
||||
3. 系统会记录所有请求的客户端信息和来源
|
||||
4. 建议在生产环境中启用HTTPS
|
||||
|
||||
### 调用注意事项
|
||||
|
||||
1. 请求标识码(ReqCode)应保证全局唯一性
|
||||
2. 任务类型必须在支持的枚举范围内
|
||||
3. 系统会根据任务类型自动选择对应的任务模板
|
||||
4. 位置代码路径用于指定AGV的行驶路径
|
||||
5. 系统支持并发调用,但相同设备的相同任务可能有限制
|
||||
|
||||
### 监控和日志
|
||||
|
||||
1. 所有接口调用都会记录详细日志
|
||||
2. 支持通过ReqCode追踪任务执行状态
|
||||
3. 错误信息会包含足够的上下文用于排查问题
|
||||
4. 建议定期监控接口响应时间和成功率
|
@ -22,7 +22,13 @@
|
||||
|
||||
### 1. 获取库位列表 (GET /api/vwed-operate-point/list)
|
||||
|
||||
获取库位列表,支持多种筛选条件和分页查询。
|
||||
获取库位列表,支持多种筛选条件、排序功能和分页查询。
|
||||
|
||||
**排序功能说明:**
|
||||
- `layer_name_sort`:支持按层名称排序,如 GSD_1_1_1 格式,先按数字部分排序,然后按字母部分排序
|
||||
- `station_name_sort`:支持按站点名称排序,如 AP1 格式,先按数字部分排序,然后按字母部分排序
|
||||
- 排序规则:首先按数字排序,如果数字都一样的话,按照字母排序
|
||||
- 可以同时使用两种排序,优先按 layer_name_sort 排序,然后按 station_name_sort 排序
|
||||
|
||||
#### 请求参数
|
||||
|
||||
@ -38,6 +44,8 @@
|
||||
| is_empty_tray | boolean | 否 | 是否空托盘 |
|
||||
| include_operate_point_info | boolean | 否 | 是否包含动作点信息(默认true) |
|
||||
| include_extended_fields | boolean | 否 | 是否包含扩展字段(默认true) |
|
||||
| layer_name_sort | boolean | 否 | 层名称排序:true(升序,默认)、false(降序) |
|
||||
| station_name_sort | boolean | 否 | 站点名称排序:true(升序,默认)、false(降序) |
|
||||
| page | integer | 否 | 页码(默认1) |
|
||||
| page_size | integer | 否 | 每页数量(默认20,最大100) |
|
||||
|
||||
@ -152,10 +160,23 @@
|
||||
|
||||
#### 调用示例
|
||||
|
||||
**基本查询:**
|
||||
```bash
|
||||
GET /api/vwed-operate-point/list?scene_id=scene-001&is_occupied=false&page=1&page_size=20
|
||||
```
|
||||
|
||||
**带排序的查询:**
|
||||
```bash
|
||||
# 按层名称升序排序
|
||||
GET /api/vwed-operate-point/list?layer_name_sort=true&page=1&page_size=20
|
||||
|
||||
# 按站点名称降序排序
|
||||
GET /api/vwed-operate-point/list?station_name_sort=false&page=1&page_size=20
|
||||
|
||||
# 组合排序:先按层名称升序,再按站点名称降序
|
||||
GET /api/vwed-operate-point/list?layer_name_sort=true&station_name_sort=false&page=1&page_size=20
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. 更新库位状态 (PUT /api/vwed-operate-point/status)
|
||||
|
BIN
__pycache__/app.cpython-311.pyc
Normal file
BIN
__pycache__/app.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
4
app.py
4
app.py
@ -63,8 +63,8 @@ if __name__ == "__main__":
|
||||
# 从环境变量中获取端口,默认为8000
|
||||
import time
|
||||
# start_time = time.time()
|
||||
port = int(os.environ.get("PORT", settings.SERVER_PORT))
|
||||
|
||||
# port = int(os.environ.get("PORT", settings.SERVER_PORT))
|
||||
port = 8001
|
||||
# 打印启动配置信息
|
||||
logger.info(f"服务器配置 - Host: 0.0.0.0, Port: {port}, Workers: {settings.SERVER_WORKERS}, Reload: {settings.SERVER_RELOAD}")
|
||||
end_time = time.time()
|
||||
|
BIN
config/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
config/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
config/__pycache__/error_code_mapping.cpython-311.pyc
Normal file
BIN
config/__pycache__/error_code_mapping.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
config/__pycache__/settings.cpython-313.pyc
Normal file
BIN
config/__pycache__/settings.cpython-313.pyc
Normal file
Binary file not shown.
BIN
config/__pycache__/tf_api_config.cpython-311.pyc
Normal file
BIN
config/__pycache__/tf_api_config.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
@ -53,25 +53,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "preBinTask",
|
||||
"type": "String",
|
||||
"label": "预置binTask",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValue": null,
|
||||
"options": [
|
||||
{
|
||||
"name": "param",
|
||||
"type": "String",
|
||||
"label": "参数",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValue": null,
|
||||
"options": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "pick",
|
||||
"type": "String",
|
||||
@ -100,25 +81,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "JackHeight",
|
||||
"type": "String",
|
||||
"label": "设置顶升高度 JackHeight",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValue": null,
|
||||
"options": [
|
||||
{
|
||||
"name": "targetHeight",
|
||||
"type": "Double",
|
||||
"label": "目标高度",
|
||||
"description": "",
|
||||
"required": true,
|
||||
"defaultValue": null,
|
||||
"options": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "drop",
|
||||
"type": "String",
|
||||
@ -156,238 +118,6 @@
|
||||
"options": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "ForkLoad",
|
||||
"type": "String",
|
||||
"label": "叉车取货 ForkLoad",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValue": null,
|
||||
"options": [
|
||||
{
|
||||
"name": "start_height",
|
||||
"type": "Double",
|
||||
"label": "起始高度",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValue": null,
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "end_height",
|
||||
"type": "Double",
|
||||
"label": "结束高度",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValue": null,
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "inspired_unique",
|
||||
"type": "Boolean",
|
||||
"label": "启用识别",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValue": null,
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "unique_file",
|
||||
"type": "String",
|
||||
"label": "识别文件",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValue": null,
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "special_height",
|
||||
"type": "Double",
|
||||
"label": "识别后高度",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValue": null,
|
||||
"options": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "ForkUnload",
|
||||
"type": "String",
|
||||
"label": "降低叉齿 ForkUnload",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValue": null,
|
||||
"options": [
|
||||
{
|
||||
"name": "start_height",
|
||||
"type": "Double",
|
||||
"label": "起始高度",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValue": null,
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "end_height",
|
||||
"type": "Double",
|
||||
"label": "结束高度",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValue": null,
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "inspired_unique",
|
||||
"type": "Boolean",
|
||||
"label": "启用识别",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValue": null,
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "unique_file",
|
||||
"type": "String",
|
||||
"label": "识别文件",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValue": null,
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "special_height",
|
||||
"type": "Double",
|
||||
"label": "识别后高度",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValue": null,
|
||||
"options": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "ForkHeight",
|
||||
"type": "String",
|
||||
"label": "边走边升降叉齿 ForkHeight",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValue": null,
|
||||
"options": [
|
||||
{
|
||||
"name": "start_height",
|
||||
"type": "Double",
|
||||
"label": "起始高度",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValue": null,
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "fork_height",
|
||||
"type": "Double",
|
||||
"label": "货叉行走过程中举升高度",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValue": null,
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "end_height",
|
||||
"type": "Double",
|
||||
"label": "结束高度",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValue": null,
|
||||
"options": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "CustomCommand",
|
||||
"type": "String",
|
||||
"label": "自定义指令",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValue": null,
|
||||
"options": [
|
||||
{
|
||||
"name": "command",
|
||||
"type": "String",
|
||||
"label": "指令方式",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValue": null,
|
||||
"options": [
|
||||
{
|
||||
"name": "operation_name",
|
||||
"type": "String",
|
||||
"label": "操作名",
|
||||
"description": "",
|
||||
"required": true,
|
||||
"defaultValue": null,
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "param",
|
||||
"type": "JSONArray",
|
||||
"label": "属性",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValue": null,
|
||||
"options": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "syspy/setDO.py",
|
||||
"type": "String",
|
||||
"label": "syspy/setDO.py",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValue": null,
|
||||
"options": [
|
||||
{
|
||||
"name": "DO",
|
||||
"type": "JSONArray",
|
||||
"label": "设置DO",
|
||||
"description": "",
|
||||
"required": true,
|
||||
"defaultValue": null,
|
||||
"options": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "syspy/waitDO.py",
|
||||
"type": "String",
|
||||
"label": "syspy/waitDO.py",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValue": null,
|
||||
"options": [
|
||||
{
|
||||
"name": "DO",
|
||||
"type": "JSONArray",
|
||||
"label": "设置DO",
|
||||
"description": "",
|
||||
"required": true,
|
||||
"defaultValue": null,
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "timeout",
|
||||
"type": "Integer",
|
||||
"label": "超时时间(秒)",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValue": null,
|
||||
"options": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -239,7 +239,7 @@
|
||||
"label": "是否为降序",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValue": false,
|
||||
"defaultValue": null,
|
||||
"options": []
|
||||
}
|
||||
],
|
||||
@ -399,7 +399,7 @@
|
||||
"extraInputParamsFunc": "",
|
||||
"outputParams": {},
|
||||
"contextVariables": {
|
||||
"site": {
|
||||
"siteId": {
|
||||
"type": "Object",
|
||||
"label": "选出的库位",
|
||||
"description": null,
|
||||
@ -564,7 +564,7 @@
|
||||
"label": "加锁者",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValue": true,
|
||||
"defaultValue": "",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
@ -645,7 +645,7 @@
|
||||
"label": "解锁者",
|
||||
"description": "",
|
||||
"required": false,
|
||||
"defaultValue": true,
|
||||
"defaultValue": "",
|
||||
"options": []
|
||||
}
|
||||
],
|
||||
|
@ -27,8 +27,9 @@ class DBConfig:
|
||||
"pool_size": settings.DB_POOL_SIZE,
|
||||
"max_overflow": settings.DB_MAX_OVERFLOW,
|
||||
"pool_recycle": settings.DB_POOL_RECYCLE,
|
||||
"pool_timeout": settings.DB_POOL_TIMEOUT,
|
||||
"echo": False, # 强制关闭SQL日志输出
|
||||
"pool_pre_ping": True
|
||||
"pool_pre_ping": settings.DB_POOL_PRE_PING
|
||||
}
|
||||
|
||||
@classmethod
|
||||
|
@ -245,9 +245,11 @@ class BaseConfig(BaseSettings):
|
||||
DB_NAME: str = Field(default=_db_config['database'], env="DB_NAME")
|
||||
DB_CHARSET: str = Field(default=_db_config['charset'], env="DB_CHARSET")
|
||||
DB_ECHO: bool = False # 是否输出SQL语句
|
||||
DB_POOL_SIZE: int = 10
|
||||
DB_MAX_OVERFLOW: int = 20
|
||||
DB_POOL_RECYCLE: int = 3600 # 连接池回收时间,防止连接过期
|
||||
DB_POOL_SIZE: int = 50 # 增加连接池基础大小
|
||||
DB_MAX_OVERFLOW: int = 100 # 增加溢出连接数
|
||||
DB_POOL_RECYCLE: int = 1800 # 减少连接回收时间,防止连接过期
|
||||
DB_POOL_TIMEOUT: int = 60 # 获取连接的超时时间
|
||||
DB_POOL_PRE_PING: bool = True # 连接前检测连接可用性
|
||||
|
||||
# Redis配置
|
||||
_redis_config = get_config_for_env(_env, 'redis')
|
||||
@ -326,8 +328,8 @@ class BaseConfig(BaseSettings):
|
||||
TASK_EXPORT_IV: str = Field(default="vwed1234task5678", env="TASK_EXPORT_IV") # 初始化向量
|
||||
|
||||
# 增强版任务调度器配置
|
||||
TASK_SCHEDULER_MIN_WORKER_COUNT: int = 15 # 最小工作线程数
|
||||
TASK_SCHEDULER_MAX_WORKER_COUNT: int = 30 # 最大工作线程数
|
||||
TASK_SCHEDULER_MIN_WORKER_COUNT: int = 100 # 最小工作线程数
|
||||
TASK_SCHEDULER_MAX_WORKER_COUNT: int = 150 # 最大工作线程数
|
||||
TASK_SCHEDULER_QUEUE_COUNT: int = 3 # 队列数量
|
||||
TASK_SCHEDULER_QUEUE_THRESHOLD_PERCENTILES: List[float] = [0.1, 0.3, 1.0] # 队列阈值百分比配置
|
||||
TASK_SCHEDULER_WORKER_RATIOS: List[float] = [0.6, 0.3, 0.1] # 工作线程分配比例
|
||||
@ -341,13 +343,10 @@ class BaseConfig(BaseSettings):
|
||||
TASK_SCHEDULER_CPU_THRESHOLD: float = 80.0 # CPU使用率阈值(百分比)
|
||||
TASK_SCHEDULER_MEMORY_THRESHOLD: float = 80.0 # 内存使用率阈值(百分比)
|
||||
TASK_SCHEDULER_AUTO_SCALE_INTERVAL: int = 120 # 自动扩缩容间隔(秒)
|
||||
TASK_SCHEDULER_WORKER_HEARTBEAT_INTERVAL: int = 120 # 心跳间隔(秒)
|
||||
TASK_SCHEDULER_WORKER_HEARTBEAT_INTERVAL: int = 1200 # 心跳间隔(秒)
|
||||
|
||||
# 告警同步配置
|
||||
ALERT_SYNC_ENABLED: bool = Field(default=True, env="ALERT_SYNC_ENABLED") # 是否启用告警同步
|
||||
ALERT_SYNC_HOST: str = Field(default="192.168.189.80", env="ALERT_SYNC_HOST") # 主系统IP
|
||||
ALERT_SYNC_PORT: int = Field(default=8080, env="ALERT_SYNC_PORT") # 主系统端口
|
||||
ALERT_SYNC_API_PATH: str = Field(default="/jeecg-boot/warning", env="ALERT_SYNC_API_PATH") # 告警API路径
|
||||
ALERT_SYNC_TIMEOUT: int = Field(default=10, env="ALERT_SYNC_TIMEOUT") # 请求超时时间(秒)
|
||||
ALERT_SYNC_RETRY_COUNT: int = Field(default=3, env="ALERT_SYNC_RETRY_COUNT") # 重试次数
|
||||
ALERT_SYNC_RETRY_DELAY: int = Field(default=1, env="ALERT_SYNC_RETRY_DELAY") # 重试延迟(秒)
|
||||
@ -365,6 +364,14 @@ class BaseConfig(BaseSettings):
|
||||
MAP_GENERAL_STORAGE_CAPACITY_PER_POINT: int = Field(default=15, env="MAP_GENERAL_STORAGE_CAPACITY_PER_POINT") # 一般库区每个动作点增加的容量
|
||||
MAP_GENERAL_STORAGE_LAYER_MULTIPLIER: float = Field(default=1.2, env="MAP_GENERAL_STORAGE_LAYER_MULTIPLIER") # 一般库区分层倍数
|
||||
|
||||
# 库位获取队列配置
|
||||
STORAGE_QUEUE_MAX_WORKERS: int = Field(default=10, env="STORAGE_QUEUE_MAX_WORKERS") # 队列工作者数量
|
||||
STORAGE_QUEUE_MAX_SIZE: int = Field(default=2000, env="STORAGE_QUEUE_MAX_SIZE") # 队列最大数量
|
||||
STORAGE_QUEUE_ENABLE_TIMEOUT: bool = Field(default=False, env="STORAGE_QUEUE_ENABLE_TIMEOUT") # 是否启用超时
|
||||
STORAGE_QUEUE_DEFAULT_TIMEOUT: int = Field(default=3600, env="STORAGE_QUEUE_DEFAULT_TIMEOUT") # 默认超时时间(秒),仅在启用超时时生效
|
||||
STORAGE_QUEUE_CLEANUP_INTERVAL: int = Field(default=300, env="STORAGE_QUEUE_CLEANUP_INTERVAL") # 清理已完成请求的间隔(秒)
|
||||
STORAGE_QUEUE_COMPLETED_REQUEST_TTL: int = Field(default=3600, env="STORAGE_QUEUE_COMPLETED_REQUEST_TTL") # 已完成请求保留时间(秒)
|
||||
|
||||
@property
|
||||
def DATABASE_URL(self) -> str:
|
||||
"""构建数据库连接URL"""
|
||||
@ -396,10 +403,10 @@ class BaseConfig(BaseSettings):
|
||||
return f"redis://:{encoded_password}@{self.REDIS_HOST}:{self.REDIS_PORT}/{self.REDIS_DB}"
|
||||
return f"redis://{self.REDIS_HOST}:{self.REDIS_PORT}/{self.REDIS_DB}"
|
||||
|
||||
@property
|
||||
def ALERT_SYNC_URL(self) -> str:
|
||||
"""构建告警同步URL"""
|
||||
return f"http://{self.ALERT_SYNC_HOST}:{self.ALERT_SYNC_PORT}{self.ALERT_SYNC_API_PATH}"
|
||||
# @property
|
||||
# def ALERT_SYNC_URL(self) -> str:
|
||||
# """构建告警同步URL"""
|
||||
# return f"http://{self.ALERT_SYNC_HOST}:{self.ALERT_SYNC_PORT}{self.ALERT_SYNC_API_PATH}"
|
||||
|
||||
# 更新为Pydantic v2的配置方式
|
||||
model_config = {
|
||||
@ -423,6 +430,11 @@ class DevelopmentConfig(BaseConfig):
|
||||
MAP_DENSE_STORAGE_CAPACITY_PER_POINT: int = 5
|
||||
MAP_GENERAL_STORAGE_BASE_CAPACITY: int = 15
|
||||
MAP_GENERAL_STORAGE_CAPACITY_PER_POINT: int = 8
|
||||
|
||||
# 开发环境队列配置
|
||||
# STORAGE_QUEUE_MAX_WORKERS: int = 5
|
||||
# STORAGE_QUEUE_MAX_SIZE: int = 500
|
||||
# STORAGE_QUEUE_ENABLE_TIMEOUT: bool = False # 开发环境也不启用超时
|
||||
|
||||
|
||||
# 根据环境变量选择配置
|
||||
|
@ -20,7 +20,8 @@ tf_api_endpoints = {
|
||||
"set_task_in_progress": "/task/vwedtask/{id}/inprogress",
|
||||
"set_task_completed": "/task/vwedtask/{id}/completed",
|
||||
"set_task_terminated": "/task/vwedtask/{id}/terminated",
|
||||
"set_task_failed": "/task/vwedtask/{id}/failed"
|
||||
"set_task_failed": "/task/vwedtask/{id}/failed",
|
||||
"set_task_description":"/task/vwedtask/{id}/description"
|
||||
}
|
||||
|
||||
# 系统内部服务API HTTP方法配置
|
||||
@ -34,18 +35,42 @@ tf_api_methods = {
|
||||
"set_task_in_progress": "PUT",
|
||||
"set_task_completed": "PUT",
|
||||
"set_task_terminated": "PUT",
|
||||
"set_task_failed": "PUT"
|
||||
"set_task_failed": "PUT",
|
||||
"set_task_description": "PUT"
|
||||
}
|
||||
# 外部任务类型对应的优先级
|
||||
TASK_TYPE_PRIORITY={
|
||||
"GT": "6", # 缸体
|
||||
"GG": "5", # 缸盖
|
||||
"QZ": "4", # 曲轴
|
||||
"ZG": "3",#
|
||||
"PHZ": "2",
|
||||
"LG": "1",
|
||||
"OR": "1"
|
||||
}
|
||||
# 外部任务对应所属库区
|
||||
TASK_TYPE_AREA={
|
||||
"GT": "ZK/ZKG",
|
||||
"GG": "ZK/ZKG",
|
||||
"QZ": "KW",
|
||||
"ZG": "ZK/ZKG",
|
||||
"PHZ": "AGW/PL",
|
||||
"LG": "AGW/PL"
|
||||
}
|
||||
|
||||
# 从环境变量读取配置,或使用默认值
|
||||
# TF_API_BASE_URL = os.getenv("TF_API_BASE_URL", "http://192.168.189.80:8080/jeecg-boot")
|
||||
TF_API_BASE_URL = os.getenv("TF_API_BASE_URL", "http://111.231.146.230:4080/jeecg-boot")
|
||||
TF_API_TIMEOUT = int(os.getenv("TF_API_TIMEOUT", "60"))
|
||||
TF_API_TIMEOUT = int(os.getenv("TF_API_TIMEOUT", "10")) # 减少超时时间从60秒到10秒
|
||||
TF_API_RETRY_TIMES = int(os.getenv("TF_API_RETRY_TIMES", "3"))
|
||||
TF_API_MOCK_MODE = False
|
||||
TF_API_TOKEN_HEADER = os.getenv("TF_API_TOKEN_HEADER", "X-Access-Token") # token请求头名称
|
||||
TF_API_TOKEN = os.getenv("TF_API_TOKEN", "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3NDk3NzY1MzEsInVzZXJuYW1lIjoiYWRtaW4ifQ.uRLHZuRQTrR2fHyA-dMzP46yXAa5wdjfdUcmr9PNY4g")
|
||||
|
||||
TF_WEB_POST = False # 是否过账
|
||||
sync_disabled_label = "sync_disabled" # 是否限制接口调用的变量名
|
||||
# 外部接口叫料模板id
|
||||
CM_ID = "571985c1-cfa5-4186-8acd-6e3868a5e08c"
|
||||
# 送货模板id
|
||||
DG_ID = "e22cacb4-a580-45ba-949e-356f57fa1a43"
|
||||
def get_tf_api_config() -> Dict[str, Any]:
|
||||
"""获取天风系统API配置"""
|
||||
return {
|
||||
|
BIN
data/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
data/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
data/__pycache__/session.cpython-313.pyc
Normal file
BIN
data/__pycache__/session.cpython-313.pyc
Normal file
Binary file not shown.
BIN
data/enum/__pycache__/call_device_enum.cpython-311.pyc
Normal file
BIN
data/enum/__pycache__/call_device_enum.cpython-311.pyc
Normal file
Binary file not shown.
BIN
data/enum/__pycache__/modbus_config_enum.cpython-311.pyc
Normal file
BIN
data/enum/__pycache__/modbus_config_enum.cpython-311.pyc
Normal file
Binary file not shown.
BIN
data/enum/__pycache__/task_block_record_enum.cpython-311.pyc
Normal file
BIN
data/enum/__pycache__/task_block_record_enum.cpython-311.pyc
Normal file
Binary file not shown.
BIN
data/enum/__pycache__/task_input_param_enum.cpython-311.pyc
Normal file
BIN
data/enum/__pycache__/task_input_param_enum.cpython-311.pyc
Normal file
Binary file not shown.
BIN
data/enum/__pycache__/task_record_enum.cpython-311.pyc
Normal file
BIN
data/enum/__pycache__/task_record_enum.cpython-311.pyc
Normal file
Binary file not shown.
@ -23,6 +23,7 @@ from data.models.operate_point import OperatePoint
|
||||
from data.models.operate_point_layer import OperatePointLayer
|
||||
from data.models.extended_property import ExtendedProperty, ExtendedPropertyTypeEnum
|
||||
from data.models.storage_location_log import StorageLocationLog
|
||||
from data.models.external_task_record import VWEDExternalTaskRecord, ExternalTaskTypeEnum, ExternalTaskStatusEnum
|
||||
|
||||
# 导出所有模型供应用程序使用
|
||||
__all__ = [
|
||||
@ -31,5 +32,6 @@ __all__ = [
|
||||
'VWEDScript', 'VWEDScriptVersion', 'VWEDScriptLog', 'VWEDModbusConfig',
|
||||
'VWEDCallDevice', 'VWEDCallDeviceButton', 'InterfaceDefHistory',
|
||||
'StorageArea', 'StorageAreaType', 'OperatePoint', 'OperatePointLayer',
|
||||
'ExtendedProperty', 'ExtendedPropertyTypeEnum', 'StorageLocationLog'
|
||||
'ExtendedProperty', 'ExtendedPropertyTypeEnum', 'StorageLocationLog',
|
||||
'VWEDExternalTaskRecord', 'ExternalTaskTypeEnum', 'ExternalTaskStatusEnum'
|
||||
]
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
data/models/__pycache__/calldevice.cpython-311.pyc
Normal file
BIN
data/models/__pycache__/calldevice.cpython-311.pyc
Normal file
Binary file not shown.
BIN
data/models/__pycache__/extended_property.cpython-311.pyc
Normal file
BIN
data/models/__pycache__/extended_property.cpython-311.pyc
Normal file
Binary file not shown.
BIN
data/models/__pycache__/external_task_record.cpython-312.pyc
Normal file
BIN
data/models/__pycache__/external_task_record.cpython-312.pyc
Normal file
Binary file not shown.
BIN
data/models/__pycache__/interfacedef.cpython-311.pyc
Normal file
BIN
data/models/__pycache__/interfacedef.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
data/models/__pycache__/operate_point.cpython-311.pyc
Normal file
BIN
data/models/__pycache__/operate_point.cpython-311.pyc
Normal file
Binary file not shown.
BIN
data/models/__pycache__/operate_point_layer.cpython-311.pyc
Normal file
BIN
data/models/__pycache__/operate_point_layer.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
data/models/__pycache__/storage_area.cpython-311.pyc
Normal file
BIN
data/models/__pycache__/storage_area.cpython-311.pyc
Normal file
Binary file not shown.
BIN
data/models/__pycache__/storage_location_log.cpython-311.pyc
Normal file
BIN
data/models/__pycache__/storage_location_log.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
137
data/models/external_task_record.py
Normal file
137
data/models/external_task_record.py
Normal file
@ -0,0 +1,137 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
外部任务执行记录模型
|
||||
用于记录外部接口调用的任务执行情况
|
||||
"""
|
||||
|
||||
import datetime
|
||||
from sqlalchemy import Column, String, Integer, Text, Boolean, Index, Enum as SQLEnum
|
||||
from sqlalchemy.dialects.mysql import DATETIME, LONGTEXT
|
||||
from data.models.base import BaseModel
|
||||
import enum
|
||||
|
||||
class ExternalTaskTypeEnum(enum.Enum):
|
||||
"""外部任务类型枚举"""
|
||||
NEW_TASK = "newTask" # /newTask 接口
|
||||
GEN_AGV_SCHEDULING_TASK = "GenAgvSchedulingTask" # /GenAgvSchedulingTask 接口
|
||||
|
||||
class ExternalTaskStatusEnum(enum.Enum):
|
||||
"""外部任务状态枚举"""
|
||||
PENDING = "pending" # 待执行
|
||||
RUNNING = "running" # 执行中
|
||||
SUCCESS = "success" # 执行成功
|
||||
FAILED = "failed" # 执行失败
|
||||
CANCELLED = "cancelled" # 已取消
|
||||
|
||||
class VWEDExternalTaskRecord(BaseModel):
|
||||
"""
|
||||
外部任务执行记录模型
|
||||
对应vwed_external_task_record表
|
||||
功能:记录外部接口调用的任务执行情况和关联关系
|
||||
"""
|
||||
__tablename__ = 'vwed_external_task_record'
|
||||
|
||||
__table_args__ = (
|
||||
Index('idx_external_task_req_code', 'req_code'),
|
||||
Index('idx_external_task_task_code', 'task_code'),
|
||||
Index('idx_external_task_type', 'task_type'),
|
||||
Index('idx_external_task_status', 'task_status'),
|
||||
Index('idx_external_task_record_id', 'task_record_id'),
|
||||
Index('idx_external_task_related_req_code', 'related_req_code'),
|
||||
Index('idx_external_task_created_at', 'created_at'),
|
||||
{
|
||||
'mysql_engine': 'InnoDB',
|
||||
'mysql_charset': 'utf8mb4',
|
||||
'mysql_collate': 'utf8mb4_general_ci',
|
||||
'info': {'order_by': 'created_at DESC'}
|
||||
}
|
||||
)
|
||||
|
||||
# 主键和基本信息
|
||||
id = Column(String(255), primary_key=True, nullable=False, comment='主键ID')
|
||||
req_code = Column(String(255), nullable=False, comment='请求标识码(ReqCode)')
|
||||
task_type = Column(SQLEnum(ExternalTaskTypeEnum), nullable=False, comment='外部任务类型(newTask或GenAgvSchedulingTask)')
|
||||
task_status = Column(SQLEnum(ExternalTaskStatusEnum), nullable=False, default=ExternalTaskStatusEnum.PENDING, comment='任务状态')
|
||||
|
||||
# 关联字段 - 用于两个任务之间的关联
|
||||
task_code = Column(String(255), comment='任务代码(TaskCode,GenAgvSchedulingTask专用)')
|
||||
related_req_code = Column(String(255), comment='关联的ReqCode(用于关联newTask和GenAgvSchedulingTask)')
|
||||
|
||||
# TaskEditService.run_task 返回的任务记录ID
|
||||
task_record_id = Column(String(255), comment='内部任务记录ID(用于检测任务是否结束)')
|
||||
|
||||
# newTask 接口专用字段
|
||||
source_id = Column(String(255), comment='来源ID(SourceID)')
|
||||
target_id = Column(String(255), comment='目标ID(TargetID)')
|
||||
business_task_type = Column(String(50), comment='业务任务类型(TaskType,如GT2MP)')
|
||||
|
||||
# GenAgvSchedulingTask 接口专用字段
|
||||
security_key = Column(String(255), comment='安全密钥(SecurityKey)')
|
||||
type_field = Column(String(50), comment='类型字段(Type)')
|
||||
sub_type = Column(String(50), comment='子类型(SubType)')
|
||||
area_position_code = Column(String(255), comment='区域位置代码(AreaPositonCode)')
|
||||
area_position_name = Column(String(255), comment='区域位置名称(AreaPositonName)')
|
||||
position_code_path = Column(LONGTEXT, comment='位置代码路径JSON(PositionCodePath)')
|
||||
client_code = Column(String(255), comment='客户端代码(ClientCode)')
|
||||
token_code = Column(String(255), comment='令牌代码(TokenCode)')
|
||||
|
||||
# 请求和响应信息
|
||||
request_params = Column(LONGTEXT, comment='完整请求参数JSON')
|
||||
response_data = Column(LONGTEXT, comment='完整响应数据JSON')
|
||||
response_code = Column(Integer, comment='响应状态码')
|
||||
response_message = Column(Text, comment='响应消息')
|
||||
response_row_count = Column(Integer, comment='响应行数(rowCount)')
|
||||
|
||||
# 执行时间信息
|
||||
start_time = Column(DATETIME(fsp=6), comment='任务开始时间')
|
||||
end_time = Column(DATETIME(fsp=6), comment='任务结束时间')
|
||||
duration = Column(Integer, comment='执行时长(毫秒)')
|
||||
|
||||
# 客户端信息
|
||||
client_ip = Column(String(50), comment='客户端IP地址')
|
||||
client_info = Column(Text, comment='客户端信息JSON')
|
||||
|
||||
# 任务模板信息
|
||||
template_id = Column(String(255), comment='使用的任务模板ID')
|
||||
|
||||
# 错误信息
|
||||
error_message = Column(Text, comment='错误信息')
|
||||
error_stack = Column(LONGTEXT, comment='错误堆栈信息')
|
||||
|
||||
# 备注信息
|
||||
remarks = Column(Text, comment='备注信息')
|
||||
|
||||
def __repr__(self):
|
||||
return f"<VWEDExternalTaskRecord(id='{self.id}', req_code='{self.req_code}', task_type='{self.task_type}', status='{self.task_status}')>"
|
||||
|
||||
def is_completed(self):
|
||||
"""判断任务是否已完成(成功或失败)"""
|
||||
return self.task_status in [ExternalTaskStatusEnum.SUCCESS, ExternalTaskStatusEnum.FAILED, ExternalTaskStatusEnum.CANCELLED]
|
||||
|
||||
def update_status(self, status, error_message=None, response_data=None):
|
||||
"""更新任务状态"""
|
||||
self.task_status = status
|
||||
if error_message:
|
||||
self.error_message = error_message
|
||||
if response_data:
|
||||
self.response_data = response_data
|
||||
|
||||
# 更新时间信息
|
||||
now = datetime.datetime.now()
|
||||
if status == ExternalTaskStatusEnum.RUNNING and not self.start_time:
|
||||
self.start_time = now
|
||||
elif status in [ExternalTaskStatusEnum.SUCCESS, ExternalTaskStatusEnum.FAILED, ExternalTaskStatusEnum.CANCELLED]:
|
||||
if not self.end_time:
|
||||
self.end_time = now
|
||||
if self.start_time and not self.duration:
|
||||
self.duration = int((self.end_time - self.start_time).total_seconds() * 1000) # 毫秒
|
||||
|
||||
def get_related_task(self, session):
|
||||
"""获取关联的任务记录"""
|
||||
if not self.related_req_code:
|
||||
return None
|
||||
return session.query(VWEDExternalTaskRecord).filter(
|
||||
VWEDExternalTaskRecord.req_code == self.related_req_code
|
||||
).first()
|
@ -100,6 +100,28 @@ async def get_async_session():
|
||||
await session.close()
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def get_async_session_read_committed():
|
||||
"""
|
||||
获取使用READ COMMITTED隔离级别的异步数据库会话
|
||||
用于需要读取最新已提交数据的场景,解决并发数据同步问题
|
||||
|
||||
Yields:
|
||||
AsyncSession: 异步数据库会话对象
|
||||
"""
|
||||
session = AsyncSessionLocal()
|
||||
try:
|
||||
# 设置事务隔离级别为READ COMMITTED
|
||||
await session.execute(text("SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED"))
|
||||
yield session
|
||||
await session.commit()
|
||||
except Exception as e:
|
||||
await session.rollback()
|
||||
raise e
|
||||
finally:
|
||||
await session.close()
|
||||
|
||||
|
||||
async def get_async_db():
|
||||
"""
|
||||
获取异步数据库会话生成器,用于FastAPI依赖注入
|
||||
|
68229
logs/app.log
68229
logs/app.log
File diff suppressed because it is too large
Load Diff
74182
logs/app.log.1
Normal file
74182
logs/app.log.1
Normal file
File diff suppressed because it is too large
Load Diff
96521
logs/app.log.2
Normal file
96521
logs/app.log.2
Normal file
File diff suppressed because it is too large
Load Diff
56329
logs/app.log.2025-08-03
Normal file
56329
logs/app.log.2025-08-03
Normal file
File diff suppressed because it is too large
Load Diff
1797
logs/app.log.2025-08-05
Normal file
1797
logs/app.log.2025-08-05
Normal file
File diff suppressed because it is too large
Load Diff
14734
logs/app.log.2025-08-06
Normal file
14734
logs/app.log.2025-08-06
Normal file
File diff suppressed because it is too large
Load Diff
106059
logs/app.log.2025-08-07
Normal file
106059
logs/app.log.2025-08-07
Normal file
File diff suppressed because it is too large
Load Diff
194808
logs/app.log.2025-08-08
Normal file
194808
logs/app.log.2025-08-08
Normal file
File diff suppressed because it is too large
Load Diff
21791
logs/app.log.2025-08-10
Normal file
21791
logs/app.log.2025-08-10
Normal file
File diff suppressed because it is too large
Load Diff
55620
logs/app.log.2025-08-11
Normal file
55620
logs/app.log.2025-08-11
Normal file
File diff suppressed because it is too large
Load Diff
2396
logs/app.log.2025-08-12
Normal file
2396
logs/app.log.2025-08-12
Normal file
File diff suppressed because it is too large
Load Diff
BIN
middlewares/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
middlewares/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
BIN
middlewares/__pycache__/error_handlers.cpython-311.pyc
Normal file
BIN
middlewares/__pycache__/error_handlers.cpython-311.pyc
Normal file
Binary file not shown.
BIN
middlewares/__pycache__/request_logger.cpython-311.pyc
Normal file
BIN
middlewares/__pycache__/request_logger.cpython-311.pyc
Normal file
Binary file not shown.
@ -19,6 +19,7 @@ from routes.modbus_config_api import router as modbus_config_router
|
||||
from routes.websocket_api import router as websocket_router
|
||||
from routes.map_data_api import router as map_data_router
|
||||
from routes.operate_point_api import router as operate_point_router
|
||||
from routes.external_task_api import router as external_task_router
|
||||
|
||||
# 路由列表,按照注册顺序排列
|
||||
routers = [
|
||||
@ -33,7 +34,8 @@ routers = [
|
||||
modbus_config_router,
|
||||
websocket_router,
|
||||
map_data_router,
|
||||
operate_point_router
|
||||
operate_point_router,
|
||||
external_task_router
|
||||
]
|
||||
|
||||
def register_routers(app):
|
||||
|
BIN
routes/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
routes/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
routes/__pycache__/calldevice_api.cpython-311.pyc
Normal file
BIN
routes/__pycache__/calldevice_api.cpython-311.pyc
Normal file
Binary file not shown.
BIN
routes/__pycache__/common_api.cpython-311.pyc
Normal file
BIN
routes/__pycache__/common_api.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
routes/__pycache__/database.cpython-311.pyc
Normal file
BIN
routes/__pycache__/database.cpython-311.pyc
Normal file
Binary file not shown.
BIN
routes/__pycache__/external_task_api.cpython-312.pyc
Normal file
BIN
routes/__pycache__/external_task_api.cpython-312.pyc
Normal file
Binary file not shown.
BIN
routes/__pycache__/map_data_api.cpython-311.pyc
Normal file
BIN
routes/__pycache__/map_data_api.cpython-311.pyc
Normal file
Binary file not shown.
BIN
routes/__pycache__/modbus_config_api.cpython-311.pyc
Normal file
BIN
routes/__pycache__/modbus_config_api.cpython-311.pyc
Normal file
Binary file not shown.
BIN
routes/__pycache__/operate_point_api.cpython-311.pyc
Normal file
BIN
routes/__pycache__/operate_point_api.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
routes/__pycache__/script_api.cpython-311.pyc
Normal file
BIN
routes/__pycache__/script_api.cpython-311.pyc
Normal file
Binary file not shown.
BIN
routes/__pycache__/task_api.cpython-311.pyc
Normal file
BIN
routes/__pycache__/task_api.cpython-311.pyc
Normal file
Binary file not shown.
BIN
routes/__pycache__/task_edit_api.cpython-311.pyc
Normal file
BIN
routes/__pycache__/task_edit_api.cpython-311.pyc
Normal file
Binary file not shown.
BIN
routes/__pycache__/task_record_api.cpython-311.pyc
Normal file
BIN
routes/__pycache__/task_record_api.cpython-311.pyc
Normal file
Binary file not shown.
BIN
routes/__pycache__/template_api.cpython-311.pyc
Normal file
BIN
routes/__pycache__/template_api.cpython-311.pyc
Normal file
Binary file not shown.
BIN
routes/__pycache__/websocket_api.cpython-311.pyc
Normal file
BIN
routes/__pycache__/websocket_api.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
1108
routes/external_task_api.py
Normal file
1108
routes/external_task_api.py
Normal file
File diff suppressed because it is too large
Load Diff
BIN
routes/model/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
routes/model/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
BIN
routes/model/__pycache__/base.cpython-311.pyc
Normal file
BIN
routes/model/__pycache__/base.cpython-311.pyc
Normal file
Binary file not shown.
BIN
routes/model/__pycache__/calldevice_model.cpython-311.pyc
Normal file
BIN
routes/model/__pycache__/calldevice_model.cpython-311.pyc
Normal file
Binary file not shown.
BIN
routes/model/__pycache__/external_task_model.cpython-312.pyc
Normal file
BIN
routes/model/__pycache__/external_task_model.cpython-312.pyc
Normal file
Binary file not shown.
BIN
routes/model/__pycache__/map_model.cpython-311.pyc
Normal file
BIN
routes/model/__pycache__/map_model.cpython-311.pyc
Normal file
Binary file not shown.
BIN
routes/model/__pycache__/modbus_config_model.cpython-311.pyc
Normal file
BIN
routes/model/__pycache__/modbus_config_model.cpython-311.pyc
Normal file
Binary file not shown.
BIN
routes/model/__pycache__/operate_point_model.cpython-311.pyc
Normal file
BIN
routes/model/__pycache__/operate_point_model.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
routes/model/__pycache__/script_model.cpython-311.pyc
Normal file
BIN
routes/model/__pycache__/script_model.cpython-311.pyc
Normal file
Binary file not shown.
BIN
routes/model/__pycache__/task_edit_model.cpython-311.pyc
Normal file
BIN
routes/model/__pycache__/task_edit_model.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
routes/model/__pycache__/task_model.cpython-311.pyc
Normal file
BIN
routes/model/__pycache__/task_model.cpython-311.pyc
Normal file
Binary file not shown.
73
routes/model/external_task_model.py
Normal file
73
routes/model/external_task_model.py
Normal file
@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
外部任务接口模型模块
|
||||
包含外部任务创建相关的请求和响应数据模型
|
||||
"""
|
||||
|
||||
from typing import Optional, List, Dict, Any
|
||||
from enum import Enum
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
# 任务类型枚举
|
||||
class TaskTypeEnum(str, Enum):
|
||||
"""任务类型枚举"""
|
||||
GG2MP = "GG2MP" # 高柜到MP
|
||||
GGFK2MP = "GGFK2MP" # 高柜发库到MP
|
||||
GT2MP = "GT2MP" # 高台到MP
|
||||
GTFK2MP = "GTFK2MP" # 高台发库到MP
|
||||
ZG2MP = "ZG2MP" # 中柜到MP
|
||||
QZ2MP = "QZ2MP" # 清洗到MP
|
||||
LG2MP = "LG2MP" # 料柜到MP
|
||||
PHZ2MP = "PHZ2MP" # 配货站到MP
|
||||
MP2GG = "MP2GG" # MP到高柜
|
||||
MP2GGFK = "MP2GGFK" # MP到高柜发库
|
||||
MP2GT = "MP2GT" # MP到高台
|
||||
MP2GTFK = "MP2GTFK" # MP到高台发库
|
||||
MP2ZG = "MP2ZG" # MP到中柜
|
||||
MP2QZ = "MP2QZ" # MP到清洗
|
||||
MP2LG = "MP2LG" # MP到料柜
|
||||
MP2PHZ = "MP2PHZ" # MP到配货站
|
||||
|
||||
# 外部任务创建请求模型
|
||||
class ExternalTaskRequest(BaseModel):
|
||||
"""外部任务创建请求模型"""
|
||||
ReqCode: str = Field(..., description="请求唯一标识码")
|
||||
SourceID: str = Field("", description="来源ID")
|
||||
TargetID: str = Field(..., description="目标ID")
|
||||
TaskType: TaskTypeEnum = Field(..., description="任务类型")
|
||||
|
||||
# 外部任务创建响应模型
|
||||
class ExternalTaskResponse(BaseModel):
|
||||
"""外部任务创建响应模型"""
|
||||
code: int = Field(..., description="响应码,0表示成功")
|
||||
reqCode: str = Field(..., description="请求唯一标识码")
|
||||
message: str = Field(..., description="响应消息")
|
||||
rowCount: int = Field(0, description="影响行数")
|
||||
|
||||
# 位置路径项模型
|
||||
class PositionCodePath1(BaseModel):
|
||||
"""位置路径项模型"""
|
||||
PositionCode: str = Field(..., description="位置代码")
|
||||
Type: str = Field(..., description="类型")
|
||||
|
||||
# AGV调度任务请求模型
|
||||
class GenAgvSchedulingTaskRequest(BaseModel):
|
||||
"""AGV调度任务请求模型"""
|
||||
ReqCode: str = Field("", description="请求唯一标识码")
|
||||
TaskTyp: str = Field("", description="任务类型")
|
||||
SecurityKey: str = Field("", description="安全密钥")
|
||||
Type: str = Field("", description="类型")
|
||||
TaskCode: str = Field("", description="任务代码")
|
||||
SubType: str = Field("", description="子类型")
|
||||
AreaPositonCode: str = Field("", description="区域位置代码")
|
||||
AreaPositonName: str = Field("", description="区域位置名称")
|
||||
PositionCodePath: List[PositionCodePath1] = Field(..., description="位置代码路径")
|
||||
ClientCode: str = Field("", description="客户端代码")
|
||||
TokenCode: str = Field("", description="令牌代码")
|
||||
|
||||
# 取消任务请求模型
|
||||
class CancelTaskRequest(BaseModel):
|
||||
"""取消任务请求模型"""
|
||||
ReqCode: str = Field(..., description="请求唯一标识码")
|
@ -118,6 +118,8 @@ class StorageLocationListRequest(BaseModel):
|
||||
is_empty_tray: Optional[bool] = Field(None, description="是否空托盘")
|
||||
include_operate_point_info: bool = Field(True, description="是否包含动作点信息")
|
||||
include_extended_fields: bool = Field(True, description="是否包含扩展字段")
|
||||
layer_name_sort: Optional[bool] = Field(None, description="层名称排序:true(升序,默认)、false(降序)")
|
||||
station_name_sort: Optional[bool] = Field(None, description="站点名称排序:true(升序,默认)、false(降序)")
|
||||
page: int = Field(1, ge=1, description="页码")
|
||||
page_size: int = Field(20, ge=1, le=100, description="每页数量")
|
||||
|
||||
|
@ -205,6 +205,7 @@ class TaskEditRunRequest(BaseModel):
|
||||
modbus_configs: Optional[List[ModbusConfigParam]] = Field(None, description="任务关联的Modbus配置列表,用于任务执行时的Modbus操作")
|
||||
use_modbus: Optional[bool] = Field(False, description="是否使用Modbus通信")
|
||||
modbus_timeout: Optional[int] = Field(5000, description="Modbus通信超时时间(毫秒)")
|
||||
priority: Optional[int] = Field(1, description="任务优先级 大于等于 1")
|
||||
|
||||
|
||||
# 重新定义更准确的任务参数模型,确保包含所有必要字段
|
||||
|
@ -78,13 +78,15 @@ async def get_storage_location_list(
|
||||
scene_id: Optional[str] = Query(None, description="场景ID"),
|
||||
storage_area_id: Optional[str] = Query(None, description="库区ID"),
|
||||
station_name: Optional[str] = Query(None, description="站点名称(支持模糊搜索)"),
|
||||
layer_name: Optional[str] = Query(None, description="层名称(支持模糊搜索)"),
|
||||
layer_name: Optional[str] = Query(None, description="库位名称(支持模糊搜索)"),
|
||||
is_disabled: Optional[bool] = Query(None, description="是否禁用"),
|
||||
is_occupied: Optional[bool] = Query(None, description="是否占用"),
|
||||
is_locked: Optional[bool] = Query(None, description="是否锁定"),
|
||||
is_empty_tray: Optional[bool] = Query(None, description="是否空托盘"),
|
||||
include_operate_point_info: bool = Query(True, description="是否包含动作点信息"),
|
||||
include_extended_fields: bool = Query(True, description="是否包含扩展字段"),
|
||||
layer_name_sort: Optional[bool] = Query(None, description="层名称排序:true(升序,默认)、false(降序)"),
|
||||
station_name_sort: Optional[bool] = Query(None, description="站点名称排序:true(升序,默认)、false(降序)"),
|
||||
page: int = Query(1, ge=1, description="页码"),
|
||||
page_size: int = Query(20, ge=1, le=100, description="每页数量"),
|
||||
db: Session = Depends(get_db)
|
||||
@ -146,6 +148,8 @@ async def get_storage_location_list(
|
||||
is_empty_tray=is_empty_tray,
|
||||
include_operate_point_info=include_operate_point_info,
|
||||
include_extended_fields=include_extended_fields,
|
||||
layer_name_sort=layer_name_sort,
|
||||
station_name_sort=station_name_sort,
|
||||
page=page,
|
||||
page_size=page_size
|
||||
)
|
||||
|
125
routes/storage_queue_api.py
Normal file
125
routes/storage_queue_api.py
Normal file
@ -0,0 +1,125 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
库位队列管理API
|
||||
提供队列状态监控和管理接口
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, HTTPException, Query
|
||||
from typing import Dict, Any, Optional
|
||||
from services.execution.handlers.storage_queue_manager import storage_queue_manager
|
||||
from utils.api_response import success_response, error_response
|
||||
from utils.logger import get_logger
|
||||
|
||||
logger = get_logger("routes.storage_queue_api")
|
||||
|
||||
router = APIRouter(prefix="/api/storage-queue", tags=["库位队列管理"])
|
||||
|
||||
@router.get("/stats", summary="获取队列统计信息")
|
||||
async def get_queue_stats() -> Dict[str, Any]:
|
||||
"""获取队列统计信息"""
|
||||
try:
|
||||
stats = storage_queue_manager.get_queue_stats()
|
||||
return success_response(data=stats, message="获取队列统计信息成功")
|
||||
except Exception as e:
|
||||
logger.error(f"获取队列统计信息失败: {str(e)}")
|
||||
return error_response(f"获取队列统计信息失败: {str(e)}")
|
||||
|
||||
@router.get("/status/{request_id}", summary="获取请求状态")
|
||||
async def get_request_status(request_id: str) -> Dict[str, Any]:
|
||||
"""获取特定请求的状态"""
|
||||
try:
|
||||
status = await storage_queue_manager.get_request_status(request_id)
|
||||
if status:
|
||||
return success_response(data=status, message="获取请求状态成功")
|
||||
else:
|
||||
return error_response("请求不存在")
|
||||
except Exception as e:
|
||||
logger.error(f"获取请求状态失败: {str(e)}")
|
||||
return error_response(f"获取请求状态失败: {str(e)}")
|
||||
|
||||
@router.post("/cancel/{request_id}", summary="取消请求")
|
||||
async def cancel_request(request_id: str) -> Dict[str, Any]:
|
||||
"""取消指定的请求"""
|
||||
try:
|
||||
success = await storage_queue_manager.cancel_request(request_id)
|
||||
if success:
|
||||
return success_response(message=f"取消请求成功: {request_id}")
|
||||
else:
|
||||
return error_response("请求不存在或无法取消")
|
||||
except Exception as e:
|
||||
logger.error(f"取消请求失败: {str(e)}")
|
||||
return error_response(f"取消请求失败: {str(e)}")
|
||||
|
||||
@router.get("/health", summary="队列健康检查")
|
||||
async def queue_health_check() -> Dict[str, Any]:
|
||||
"""检查队列管理器的健康状态"""
|
||||
try:
|
||||
stats = storage_queue_manager.get_queue_stats()
|
||||
|
||||
# 判断健康状态
|
||||
health_status = "healthy"
|
||||
issues = []
|
||||
|
||||
# 检查队列大小
|
||||
if stats.get('queue_size', 0) > 800: # 队列接近满载
|
||||
health_status = "warning"
|
||||
issues.append("队列接近满载")
|
||||
|
||||
# 检查活跃工作者数量
|
||||
if stats.get('active_workers', 0) == 0:
|
||||
health_status = "critical"
|
||||
issues.append("没有活跃的工作者")
|
||||
|
||||
# 检查失败率
|
||||
total_requests = stats.get('requests_total', 0)
|
||||
if total_requests > 0:
|
||||
failure_rate = stats.get('requests_failed', 0) / total_requests
|
||||
if failure_rate > 0.1: # 失败率超过10%
|
||||
health_status = "warning"
|
||||
issues.append(f"失败率过高: {failure_rate:.2%}")
|
||||
|
||||
return success_response(data={
|
||||
"status": health_status,
|
||||
"issues": issues,
|
||||
"stats": stats
|
||||
}, message="队列健康检查完成")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"队列健康检查失败: {str(e)}")
|
||||
return error_response(f"队列健康检查失败: {str(e)}")
|
||||
|
||||
@router.post("/start", summary="启动队列管理器")
|
||||
async def start_queue_manager() -> Dict[str, Any]:
|
||||
"""启动队列管理器"""
|
||||
try:
|
||||
await storage_queue_manager.start()
|
||||
return success_response(message="队列管理器启动成功")
|
||||
except Exception as e:
|
||||
logger.error(f"启动队列管理器失败: {str(e)}")
|
||||
return error_response(f"启动队列管理器失败: {str(e)}")
|
||||
|
||||
@router.post("/stop", summary="停止队列管理器")
|
||||
async def stop_queue_manager() -> Dict[str, Any]:
|
||||
"""停止队列管理器"""
|
||||
try:
|
||||
await storage_queue_manager.stop()
|
||||
return success_response(message="队列管理器停止成功")
|
||||
except Exception as e:
|
||||
logger.error(f"停止队列管理器失败: {str(e)}")
|
||||
return error_response(f"停止队列管理器失败: {str(e)}")
|
||||
|
||||
@router.get("/config", summary="获取队列配置")
|
||||
async def get_queue_config() -> Dict[str, Any]:
|
||||
"""获取队列管理器配置"""
|
||||
try:
|
||||
config = {
|
||||
"max_workers": storage_queue_manager.max_workers,
|
||||
"max_queue_size": storage_queue_manager.max_queue_size,
|
||||
"handlers_registered": list(storage_queue_manager.handlers.keys())
|
||||
}
|
||||
return success_response(data=config, message="获取队列配置成功")
|
||||
except Exception as e:
|
||||
logger.error(f"获取队列配置失败: {str(e)}")
|
||||
return error_response(f"获取队列配置失败: {str(e)}")
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user