初始提交
This commit is contained in:
commit
a950b043c8
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# 基于编辑器的 HTTP 客户端请求
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
6
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
6
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
</profile>
|
||||
</component>
|
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
7
.idea/misc.xml
generated
Normal file
7
.idea/misc.xml
generated
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Black">
|
||||
<option name="sdkName" value="pytf" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="pytf" project-jdk-type="Python SDK" />
|
||||
</project>
|
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/tianfeng_task.iml" filepath="$PROJECT_DIR$/.idea/tianfeng_task.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
12
.idea/tianfeng_task.iml
generated
Normal file
12
.idea/tianfeng_task.iml
generated
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="jdk" jdkName="pytf" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
<component name="PyDocumentationSettings">
|
||||
<option name="format" value="PLAIN" />
|
||||
<option name="myDocStringFormat" value="Plain" />
|
||||
</component>
|
||||
</module>
|
431
README.md
Normal file
431
README.md
Normal file
@ -0,0 +1,431 @@
|
||||
# 天风任务模块
|
||||
|
||||
## 项目概述
|
||||
|
||||
天风任务模块是AMR(自主移动机器人)调度系统的核心组件之一,用于管理和监控机器人任务的全生命周期。该模块提供了低代码配置工具,使用户能够通过可视化界面设计和配置复杂的机器人任务流程,无需编写大量代码。系统支持任务版本控制、流程编辑、测试执行和源代码生成等功能,大幅提高了机器人任务配置的效率和灵活性。
|
||||
|
||||
## 项目架构
|
||||
|
||||
天风任务模块采用分层架构设计,遵循关注点分离原则,各层次职责明确,便于维护和扩展:
|
||||
|
||||
### 1. 架构层次
|
||||
|
||||
- **表现层(API层)**:处理HTTP请求和响应,提供RESTful API接口
|
||||
- **业务逻辑层(Service层)**:实现核心业务逻辑,处理任务和工作流的创建、执行和管理
|
||||
- **代理层(Agents层)**:提供智能代理服务,支持任务自动化和优化(已设计,尚未实现)
|
||||
- **领域层(Core层)**:定义核心领域模型和业务规则,包含工作流引擎和组件系统
|
||||
- **数据层(Data层)**:提供数据模型定义和数据访问功能,支持任务、流程、组件等数据的持久化
|
||||
- **智能服务层**:提供向量化和向量存储服务,支持知识检索和智能决策(已设计,尚未实现)
|
||||
- **基础设施层**:提供数据持久化、日志记录、配置管理等基础服务
|
||||
|
||||
### 2. 设计模式
|
||||
|
||||
- **工厂模式**:用于组件的创建和注册
|
||||
- **策略模式**:用于实现不同类型组件的执行逻辑
|
||||
- **观察者模式**:用于任务状态变更通知
|
||||
- **命令模式**:用于任务执行和撤销操作
|
||||
- **组合模式**:用于构建组件树和工作流结构
|
||||
- **仓储模式**:用于数据访问和持久化
|
||||
|
||||
### 3. 系统架构图
|
||||
|
||||
```
|
||||
+-------------------+
|
||||
| 用户界面 |
|
||||
+--------+----------+
|
||||
|
|
||||
v
|
||||
+------------------+ +----------+---------+
|
||||
| | | |
|
||||
| 外部知识库 +------------->+ API层 |
|
||||
| | | |
|
||||
+------------------+ +----+------+--------+
|
||||
| |
|
||||
| |
|
||||
+----------------------------+ |
|
||||
| |
|
||||
v v
|
||||
+----------+---------+ +---------+----------+
|
||||
| | | |
|
||||
| Agent系统 |<------------>| 业务服务层 |
|
||||
| (包含LLM功能) | | |
|
||||
+----+---------------+ +--------+-----------+
|
||||
| |
|
||||
| |
|
||||
| v
|
||||
| +--------+-----------+
|
||||
| | |
|
||||
| | 工作流引擎 |
|
||||
| | |
|
||||
| +--------+-----------+
|
||||
| |
|
||||
| |
|
||||
| v
|
||||
| +--------+-----------+
|
||||
| | |
|
||||
+----------------------------->+ 组件系统 |
|
||||
| | |
|
||||
| +--------+-----------+
|
||||
| |
|
||||
v v
|
||||
+----+---------------+ +--------+-----------+
|
||||
| | | |
|
||||
| 智能服务层 | | 外部系统/设备 |
|
||||
| (向量化和向量存储) | | |
|
||||
+----+---------------+ +--------------------+
|
||||
|
|
||||
|
|
||||
v
|
||||
+----+---+
|
||||
| |
|
||||
|向量数据库|
|
||||
| |
|
||||
+--------+
|
||||
```
|
||||
|
||||
> **注意**:架构图中的Agent系统、外部知识库、智能服务层和向量数据库部分已完成设计,但尚未实现。这些组件将在后续版本中逐步开发和集成。
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
tianfeng_task/
|
||||
├── api/ # API接口定义
|
||||
│ ├── task_api.py # 任务相关API
|
||||
│ ├── workflow_api.py # 工作流相关API
|
||||
│ └── component_api.py # 组件相关API
|
||||
├── services/ # 业务服务层
|
||||
│ ├── task_service.py # 任务服务
|
||||
│ ├── workflow_service.py # 工作流服务
|
||||
│ ├── component_service.py # 组件服务
|
||||
│ └── editor_service.py # 编辑器服务
|
||||
├── agents/ # 智能代理层(已设计,尚未实现)
|
||||
│ ├── base_agent.py # 代理基类
|
||||
│ ├── task_agent.py # 任务代理
|
||||
│ ├── llm/ # 大语言模型集成
|
||||
│ ├── prompts/ # 提示词模板
|
||||
│ ├── tools/ # 代理工具集
|
||||
│ └── teams/ # 代理团队协作
|
||||
├── components/ # 组件实现
|
||||
│ ├── base_components.py # 基础组件
|
||||
│ ├── flow_components.py # 流程控制组件
|
||||
│ ├── http_components.py # HTTP请求组件
|
||||
│ ├── robot_components.py # 机器人调度组件
|
||||
│ ├── script_components.py # 脚本组件
|
||||
│ ├── site_components.py # 库位组件
|
||||
│ ├── subtask_components.py # 子任务组件
|
||||
│ ├── task_components.py # 任务组件
|
||||
│ └── device_components.py # 设备组件
|
||||
├── core/ # 核心功能模块
|
||||
│ ├── component.py # 组件基类定义
|
||||
│ ├── context.py # 执行上下文
|
||||
│ ├── exceptions.py # 异常定义
|
||||
│ └── workflow.py # 工作流引擎
|
||||
├── data/ # 数据存储
|
||||
│ ├── models/ # 数据模型定义
|
||||
│ │ ├── base.py # 基础模型
|
||||
│ │ ├── task.py # 任务相关模型
|
||||
│ │ ├── task_flow.py # 任务流程模型
|
||||
│ │ ├── task_variable.py # 任务变量模型
|
||||
│ │ ├── task_edit_history.py # 编辑历史模型
|
||||
│ │ ├── component.py # 组件模型
|
||||
│ │ ├── robot.py # 机器人模型
|
||||
│ │ ├── storage.py # 库位模型
|
||||
│ │ ├── device.py # 设备模型
|
||||
│ │ └── user_operation.py # 用户操作日志模型
|
||||
│ ├── session.py # 数据库会话管理
|
||||
│ └── repositories/ # 数据仓储实现
|
||||
├── intelligent/ # 智能服务层(已设计,尚未实现)
|
||||
│ ├── vectorization/ # 向量化服务
|
||||
│ ├── retrieval/ # 知识检索服务
|
||||
│ ├── embedding/ # 嵌入模型服务
|
||||
│ └── vector_store/ # 向量存储服务
|
||||
├── migrations/ # 数据库迁移
|
||||
│ ├── versions/ # 迁移版本
|
||||
│ ├── env.py # 迁移环境配置
|
||||
│ ├── script.py.mako # 迁移脚本模板
|
||||
│ └── alembic.ini # Alembic配置
|
||||
├── scripts/ # 脚本工具
|
||||
│ ├── generate_migration.py # 生成迁移脚本
|
||||
│ └── run_migration.py # 执行迁移脚本
|
||||
├── utils/ # 工具函数
|
||||
│ ├── logger.py # 日志工具
|
||||
│ ├── validators.py # 数据验证工具
|
||||
│ └── helpers.py # 辅助函数
|
||||
├── config/ # 配置文件
|
||||
│ ├── database.py # 数据库配置
|
||||
│ ├── component_registry.py # 组件注册配置
|
||||
│ └── settings.py # 应用设置
|
||||
├── logs/ # 日志文件
|
||||
├── tests/ # 测试代码
|
||||
│ ├── unit/ # 单元测试
|
||||
│ ├── integration/ # 集成测试
|
||||
│ └── fixtures/ # 测试数据
|
||||
├── app.py # 应用入口
|
||||
└── requirements.txt # 依赖包列表
|
||||
```
|
||||
|
||||
## 核心功能
|
||||
|
||||
### 1. 任务管理
|
||||
|
||||
- **任务创建**:支持创建普通任务和定时任务
|
||||
- **任务监控**:实时监控任务执行状态和进度
|
||||
- **任务操作**:支持启动、暂停、取消、重试等操作
|
||||
- **任务历史**:记录任务执行历史和结果
|
||||
- **版本控制**:支持任务版本管理,可以回滚到历史版本
|
||||
- **任务测试**:支持在测试环境中执行任务,验证任务配置
|
||||
|
||||
### 2. 任务编辑器
|
||||
|
||||
天风任务模块提供了强大的可视化编辑器,支持以下功能:
|
||||
|
||||
- **流程图设计**:通过拖拽方式设计任务流程图
|
||||
- **属性配置**:配置组件和连接的属性
|
||||
- **变量管理**:定义和管理任务执行过程中的变量
|
||||
- **撤销/重做**:支持编辑操作的撤销和重做
|
||||
- **源代码生成**:自动生成任务执行的源代码
|
||||
- **版本备份**:支持任务配置的版本备份和恢复
|
||||
|
||||
### 3. 组件库
|
||||
|
||||
天风任务模块提供了丰富的组件库,用户可以通过拖拽组件和配置参数的方式设计复杂的任务流程:
|
||||
|
||||
- **子任务组件**:支持任务的模块化和复用
|
||||
- **脚本组件**:支持编写自定义JavaScript脚本
|
||||
- **HTTP请求组件**:支持与外部系统进行通信
|
||||
- **任务组件**:提供任务数据管理和状态控制功能
|
||||
- **流程组件**:提供条件判断、循环、并行执行等流程控制功能
|
||||
- **基础组件**:提供数据验证、ID生成、时间处理等基础功能
|
||||
- **库位组件**:提供库位管理和操作功能
|
||||
- **机器人调度组件**:提供机器人选择、控制和状态监控功能
|
||||
- **设备组件**:提供与外部设备通信的功能
|
||||
|
||||
### 4. 智能代理系统(已设计,尚未实现)
|
||||
|
||||
- **任务代理**:自动分析和优化任务流程
|
||||
- **LLM集成**:集成大语言模型,提供智能决策支持
|
||||
- **代理工具**:提供丰富的工具集,支持代理执行各种操作
|
||||
- **团队协作**:支持多代理协作完成复杂任务
|
||||
- **知识检索**:从外部知识库中检索相关信息,辅助决策
|
||||
|
||||
### 5. 智能服务层(已设计,尚未实现)
|
||||
|
||||
- **向量化服务**:将文本、图像等数据转换为向量表示
|
||||
- **知识检索**:基于语义相似度进行知识检索
|
||||
- **嵌入模型**:提供多种嵌入模型,支持不同类型数据的向量化
|
||||
- **向量存储**:高效存储和检索向量数据
|
||||
|
||||
### 6. 工作流引擎
|
||||
|
||||
- **流程执行**:解析和执行任务流程图
|
||||
- **上下文管理**:管理任务执行过程中的变量和状态
|
||||
- **错误处理**:提供异常捕获和处理机制
|
||||
- **并行执行**:支持多分支并行执行
|
||||
- **动态加载**:支持动态加载和执行组件
|
||||
|
||||
### 7. 数据模型
|
||||
|
||||
系统提供了完善的数据模型,支持任务管理的各个方面:
|
||||
|
||||
- **任务模型**:存储任务基本信息和配置
|
||||
- **任务版本**:管理任务的不同版本
|
||||
- **任务记录**:记录任务的执行情况
|
||||
- **任务流程节点**:存储流程图中的节点信息
|
||||
- **任务流程连接**:存储流程图中的连接信息
|
||||
- **任务变量定义**:管理任务中使用的变量
|
||||
- **任务编辑历史**:记录编辑操作,支持撤销/重做
|
||||
- **任务备份**:存储任务的备份数据
|
||||
|
||||
## 执行业务流程
|
||||
|
||||
### 系统启动流程
|
||||
|
||||
1. 应用启动(app.py)
|
||||
2. 加载配置信息(config模块)
|
||||
3. 初始化日志系统(utils.logger)
|
||||
4. 初始化数据库连接(data.session)
|
||||
5. 注册所有组件(config.component_registry)
|
||||
6. 注册API路由(api模块)
|
||||
7. 启动HTTP服务(uvicorn)
|
||||
|
||||
### 任务创建流程
|
||||
|
||||
1. 用户在天风任务主页面点击"定义新任务"按钮
|
||||
2. 前端发送创建任务请求到API层(/api/tasks)
|
||||
3. API层调用任务服务(task_service.py)创建任务记录
|
||||
4. 系统创建任务并返回任务ID
|
||||
5. 前端跳转到任务编辑页面
|
||||
6. 用户通过拖拽组件设计任务流程
|
||||
7. 前端发送保存流程请求到API层(/api/workflows)
|
||||
8. API层调用工作流服务(workflow_service.py)保存流程配置
|
||||
9. 系统将流程配置保存到数据库,创建新版本
|
||||
|
||||
### 任务编辑流程
|
||||
|
||||
1. 用户打开任务编辑页面
|
||||
2. 系统加载任务的最新版本和流程图数据
|
||||
3. 用户通过拖拽组件和连接设计流程图
|
||||
4. 用户配置组件属性和变量
|
||||
5. 系统记录每一步编辑操作,支持撤销/重做
|
||||
6. 用户点击保存按钮,系统创建新版本
|
||||
7. 用户可以点击测试按钮,在测试环境中执行任务
|
||||
8. 用户可以点击生成源码按钮,系统生成任务执行代码
|
||||
|
||||
### 任务执行流程
|
||||
|
||||
1. 用户在任务列表中选择任务并点击"执行"按钮
|
||||
2. 前端发送执行任务请求到API层(/api/tasks/{id}/execute)
|
||||
3. API层调用任务服务(task_service.py)创建任务记录
|
||||
4. 任务服务加载任务配置和工作流定义
|
||||
5. 任务服务初始化执行上下文(context.py)
|
||||
6. 任务服务调用工作流引擎(workflow.py)执行任务
|
||||
7. 工作流引擎解析任务流程图并按顺序执行各组件
|
||||
8. 组件执行结果存储在上下文中,供后续组件使用
|
||||
9. 系统实时更新任务状态和进度
|
||||
10. 任务执行完成后,系统记录执行结果和日志
|
||||
|
||||
### 智能任务执行流程(已设计,尚未实现)
|
||||
|
||||
1. 用户在任务列表中选择任务并点击"智能执行"按钮
|
||||
2. 前端发送智能执行任务请求到API层
|
||||
3. API层调用Agent系统进行任务分析和优化
|
||||
4. Agent系统从外部知识库检索相关知识
|
||||
5. Agent系统基于LLM和检索到的知识生成执行计划
|
||||
6. Agent系统调用业务服务层执行优化后的任务
|
||||
7. 业务服务层调用工作流引擎执行任务
|
||||
8. 执行过程中,Agent系统持续监控任务状态
|
||||
9. 遇到异常情况时,Agent系统自动调整执行策略
|
||||
10. 任务执行完成后,Agent系统生成执行报告和优化建议
|
||||
|
||||
## 交互方式
|
||||
|
||||
天风任务模块提供多种交互方式,满足不同场景的需求:
|
||||
|
||||
### 1. 用户界面交互
|
||||
|
||||
- **任务列表界面**:
|
||||
- 展示所有任务及其状态
|
||||
- 提供任务筛选、搜索和排序功能
|
||||
- 支持任务的创建、编辑、删除、执行等操作
|
||||
- 显示任务执行状态和进度
|
||||
|
||||
- **任务编辑界面**:
|
||||
- 提供可视化的流程设计工具
|
||||
- 左侧组件面板:展示可用组件
|
||||
- 中央流程图编辑区:通过拖拽设计任务流程
|
||||
- 右侧属性面板:配置组件参数
|
||||
- 顶部工具栏:保存、运行、测试、撤销、重做等操作
|
||||
|
||||
- **任务监控界面**:
|
||||
- 实时显示任务执行状态和进度
|
||||
- 展示任务执行日志和变量值
|
||||
- 提供任务暂停、取消、重试等操作
|
||||
|
||||
### 2. API接口交互
|
||||
|
||||
系统提供RESTful API接口,支持第三方系统集成:
|
||||
|
||||
- **任务管理API**:
|
||||
- `GET /api/tasks`:获取任务列表
|
||||
- `POST /api/tasks`:创建新任务
|
||||
- `GET /api/tasks/{id}`:获取任务详情
|
||||
- `PUT /api/tasks/{id}`:更新任务
|
||||
- `DELETE /api/tasks/{id}`:删除任务
|
||||
- `POST /api/tasks/{id}/execute`:执行任务
|
||||
- `POST /api/tasks/{id}/cancel`:取消任务
|
||||
|
||||
- **工作流管理API**:
|
||||
- `GET /api/workflows/{id}`:获取工作流定义
|
||||
- `POST /api/workflows`:保存工作流定义
|
||||
- `GET /api/workflows/{id}/validate`:验证工作流定义
|
||||
|
||||
- **编辑器API**:
|
||||
- `POST /api/editor/save`:保存编辑内容
|
||||
- `POST /api/editor/undo`:撤销操作
|
||||
- `POST /api/editor/redo`:重做操作
|
||||
- `POST /api/editor/backup`:创建备份
|
||||
- `GET /api/editor/history`:获取编辑历史
|
||||
|
||||
- **智能服务API**(已设计,尚未实现):
|
||||
- `POST /api/intelligent/analyze`:分析任务流程
|
||||
- `POST /api/intelligent/optimize`:优化任务流程
|
||||
- `POST /api/intelligent/search`:知识检索
|
||||
- `POST /api/intelligent/vectorize`:向量化数据
|
||||
|
||||
## 技术栈
|
||||
|
||||
- **后端**:FastAPI (Python)
|
||||
- **数据库**:SQLAlchemy ORM,支持MySQL、PostgreSQL等
|
||||
- **前端**:React + Ant Design(低代码编辑器)
|
||||
- **API文档**:Swagger UI(自动生成)
|
||||
- **工作流引擎**:自研基于DAG的工作流引擎
|
||||
- **组件系统**:可扩展的组件注册和执行系统
|
||||
- **数据迁移**:Alembic
|
||||
- **智能代理**:基于大语言模型的智能代理系统(已设计,尚未实现)
|
||||
- **向量数据库**:支持高效的向量存储和检索(已设计,尚未实现)
|
||||
- **嵌入模型**:支持文本、图像等数据的向量化(已设计,尚未实现)
|
||||
|
||||
## 部署说明
|
||||
|
||||
### 环境要求
|
||||
|
||||
- Python 3.8+
|
||||
- 数据库(MySQL/PostgreSQL)
|
||||
- Node.js 14+(前端开发)
|
||||
|
||||
### 安装步骤
|
||||
|
||||
1. 克隆代码库
|
||||
2. 安装依赖:`pip install -r requirements.txt`
|
||||
3. 配置数据库连接:修改`config/database.py`
|
||||
4. 初始化数据库:`python -m scripts.run_migration`
|
||||
5. 启动应用:`python app.py`
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 创建简单任务
|
||||
|
||||
1. 进入天风任务主页面
|
||||
2. 点击"定义新任务"按钮
|
||||
3. 输入任务名称"测试任务",选择"普通任务"类型
|
||||
4. 在编辑页面拖入"选择执行机器人"组件
|
||||
5. 配置机器人选择参数
|
||||
6. 拖入"机器人通用动作"组件,配置目标站点
|
||||
7. 连接组件,形成执行流程
|
||||
8. 保存任务并执行
|
||||
|
||||
### 使用版本控制
|
||||
|
||||
1. 打开现有任务的编辑页面
|
||||
2. 修改任务流程或组件配置
|
||||
3. 点击"保存"按钮,系统自动创建新版本
|
||||
4. 在版本历史中可以查看所有历史版本
|
||||
5. 可以选择任意历史版本进行查看或恢复
|
||||
|
||||
### 使用撤销/重做功能
|
||||
|
||||
1. 在编辑页面进行操作(如添加节点、移动节点等)
|
||||
2. 如需撤销操作,点击工具栏中的"撤销"按钮
|
||||
3. 如需重做已撤销的操作,点击"重做"按钮
|
||||
4. 系统会记录编辑会话中的所有操作历史
|
||||
|
||||
## 常见问题
|
||||
|
||||
1. **任务执行失败怎么办?**
|
||||
- 查看任务执行日志,定位失败原因
|
||||
- 在测试环境中验证任务配置
|
||||
- 修改任务配置,解决问题后重试
|
||||
|
||||
2. **如何调试复杂任务?**
|
||||
- 使用测试功能验证任务配置
|
||||
- 查看执行日志和变量值
|
||||
- 使用条件断点和单步执行
|
||||
|
||||
3. **如何管理大量任务?**
|
||||
- 使用任务模板功能创建可复用的任务模板
|
||||
- 使用子任务功能将复杂任务拆分为多个子任务
|
||||
- 使用标签和分类功能组织任务
|
||||
|
||||
## 联系方式
|
||||
|
||||
如有问题或建议,请联系系统管理员或开发团队。
|
BIN
__pycache__/app.cpython-312.pyc
Normal file
BIN
__pycache__/app.cpython-312.pyc
Normal file
Binary file not shown.
0
agents/__init__.py
Normal file
0
agents/__init__.py
Normal file
3
agents/base_agent.py
Normal file
3
agents/base_agent.py
Normal file
@ -0,0 +1,3 @@
|
||||
"""
|
||||
Agent基类
|
||||
"""
|
0
agents/llm/__init__.py
Normal file
0
agents/llm/__init__.py
Normal file
3
agents/llm/base_llm.py
Normal file
3
agents/llm/base_llm.py
Normal file
@ -0,0 +1,3 @@
|
||||
"""
|
||||
LLM基类
|
||||
"""
|
3
agents/llm/openai_llm.py
Normal file
3
agents/llm/openai_llm.py
Normal file
@ -0,0 +1,3 @@
|
||||
"""
|
||||
OpenAI LLM实现
|
||||
"""
|
0
agents/prompts/__init__.py
Normal file
0
agents/prompts/__init__.py
Normal file
3
agents/prompts/task_analysis.py
Normal file
3
agents/prompts/task_analysis.py
Normal file
@ -0,0 +1,3 @@
|
||||
"""
|
||||
任务分析提示词
|
||||
"""
|
3
agents/task_agent.py
Normal file
3
agents/task_agent.py
Normal file
@ -0,0 +1,3 @@
|
||||
"""
|
||||
任务Agent
|
||||
"""
|
0
agents/teams/__init__.py
Normal file
0
agents/teams/__init__.py
Normal file
0
agents/tools/__init__.py
Normal file
0
agents/tools/__init__.py
Normal file
3
agents/tools/task_tools.py
Normal file
3
agents/tools/task_tools.py
Normal file
@ -0,0 +1,3 @@
|
||||
"""
|
||||
任务相关工具
|
||||
"""
|
0
api/__init__.py
Normal file
0
api/__init__.py
Normal file
BIN
api/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
api/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
api/__pycache__/component_api.cpython-312.pyc
Normal file
BIN
api/__pycache__/component_api.cpython-312.pyc
Normal file
Binary file not shown.
BIN
api/__pycache__/models.cpython-312.pyc
Normal file
BIN
api/__pycache__/models.cpython-312.pyc
Normal file
Binary file not shown.
BIN
api/__pycache__/task_api.cpython-312.pyc
Normal file
BIN
api/__pycache__/task_api.cpython-312.pyc
Normal file
Binary file not shown.
BIN
api/__pycache__/workflow_api.cpython-312.pyc
Normal file
BIN
api/__pycache__/workflow_api.cpython-312.pyc
Normal file
Binary file not shown.
133
api/component_api.py
Normal file
133
api/component_api.py
Normal file
@ -0,0 +1,133 @@
|
||||
# api/component_api.py
|
||||
from typing import Dict, Any, List, Optional
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from pydantic import BaseModel
|
||||
from services.component_service import ComponentService
|
||||
from config.component_config import (
|
||||
ComponentDiscovery,
|
||||
ComponentCategory
|
||||
)
|
||||
from config.component_detail_config import ComponentDetailConfig
|
||||
from config.api_config import (
|
||||
ApiResponseCode,
|
||||
ApiResponseMessage
|
||||
)
|
||||
from api.models import ApiResponse, ComponentDiscoverInput
|
||||
|
||||
# 创建路由器
|
||||
router = APIRouter(prefix="/component", tags=["组件管理"])
|
||||
|
||||
# 创建服务实例
|
||||
component_service = ComponentService()
|
||||
|
||||
@router.get("/components", response_model=ApiResponse)
|
||||
async def get_components():
|
||||
"""获取所有组件类型及其详细信息"""
|
||||
try:
|
||||
# 获取所有组件类型
|
||||
component_types = component_service.get_all_component_types()
|
||||
|
||||
# 获取组件类型中文名称映射
|
||||
component_type_names = ComponentDetailConfig.get_component_type_names()
|
||||
|
||||
# 构建组件类型信息,包含英文标识和中文名称
|
||||
component_type_info = []
|
||||
for component_type in component_types:
|
||||
# 获取组件类型的中文名称,如果没有则使用英文标识
|
||||
type_name = component_type_names.get(component_type, component_type)
|
||||
component_type_info.append({
|
||||
"type": component_type, # 英文标识
|
||||
"name": type_name, # 中文名称
|
||||
})
|
||||
|
||||
# 按类别分组
|
||||
component_categories = {}
|
||||
for component_type in component_types:
|
||||
# 根据组件类型推断类别
|
||||
category = ComponentCategory.BASE # 默认类别
|
||||
|
||||
# 使用配置文件中的映射关系确定组件类别
|
||||
mapping = ComponentCategory.get_mapping()
|
||||
for prefix, cat in mapping.items():
|
||||
if component_type.startswith(prefix):
|
||||
category = cat
|
||||
break
|
||||
|
||||
if category not in component_categories:
|
||||
component_categories[category] = []
|
||||
|
||||
component_categories[category].append(component_type)
|
||||
|
||||
# 获取所有组件的详细配置
|
||||
component_details = ComponentDetailConfig.get_all_components()
|
||||
|
||||
# 按组件类型分组
|
||||
component_details_by_type = {}
|
||||
for component in component_details:
|
||||
component_type = component["type"]
|
||||
if component_type not in component_details_by_type:
|
||||
component_details_by_type[component_type] = []
|
||||
component_details_by_type[component_type].append(component)
|
||||
|
||||
return {
|
||||
"code": ApiResponseCode.SUCCESS,
|
||||
"message": ApiResponseMessage.Component.FETCHED,
|
||||
"data": {
|
||||
"component_types": component_types,
|
||||
"component_type_info": component_type_info, # 新增:包含中文名称的组件类型信息
|
||||
"component_categories": component_categories,
|
||||
"component_details": component_details,
|
||||
"component_details_by_type": component_details_by_type
|
||||
}
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=ApiResponseCode.SERVER_ERROR,
|
||||
detail=f"获取组件类型失败: {str(e)}"
|
||||
)
|
||||
|
||||
@router.get("/components/{component_type}", response_model=ApiResponse)
|
||||
async def get_components_by_type(component_type: str):
|
||||
"""获取指定类型的组件详细信息"""
|
||||
try:
|
||||
# 获取指定类型的组件详细配置
|
||||
components = ComponentDetailConfig.get_components_by_type(component_type)
|
||||
|
||||
if not components:
|
||||
return {
|
||||
"code": ApiResponseCode.NOT_FOUND,
|
||||
"message": f"未找到类型为 {component_type} 的组件",
|
||||
"data": {"components": []}
|
||||
}
|
||||
|
||||
return {
|
||||
"code": ApiResponseCode.SUCCESS,
|
||||
"message": ApiResponseMessage.Component.FETCHED,
|
||||
"data": {"components": components}
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=ApiResponseCode.SERVER_ERROR,
|
||||
detail=f"获取组件详细信息失败: {str(e)}"
|
||||
)
|
||||
|
||||
@router.post("/components/discover", response_model=ApiResponse)
|
||||
async def discover_components(discover_input: ComponentDiscoverInput):
|
||||
"""自动发现并注册组件"""
|
||||
try:
|
||||
# 自动发现并注册组件
|
||||
component_service.auto_discover_components(discover_input.package_name)
|
||||
|
||||
# 获取所有组件类型
|
||||
component_types = component_service.get_all_component_types()
|
||||
|
||||
return {
|
||||
"code": ApiResponseCode.SUCCESS,
|
||||
"message": f"{ApiResponseMessage.Component.DISCOVERED},共发现 {len(component_types)} 个组件",
|
||||
"data": {"component_types": component_types}
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=ApiResponseCode.SERVER_ERROR,
|
||||
detail=f"自动发现并注册组件失败: {str(e)}"
|
||||
)
|
93
api/models.py
Normal file
93
api/models.py
Normal file
@ -0,0 +1,93 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
API模型模块
|
||||
包含API请求和响应的数据模型
|
||||
"""
|
||||
|
||||
from typing import Dict, Any, List, Optional, Literal
|
||||
from pydantic import BaseModel, Field, validator
|
||||
from config.component_config import ComponentDiscovery
|
||||
from config.task_config import TASK_TYPE_CONFIG, TaskType, TaskStatus
|
||||
from enum import Enum
|
||||
|
||||
# 获取所有任务类型的key值
|
||||
TASK_TYPE_KEYS = list(TASK_TYPE_CONFIG.keys())
|
||||
|
||||
# 排序字段枚举
|
||||
class SortField(str, Enum):
|
||||
CREATED_AT = "created_at"
|
||||
UPDATED_AT = "updated_at"
|
||||
NAME = "name"
|
||||
|
||||
# 排序方式枚举
|
||||
class SortOrder(str, Enum):
|
||||
ASC = "asc"
|
||||
DESC = "desc"
|
||||
|
||||
# 通用响应模型
|
||||
class ApiResponse(BaseModel):
|
||||
code: int = Field(..., description="状态码")
|
||||
message: str = Field(..., description="消息")
|
||||
data: Optional[dict] = Field(None, description="数据")
|
||||
|
||||
# 任务相关模型
|
||||
class TaskInput(BaseModel):
|
||||
name: str = Field(..., description="任务名称")
|
||||
task_type: TaskType = Field(
|
||||
...,
|
||||
description="任务类型,可选值为:" + ", ".join([t.value for t in TaskType]),
|
||||
example=TaskType.NORMAL
|
||||
)
|
||||
|
||||
class TaskBatchInput(BaseModel):
|
||||
tasks: List[TaskInput]
|
||||
|
||||
class TaskIdList(BaseModel):
|
||||
task_ids: List[str]
|
||||
|
||||
class TaskTypeInfo(BaseModel):
|
||||
key: str
|
||||
name: str
|
||||
description: str
|
||||
|
||||
# 工作流相关模型
|
||||
class WorkflowInput(BaseModel):
|
||||
name: str
|
||||
workflow_type: Optional[str] = "normal"
|
||||
description: Optional[str] = ""
|
||||
blocks: Optional[List[Dict[str, Any]]] = []
|
||||
variables: Optional[Dict[str, Any]] = {}
|
||||
schedule: Optional[Dict[str, Any]] = None
|
||||
|
||||
class WorkflowUpdateInput(BaseModel):
|
||||
name: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
blocks: Optional[List[Dict[str, Any]]] = None
|
||||
variables: Optional[Dict[str, Any]] = None
|
||||
schedule: Optional[Dict[str, Any]] = None
|
||||
|
||||
class WorkflowExecuteInput(BaseModel):
|
||||
task_inputs: Optional[Dict[str, Any]] = None
|
||||
|
||||
class WorkflowImportInput(BaseModel):
|
||||
workflow_json: str
|
||||
|
||||
# 组件相关模型
|
||||
class ComponentDiscoverInput(BaseModel):
|
||||
package_name: Optional[str] = ComponentDiscovery.DEFAULT_PACKAGE
|
||||
|
||||
# 任务编辑相关模型
|
||||
class TaskUpdateInput(BaseModel):
|
||||
name: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
task_type: Optional[TaskType] = None
|
||||
blocks: Optional[List[Dict[str, Any]]] = None
|
||||
variables: Optional[Dict[str, Any]] = None
|
||||
schedule: Optional[Dict[str, Any]] = None
|
||||
|
||||
class TaskEditInput(BaseModel):
|
||||
task_id: str
|
||||
blocks: List[Dict[str, Any]]
|
||||
variables: Optional[Dict[str, Any]] = {}
|
431
api/task_api.py
Normal file
431
api/task_api.py
Normal file
@ -0,0 +1,431 @@
|
||||
# api/task_api.py
|
||||
from typing import Dict, Any, List, Optional
|
||||
from fastapi import APIRouter, HTTPException, Depends, Query
|
||||
from pydantic import BaseModel, Field
|
||||
from services.task_service import TaskService
|
||||
from core.exceptions import TianfengTaskError
|
||||
from config.task_config import (
|
||||
get_all_task_types,
|
||||
get_task_type_name,
|
||||
get_task_type_description,
|
||||
TaskTypeConfig,
|
||||
TaskStatusConfig,
|
||||
DefaultConfig,
|
||||
TASK_TYPE_CONFIG,
|
||||
TASK_TYPE_NORMAL,
|
||||
TASK_TYPE_SCHEDULED,
|
||||
DEFAULT_TASK_DESCRIPTION,
|
||||
DEFAULT_TEMPLATE_DESCRIPTION,
|
||||
TaskType,
|
||||
TaskStatus
|
||||
)
|
||||
from api.models import ApiResponse, TaskInput, TaskBatchInput, TaskIdList, TaskTypeInfo, SortField, SortOrder, TaskUpdateInput, TaskEditInput
|
||||
|
||||
# 创建路由器
|
||||
router = APIRouter(tags=["任务管理"])
|
||||
|
||||
# 创建服务实例
|
||||
task_service = TaskService()
|
||||
|
||||
@router.get("/tasks", response_model=ApiResponse)
|
||||
async def get_tasks(
|
||||
status: Optional[TaskStatus] = Query(None, description="任务状态"),
|
||||
task_type: Optional[TaskType] = Query(None, description="任务类型"),
|
||||
name: Optional[str] = Query(None, description="任务名称(模糊查询)"),
|
||||
is_scheduled: Optional[bool] = Query(None, description="是否为定时任务"),
|
||||
created_start: Optional[int] = Query(None, description="创建时间起始(毫秒时间戳)"),
|
||||
created_end: Optional[int] = Query(None, description="创建时间结束(毫秒时间戳)"),
|
||||
sort_by: SortField = Query(default=SortField.CREATED_AT, description="排序字段"),
|
||||
sort_order: SortOrder = Query(default=SortOrder.DESC, description="排序方式"),
|
||||
page: int = Query(1, ge=1, description="页码"),
|
||||
page_size: int = Query(10, ge=1, le=100, description="每页数量")
|
||||
):
|
||||
"""
|
||||
获取任务列表
|
||||
|
||||
支持多种筛选条件、排序和分页
|
||||
"""
|
||||
try:
|
||||
# 获取任务列表
|
||||
tasks, total = task_service.get_all_tasks(
|
||||
status=status,
|
||||
task_type=task_type,
|
||||
name=name,
|
||||
is_scheduled=is_scheduled,
|
||||
created_start=created_start,
|
||||
created_end=created_end,
|
||||
sort_by=sort_by,
|
||||
sort_order=sort_order,
|
||||
page=page,
|
||||
page_size=page_size
|
||||
)
|
||||
|
||||
# 构建分页信息
|
||||
pagination = {
|
||||
"page": page,
|
||||
"page_size": page_size,
|
||||
"total": total,
|
||||
"total_pages": (total + page_size - 1) // page_size
|
||||
}
|
||||
|
||||
return {
|
||||
"code": 200,
|
||||
"message": "获取任务列表成功",
|
||||
"data": {
|
||||
"tasks": tasks,
|
||||
"pagination": pagination
|
||||
}
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"获取任务列表失败: {str(e)}")
|
||||
|
||||
|
||||
@router.get("/task/types", response_model=ApiResponse)
|
||||
async def get_task_types():
|
||||
"""获取任务类型列表"""
|
||||
try:
|
||||
# 从配置文件中获取任务类型列表
|
||||
task_types = get_all_task_types()
|
||||
# 添加value字段(枚举值)
|
||||
for task_type in task_types:
|
||||
# 从TaskType枚举中获取value
|
||||
try:
|
||||
# 尝试通过名称获取枚举值
|
||||
task_type["value"] = TaskType[task_type["key"]].value
|
||||
except (KeyError, AttributeError):
|
||||
# 如果枚举中没有对应的值,则使用key的小写作为value
|
||||
task_type["value"] = task_type["key"]
|
||||
|
||||
return {
|
||||
"code": 200,
|
||||
"message": "获取任务类型列表成功",
|
||||
"data": task_types
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"获取任务类型列表失败: {str(e)}")
|
||||
|
||||
@router.post("/task/create", response_model=ApiResponse)
|
||||
async def create_task(task_input: TaskInput):
|
||||
"""创建任务"""
|
||||
try:
|
||||
# 获取任务类型值(处理枚举)
|
||||
task_type = task_input.task_type
|
||||
if hasattr(task_type, "value"):
|
||||
task_type_value = task_type.value
|
||||
else:
|
||||
task_type_value = task_type
|
||||
|
||||
# 从配置文件中获取任务类型信息
|
||||
task_type_info = TaskTypeConfig.DETAILS.get(task_type_value, {})
|
||||
|
||||
# 根据任务类型决定是否为定时任务
|
||||
is_scheduled = task_type_value == TASK_TYPE_SCHEDULED
|
||||
|
||||
# 创建任务,设置固定参数
|
||||
task = task_service.create_task(
|
||||
name=task_input.name,
|
||||
task_type=task_type_value, # 使用key作为task_type
|
||||
description=DEFAULT_TASK_DESCRIPTION, # 使用配置文件中的默认备注
|
||||
template_desc=DEFAULT_TEMPLATE_DESCRIPTION, # 使用配置文件中的默认模板描述
|
||||
is_scheduled=is_scheduled # 根据任务类型决定
|
||||
)
|
||||
|
||||
# 在返回结果中添加任务类型的中文名称
|
||||
if "task_type_name" not in task and task_type_info:
|
||||
task["task_type_name"] = task_type_info.get("name", task_type_value)
|
||||
|
||||
return {
|
||||
"code": 200,
|
||||
"message": "创建任务成功",
|
||||
"data": task
|
||||
}
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"创建任务失败: {str(e)}")
|
||||
|
||||
|
||||
@router.get("/task/{task_id}", response_model=ApiResponse)
|
||||
async def get_task(task_id: str):
|
||||
"""获取任务详情"""
|
||||
try:
|
||||
# 获取任务
|
||||
task = task_service.get_task_by_id(task_id)
|
||||
|
||||
if not task:
|
||||
return {
|
||||
"code": 404,
|
||||
"message": f"任务不存在: {task_id}",
|
||||
"data": None
|
||||
}
|
||||
|
||||
return {
|
||||
"code": 200,
|
||||
"message": "获取任务详情成功",
|
||||
"data": task
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"获取任务详情失败: {str(e)}")
|
||||
|
||||
|
||||
@router.delete("/task/{task_id}", response_model=ApiResponse)
|
||||
async def delete_task(task_id: str):
|
||||
"""删除任务"""
|
||||
try:
|
||||
# 删除任务
|
||||
success = task_service.delete_task(task_id)
|
||||
|
||||
# 如果任务不存在,返回成功但提示任务不存在
|
||||
if not success:
|
||||
return {
|
||||
"code": 200,
|
||||
"message": f"任务不存在或已被删除: {task_id}",
|
||||
"data": None
|
||||
}
|
||||
|
||||
return {
|
||||
"code": 200,
|
||||
"message": "删除任务成功",
|
||||
"data": None
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"删除任务失败: {str(e)}")
|
||||
|
||||
|
||||
@router.post("/task/{task_id}/execute", response_model=ApiResponse)
|
||||
async def execute_task(task_id: str):
|
||||
"""执行任务"""
|
||||
try:
|
||||
# 执行任务
|
||||
task = task_service.execute_task(task_id)
|
||||
|
||||
return {
|
||||
"code": 200,
|
||||
"message": "执行任务成功",
|
||||
"data": task
|
||||
}
|
||||
except ValueError as e:
|
||||
# 任务不存在
|
||||
return {
|
||||
"code": 404,
|
||||
"message": str(e),
|
||||
"data": None
|
||||
}
|
||||
except TianfengTaskError as e:
|
||||
# 业务逻辑错误
|
||||
return {
|
||||
"code": 400,
|
||||
"message": str(e),
|
||||
"data": None
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"执行任务失败: {str(e)}")
|
||||
|
||||
@router.post("/task/{task_id}/cancel", response_model=ApiResponse)
|
||||
async def cancel_task(task_id: str):
|
||||
"""取消任务"""
|
||||
try:
|
||||
# 取消任务
|
||||
task = task_service.cancel_task(task_id)
|
||||
|
||||
return {
|
||||
"code": 200,
|
||||
"message": "取消任务成功",
|
||||
"data": task
|
||||
}
|
||||
except ValueError as e:
|
||||
# 任务不存在或状态不允许取消
|
||||
return {
|
||||
"code": 400,
|
||||
"message": str(e),
|
||||
"data": None
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"取消任务失败: {str(e)}")
|
||||
|
||||
|
||||
|
||||
@router.delete("/task/batch", response_model=ApiResponse)
|
||||
async def batch_delete_tasks(id_list: TaskIdList):
|
||||
"""批量删除任务"""
|
||||
try:
|
||||
# 批量删除任务
|
||||
deleted_count = 0
|
||||
not_found_ids = []
|
||||
|
||||
for task_id in id_list.task_ids:
|
||||
if task_service.delete_task(task_id):
|
||||
deleted_count += 1
|
||||
else:
|
||||
not_found_ids.append(task_id)
|
||||
|
||||
# 构建消息
|
||||
message = f"批量删除任务成功,共删除 {deleted_count} 个任务"
|
||||
if not_found_ids:
|
||||
message += f",有 {len(not_found_ids)} 个任务不存在或已被删除"
|
||||
|
||||
return {
|
||||
"code": 200,
|
||||
"message": message,
|
||||
"data": {
|
||||
"deleted_count": deleted_count,
|
||||
"total_count": len(id_list.task_ids),
|
||||
"not_found_ids": not_found_ids
|
||||
}
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"批量删除任务失败: {str(e)}")
|
||||
|
||||
|
||||
@router.put("/task/{task_id}", response_model=ApiResponse)
|
||||
async def update_task(task_id: str, task_update: TaskUpdateInput):
|
||||
"""更新任务基本信息"""
|
||||
try:
|
||||
# 检查任务是否存在
|
||||
task = task_service.get_task_by_id(task_id)
|
||||
if not task:
|
||||
return {
|
||||
"code": 404,
|
||||
"message": f"任务 {task_id} 不存在",
|
||||
"data": None
|
||||
}
|
||||
|
||||
# 更新任务信息
|
||||
updated_task = task_service.update_task(
|
||||
task_id=task_id,
|
||||
name=task_update.name,
|
||||
description=task_update.description,
|
||||
task_type=task_update.task_type,
|
||||
blocks=task_update.blocks,
|
||||
variables=task_update.variables,
|
||||
schedule=task_update.schedule
|
||||
)
|
||||
|
||||
return {
|
||||
"code": 200,
|
||||
"message": "更新任务成功",
|
||||
"data": {"task": updated_task}
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"更新任务失败: {str(e)}")
|
||||
|
||||
@router.post("/task/edit", response_model=ApiResponse)
|
||||
async def edit_task(task_edit: TaskEditInput):
|
||||
"""编辑任务流程和变量"""
|
||||
try:
|
||||
# 检查任务是否存在
|
||||
task = task_service.get_task_by_id(task_edit.task_id)
|
||||
if not task:
|
||||
return {
|
||||
"code": 404,
|
||||
"message": f"任务 {task_edit.task_id} 不存在",
|
||||
"data": None
|
||||
}
|
||||
|
||||
# 更新任务流程和变量
|
||||
updated_task = task_service.update_task_workflow(
|
||||
task_id=task_edit.task_id,
|
||||
blocks=task_edit.blocks,
|
||||
variables=task_edit.variables
|
||||
)
|
||||
|
||||
return {
|
||||
"code": 200,
|
||||
"message": "编辑任务成功",
|
||||
"data": {"task": updated_task}
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"编辑任务失败: {str(e)}")
|
||||
|
||||
@router.get("/task/{task_id}/edit", response_model=ApiResponse)
|
||||
async def get_task_edit_info(task_id: str):
|
||||
"""获取任务编辑信息"""
|
||||
try:
|
||||
# 获取任务信息
|
||||
task = task_service.get_task_by_id(task_id)
|
||||
if not task:
|
||||
return {
|
||||
"code": 404,
|
||||
"message": f"任务 {task_id} 不存在",
|
||||
"data": None
|
||||
}
|
||||
|
||||
# 获取可用的子任务列表(排除当前任务自身)
|
||||
available_subtasks = []
|
||||
try:
|
||||
all_tasks, _ = task_service.get_all_tasks(page=1, page_size=1000) # 获取所有任务
|
||||
for t in all_tasks:
|
||||
if t["task_id"] != task_id: # 排除当前任务
|
||||
available_subtasks.append({
|
||||
"task_id": t["task_id"],
|
||||
"name": t["name"]
|
||||
})
|
||||
except Exception as e:
|
||||
# 如果获取任务列表失败,记录错误但继续执行
|
||||
print(f"获取可用子任务列表失败: {str(e)}")
|
||||
# 获取组件详细信息
|
||||
from config.component_detail_config import ComponentDetailConfig
|
||||
component_details = ComponentDetailConfig.get_all_components()
|
||||
# 获取组件类型中文名称映射
|
||||
component_type_names = ComponentDetailConfig.get_component_type_names()
|
||||
|
||||
# 按组件类型分组并添加中文名称
|
||||
component_types = {}
|
||||
for component in component_details:
|
||||
component_type = component["type"]
|
||||
|
||||
# 如果该类型还未添加到结果中,先创建类型信息
|
||||
if component_type not in component_types:
|
||||
# 获取组件类型的中文名称,如果没有则使用英文标识
|
||||
type_name = component_type_names.get(component_type, component_type)
|
||||
component_types[component_type] = {
|
||||
"type": component_type, # 英文标识
|
||||
"name": type_name, # 中文名称
|
||||
"components": [] # 该类型下的组件列表
|
||||
}
|
||||
|
||||
# 添加组件到对应类型下
|
||||
component_types[component_type]["components"].append(component)
|
||||
|
||||
# 不再将子任务列表嵌入到组件类型对象中
|
||||
if "subtask" in component_types:
|
||||
component_types["subtask"]["available_subtasks"] = available_subtasks
|
||||
|
||||
return {
|
||||
"code": 200,
|
||||
"message": "获取任务编辑信息成功",
|
||||
"data": {
|
||||
"task": task,
|
||||
"component_types": component_types,
|
||||
"available_subtasks": available_subtasks # 作为单独的字段返回
|
||||
}
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"获取任务编辑信息失败: {str(e)}")
|
||||
|
||||
@router.get("/tasks/available-subtasks", response_model=ApiResponse)
|
||||
async def get_available_subtasks(current_task_id: Optional[str] = None):
|
||||
"""获取可用的子任务列表"""
|
||||
try:
|
||||
# 获取所有任务
|
||||
all_tasks, _ = task_service.get_all_tasks(page=1, page_size=1000)
|
||||
|
||||
# 过滤出可用的子任务(如果提供了当前任务ID,则排除当前任务)
|
||||
available_subtasks = []
|
||||
for task in all_tasks:
|
||||
if not current_task_id or task["task_id"] != current_task_id:
|
||||
available_subtasks.append({
|
||||
"task_id": task["task_id"],
|
||||
"name": task["name"]
|
||||
})
|
||||
|
||||
return {
|
||||
"code": 200,
|
||||
"message": "获取可用子任务列表成功",
|
||||
"data": {
|
||||
"subtasks": available_subtasks
|
||||
}
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"获取可用子任务列表失败: {str(e)}")
|
||||
|
184
api/workflow_api.py
Normal file
184
api/workflow_api.py
Normal file
@ -0,0 +1,184 @@
|
||||
# api/workflow_api.py
|
||||
from typing import Dict, Any, List, Optional
|
||||
from fastapi import APIRouter, HTTPException, Depends, Query
|
||||
from pydantic import BaseModel, Field
|
||||
from services.workflow_service import WorkflowService
|
||||
from core.exceptions import TianfengTaskError
|
||||
from core.workflow import WorkflowDefinition
|
||||
from config.api_config import ApiResponseCode, ApiResponseMessage
|
||||
from api.models import ApiResponse, WorkflowInput, WorkflowUpdateInput, WorkflowExecuteInput, WorkflowImportInput
|
||||
|
||||
# 创建路由器
|
||||
router = APIRouter(prefix="/workflow", tags=["工作流管理"])
|
||||
|
||||
# 创建服务实例
|
||||
workflow_service = WorkflowService()
|
||||
|
||||
@router.get("/workflows", response_model=ApiResponse)
|
||||
async def get_workflows(
|
||||
type: Optional[str] = Query(None, description="工作流类型")
|
||||
):
|
||||
"""获取工作流列表"""
|
||||
try:
|
||||
# 获取工作流列表
|
||||
workflows = workflow_service.get_all_workflows(type)
|
||||
|
||||
# 转换为字典列表
|
||||
workflow_dicts = [wf.to_dict() for wf in workflows]
|
||||
|
||||
return {
|
||||
"code": ApiResponseCode.SUCCESS,
|
||||
"message": "获取工作流列表成功",
|
||||
"data": workflow_dicts
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=ApiResponseCode.SERVER_ERROR, detail=f"获取工作流列表失败: {str(e)}")
|
||||
|
||||
@router.get("/workflows/{workflow_id}", response_model=ApiResponse)
|
||||
async def get_workflow(workflow_id: str):
|
||||
"""获取工作流详情"""
|
||||
try:
|
||||
# 获取工作流
|
||||
workflow = workflow_service.get_workflow_by_id(workflow_id)
|
||||
|
||||
if not workflow:
|
||||
raise HTTPException(status_code=ApiResponseCode.NOT_FOUND, detail=f"找不到工作流: {workflow_id}")
|
||||
|
||||
return {
|
||||
"code": ApiResponseCode.SUCCESS,
|
||||
"message": "获取工作流详情成功",
|
||||
"data": workflow.to_dict()
|
||||
}
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=ApiResponseCode.SERVER_ERROR, detail=f"获取工作流详情失败: {str(e)}")
|
||||
|
||||
@router.post("/workflows", response_model=ApiResponse)
|
||||
async def create_workflow(workflow_input: WorkflowInput):
|
||||
"""创建工作流"""
|
||||
try:
|
||||
# 创建工作流
|
||||
workflow = workflow_service.create_workflow(
|
||||
name=workflow_input.name,
|
||||
workflow_type=workflow_input.workflow_type,
|
||||
description=workflow_input.description,
|
||||
blocks=workflow_input.blocks,
|
||||
variables=workflow_input.variables,
|
||||
schedule=workflow_input.schedule
|
||||
)
|
||||
|
||||
return {
|
||||
"code": ApiResponseCode.SUCCESS,
|
||||
"message": ApiResponseMessage.Workflow.CREATED,
|
||||
"data": workflow.to_dict()
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=ApiResponseCode.SERVER_ERROR, detail=f"创建工作流失败: {str(e)}")
|
||||
|
||||
@router.put("/workflows/{workflow_id}", response_model=ApiResponse)
|
||||
async def update_workflow(workflow_id: str, workflow_input: WorkflowUpdateInput):
|
||||
"""更新工作流"""
|
||||
try:
|
||||
# 更新工作流
|
||||
workflow = workflow_service.update_workflow(
|
||||
workflow_id=workflow_id,
|
||||
name=workflow_input.name,
|
||||
description=workflow_input.description,
|
||||
blocks=workflow_input.blocks,
|
||||
variables=workflow_input.variables,
|
||||
schedule=workflow_input.schedule
|
||||
)
|
||||
|
||||
if not workflow:
|
||||
raise HTTPException(status_code=ApiResponseCode.NOT_FOUND, detail=f"找不到工作流: {workflow_id}")
|
||||
|
||||
return {
|
||||
"code": ApiResponseCode.SUCCESS,
|
||||
"message": ApiResponseMessage.Workflow.UPDATED,
|
||||
"data": workflow.to_dict()
|
||||
}
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=ApiResponseCode.SERVER_ERROR, detail=f"更新工作流失败: {str(e)}")
|
||||
|
||||
@router.delete("/workflows/{workflow_id}", response_model=ApiResponse)
|
||||
async def delete_workflow(workflow_id: str):
|
||||
"""删除工作流"""
|
||||
try:
|
||||
# 删除工作流
|
||||
success = workflow_service.delete_workflow(workflow_id)
|
||||
|
||||
if not success:
|
||||
raise HTTPException(status_code=ApiResponseCode.NOT_FOUND, detail=f"找不到工作流: {workflow_id}")
|
||||
|
||||
return {
|
||||
"code": ApiResponseCode.SUCCESS,
|
||||
"message": ApiResponseMessage.Workflow.DELETED,
|
||||
"data": None
|
||||
}
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=ApiResponseCode.SERVER_ERROR, detail=f"删除工作流失败: {str(e)}")
|
||||
|
||||
@router.post("/workflows/{workflow_id}/execute", response_model=ApiResponse)
|
||||
async def execute_workflow(workflow_id: str, execute_input: WorkflowExecuteInput):
|
||||
"""执行工作流"""
|
||||
try:
|
||||
# 获取工作流
|
||||
workflow = workflow_service.get_workflow_by_id(workflow_id)
|
||||
|
||||
if not workflow:
|
||||
raise HTTPException(status_code=ApiResponseCode.NOT_FOUND, detail=f"找不到工作流: {workflow_id}")
|
||||
|
||||
# 执行工作流
|
||||
result = workflow_service.execute_workflow(
|
||||
workflow=workflow,
|
||||
task_inputs=execute_input.task_inputs
|
||||
)
|
||||
|
||||
return {
|
||||
"code": ApiResponseCode.SUCCESS,
|
||||
"message": ApiResponseMessage.Workflow.EXECUTED,
|
||||
"data": result
|
||||
}
|
||||
except HTTPException:
|
||||
raise
|
||||
except TianfengTaskError as e:
|
||||
raise HTTPException(status_code=ApiResponseCode.BAD_REQUEST, detail=str(e))
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=ApiResponseCode.SERVER_ERROR, detail=f"执行工作流失败: {str(e)}")
|
||||
|
||||
@router.post("/workflows/import", response_model=ApiResponse)
|
||||
async def import_workflow(import_input: WorkflowImportInput):
|
||||
"""导入工作流"""
|
||||
try:
|
||||
# 导入工作流
|
||||
workflow = workflow_service.import_workflow(import_input.workflow_json)
|
||||
|
||||
return {
|
||||
"code": ApiResponseCode.SUCCESS,
|
||||
"message": ApiResponseMessage.Workflow.IMPORTED,
|
||||
"data": workflow.to_dict()
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=ApiResponseCode.SERVER_ERROR, detail=f"导入工作流失败: {str(e)}")
|
||||
|
||||
@router.get("/workflows/{workflow_id}/export", response_model=ApiResponse)
|
||||
async def export_workflow(workflow_id: str):
|
||||
"""导出工作流"""
|
||||
try:
|
||||
# 导出工作流
|
||||
workflow_json = workflow_service.export_workflow(workflow_id)
|
||||
|
||||
return {
|
||||
"code": ApiResponseCode.SUCCESS,
|
||||
"message": ApiResponseMessage.Workflow.EXPORTED,
|
||||
"data": {"workflow_json": workflow_json}
|
||||
}
|
||||
except TianfengTaskError as e:
|
||||
raise HTTPException(status_code=ApiResponseCode.BAD_REQUEST, detail=str(e))
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=ApiResponseCode.SERVER_ERROR, detail=f"导出工作流失败: {str(e)}")
|
186
app.py
Normal file
186
app.py
Normal file
@ -0,0 +1,186 @@
|
||||
# app.py
|
||||
from fastapi import FastAPI, Request, HTTPException
|
||||
from fastapi.responses import JSONResponse
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
import logging
|
||||
import time
|
||||
from utils.logger import setup_logger
|
||||
from config.component_registry import register_all_components
|
||||
from config.settings import (
|
||||
AppConfig, ServerConfig, ApiConfig, CorsConfig
|
||||
)
|
||||
from api.task_api import router as task_router
|
||||
from api.workflow_api import router as workflow_router
|
||||
from api.component_api import router as component_router
|
||||
from core.exceptions import TianfengTaskError
|
||||
from config.api_config import ApiResponseCode, ApiResponseMessage
|
||||
from config.component_config import ComponentCategoryConfig
|
||||
# 导入数据库相关模块
|
||||
from config.database import DBConfig, CacheConfig, db_session
|
||||
import data.models # 导入所有模型以确保它们被注册
|
||||
from data.models.component import ComponentCategory, ComponentType, Component, ComponentCategoryEnum
|
||||
# 导入Lifespan
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
# 设置日志
|
||||
logger = setup_logger()
|
||||
|
||||
# 定义Lifespan上下文管理器
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
"""
|
||||
应用生命周期管理
|
||||
在应用启动时执行初始化操作,在应用关闭时执行清理操作
|
||||
"""
|
||||
# 应用启动时执行
|
||||
logger.info("应用启动")
|
||||
|
||||
yield # 应用运行期间
|
||||
|
||||
# 应用关闭时执行
|
||||
logger.info("应用关闭")
|
||||
DBConfig.shutdown_session()
|
||||
|
||||
# 创建FastAPI应用,使用Lifespan
|
||||
app = FastAPI(
|
||||
title=ApiConfig.TITLE,
|
||||
description=ApiConfig.DESCRIPTION,
|
||||
version=ApiConfig.VERSION,
|
||||
lifespan=lifespan
|
||||
)
|
||||
|
||||
# 添加CORS中间件
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=CorsConfig.ALLOW_ORIGINS,
|
||||
allow_credentials=CorsConfig.ALLOW_CREDENTIALS,
|
||||
allow_methods=CorsConfig.ALLOW_METHODS,
|
||||
allow_headers=CorsConfig.ALLOW_HEADERS,
|
||||
)
|
||||
|
||||
# 初始化数据库
|
||||
def init_database():
|
||||
"""初始化数据库,创建所有表"""
|
||||
try:
|
||||
logger.info("开始初始化数据库...")
|
||||
# 初始化数据库表
|
||||
DBConfig.init_db()
|
||||
logger.info("数据库表创建成功")
|
||||
|
||||
# 初始化基础数据
|
||||
logger.info("开始初始化基础数据...")
|
||||
init_base_data(db_session)
|
||||
logger.info("基础数据初始化成功")
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"数据库初始化失败: {str(e)}")
|
||||
raise
|
||||
|
||||
def init_base_data(db_session):
|
||||
"""初始化基础数据"""
|
||||
try:
|
||||
# 导入需要的模型
|
||||
# 检查是否已存在组件分类
|
||||
existing_categories = db_session.query(ComponentCategory).filter(ComponentCategory.is_deleted == False).all()
|
||||
if existing_categories:
|
||||
logger.info("基础数据已存在,跳过初始化")
|
||||
return
|
||||
|
||||
# 创建组件分类
|
||||
categories = []
|
||||
for category_enum in ComponentCategoryEnum:
|
||||
category = ComponentCategory(
|
||||
name=ComponentCategoryConfig.get_category_name(category_enum),
|
||||
code=category_enum,
|
||||
description=ComponentCategoryConfig.get_category_description(category_enum),
|
||||
icon=f"icon-{category_enum.value}",
|
||||
order=ComponentCategoryConfig.get_category_order(category_enum)
|
||||
)
|
||||
categories.append(category)
|
||||
db_session.add(category)
|
||||
|
||||
db_session.commit()
|
||||
logger.info(f"创建了 {len(categories)} 个组件分类")
|
||||
|
||||
# 这里可以添加更多基础数据的初始化,如组件类型、系统组件等
|
||||
|
||||
except Exception as e:
|
||||
db_session.rollback()
|
||||
logger.error(f"基础数据初始化失败: {str(e)}")
|
||||
raise
|
||||
|
||||
# 初始化数据库
|
||||
init_database()
|
||||
|
||||
# 注册所有组件
|
||||
register_all_components()
|
||||
|
||||
# 注册API路由
|
||||
app.include_router(task_router, prefix=ApiConfig.PREFIX)
|
||||
app.include_router(workflow_router, prefix=ApiConfig.PREFIX)
|
||||
app.include_router(component_router, prefix=ApiConfig.PREFIX)
|
||||
|
||||
# 请求中间件
|
||||
@app.middleware("http")
|
||||
async def add_process_time_header(request: Request, call_next):
|
||||
start_time = time.time()
|
||||
response = await call_next(request)
|
||||
process_time = time.time() - start_time
|
||||
response.headers["X-Process-Time"] = str(process_time)
|
||||
return response
|
||||
|
||||
# 数据库会话中间件
|
||||
@app.middleware("http")
|
||||
async def db_session_middleware(request: Request, call_next):
|
||||
try:
|
||||
response = await call_next(request)
|
||||
return response
|
||||
finally:
|
||||
db_session.remove()
|
||||
|
||||
# 全局异常处理
|
||||
@app.exception_handler(TianfengTaskError)
|
||||
async def tianfeng_task_error_handler(request: Request, exc: TianfengTaskError):
|
||||
"""处理天风任务模块异常"""
|
||||
return JSONResponse(
|
||||
status_code=ApiResponseCode.BAD_REQUEST,
|
||||
content={
|
||||
"code": ApiResponseCode.BAD_REQUEST,
|
||||
"message": str(exc),
|
||||
"data": None
|
||||
}
|
||||
)
|
||||
|
||||
@app.exception_handler(Exception)
|
||||
async def global_exception_handler(request: Request, exc: Exception):
|
||||
"""处理通用异常"""
|
||||
logger.exception("未处理的异常")
|
||||
return JSONResponse(
|
||||
status_code=ApiResponseCode.SERVER_ERROR,
|
||||
content={
|
||||
"code": ApiResponseCode.SERVER_ERROR,
|
||||
"message": f"{ApiResponseMessage.SERVER_ERROR}: {str(exc)}",
|
||||
"data": None
|
||||
}
|
||||
)
|
||||
|
||||
# 健康检查接口
|
||||
@app.get(f"{ApiConfig.PREFIX}/health")
|
||||
async def health_check():
|
||||
"""健康检查"""
|
||||
return {
|
||||
"code": ApiResponseCode.SUCCESS,
|
||||
"message": "服务正常",
|
||||
"data": {
|
||||
"app_name": AppConfig.NAME,
|
||||
"app_version": AppConfig.VERSION,
|
||||
"status": "healthy"
|
||||
}
|
||||
}
|
||||
|
||||
# 主函数
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
logger.info(f"启动天风任务模块服务,调试模式: {AppConfig.DEBUG}")
|
||||
uvicorn.run("app:app", host=ServerConfig.HOST, port=ServerConfig.PORT, reload=ServerConfig.RELOAD)
|
0
components/__init__.py
Normal file
0
components/__init__.py
Normal file
BIN
components/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
components/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
components/__pycache__/base_components.cpython-312.pyc
Normal file
BIN
components/__pycache__/base_components.cpython-312.pyc
Normal file
Binary file not shown.
BIN
components/__pycache__/device_components.cpython-312.pyc
Normal file
BIN
components/__pycache__/device_components.cpython-312.pyc
Normal file
Binary file not shown.
BIN
components/__pycache__/flow_components.cpython-312.pyc
Normal file
BIN
components/__pycache__/flow_components.cpython-312.pyc
Normal file
Binary file not shown.
BIN
components/__pycache__/http_components.cpython-312.pyc
Normal file
BIN
components/__pycache__/http_components.cpython-312.pyc
Normal file
Binary file not shown.
BIN
components/__pycache__/robot_components.cpython-312.pyc
Normal file
BIN
components/__pycache__/robot_components.cpython-312.pyc
Normal file
Binary file not shown.
BIN
components/__pycache__/script_components.cpython-312.pyc
Normal file
BIN
components/__pycache__/script_components.cpython-312.pyc
Normal file
Binary file not shown.
BIN
components/__pycache__/site_components.cpython-312.pyc
Normal file
BIN
components/__pycache__/site_components.cpython-312.pyc
Normal file
Binary file not shown.
BIN
components/__pycache__/subtask_components.cpython-312.pyc
Normal file
BIN
components/__pycache__/subtask_components.cpython-312.pyc
Normal file
Binary file not shown.
BIN
components/__pycache__/task_components.cpython-312.pyc
Normal file
BIN
components/__pycache__/task_components.cpython-312.pyc
Normal file
Binary file not shown.
222
components/base_components.py
Normal file
222
components/base_components.py
Normal file
@ -0,0 +1,222 @@
|
||||
# components/base_components.py
|
||||
"""
|
||||
基础组件
|
||||
"""
|
||||
from typing import Dict, Any, Optional
|
||||
import time
|
||||
import hashlib
|
||||
import json
|
||||
import uuid
|
||||
from core.component import Component, ComponentFactory
|
||||
from core.exceptions import ComponentError
|
||||
|
||||
@ComponentFactory.register("check_task_instance_id_exists")
|
||||
class CheckTaskInstanceIdExistsComponent(Component):
|
||||
"""校验任务实例Id是否存在组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
task_instance_id = self.resolve_param("task_instance_id")
|
||||
|
||||
# 验证必要参数
|
||||
self.validate_required_params(["task_instance_id"])
|
||||
|
||||
# 实际实现中,这里应该查询数据库验证任务ID是否存在
|
||||
# 这里简化为检查是否与当前任务ID相同
|
||||
exists = task_instance_id == self.context.task_id
|
||||
|
||||
result = {"exists": exists}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
@ComponentFactory.register("create_unique_id")
|
||||
class CreateUniqueIdComponent(Component):
|
||||
"""创建唯一ID组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
prefix = self.resolve_param("prefix", "")
|
||||
|
||||
# 生成唯一ID
|
||||
unique_id = f"{prefix}{uuid.uuid4().hex}"
|
||||
|
||||
result = {"id": unique_id}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
@ComponentFactory.register("current_timestamp")
|
||||
class CurrentTimestampComponent(Component):
|
||||
"""当前时间戳组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取当前时间戳(毫秒级)
|
||||
timestamp = int(time.time() * 1000)
|
||||
|
||||
result = {"timestamp": timestamp}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
@ComponentFactory.register("current_time")
|
||||
class CurrentTimeComponent(Component):
|
||||
"""当前时间组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
format_str = self.resolve_param("format", "%Y-%m-%d %H:%M:%S")
|
||||
|
||||
# 获取格式化的当前时间
|
||||
current_time = time.strftime(format_str, time.localtime())
|
||||
|
||||
result = {"time": current_time}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
@ComponentFactory.register("execute_sql")
|
||||
class ExecuteSqlComponent(Component):
|
||||
"""执行SQL组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
sql = self.resolve_param("sql")
|
||||
params = self.resolve_param("params", [])
|
||||
|
||||
# 验证必要参数
|
||||
self.validate_required_params(["sql"])
|
||||
|
||||
# 实际实现中,这里应该执行SQL语句
|
||||
# 这里简化为返回模拟结果
|
||||
result = {
|
||||
"affected_rows": 1,
|
||||
"success": True
|
||||
}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
@ComponentFactory.register("query_sql")
|
||||
class QuerySqlComponent(Component):
|
||||
"""查询SQL组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
sql = self.resolve_param("sql")
|
||||
params = self.resolve_param("params", [])
|
||||
|
||||
# 验证必要参数
|
||||
self.validate_required_params(["sql"])
|
||||
|
||||
# 实际实现中,这里应该执行SQL查询
|
||||
# 这里简化为返回模拟结果
|
||||
result = {
|
||||
"result": [{"id": 1, "name": "示例数据"}],
|
||||
"count": 1
|
||||
}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
@ComponentFactory.register("string_md5_encrypt")
|
||||
class StringMd5EncryptComponent(Component):
|
||||
"""字符串MD5加密组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
text = self.resolve_param("text")
|
||||
uppercase = self.resolve_param("uppercase", False)
|
||||
salt = self.resolve_param("salt", "")
|
||||
|
||||
# 验证必要参数
|
||||
self.validate_required_params(["text"])
|
||||
|
||||
# 执行MD5加密
|
||||
md5_hash = hashlib.md5((text + salt).encode()).hexdigest()
|
||||
if uppercase:
|
||||
md5_hash = md5_hash.upper()
|
||||
|
||||
result = {"md5": md5_hash}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
@ComponentFactory.register("string_to_json_array")
|
||||
class StringToJsonArrayComponent(Component):
|
||||
"""将字符串转换成JSON数组组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
text = self.resolve_param("text")
|
||||
default = self.resolve_param("default", [])
|
||||
|
||||
# 验证必要参数
|
||||
self.validate_required_params(["text"])
|
||||
|
||||
# 执行转换
|
||||
try:
|
||||
json_array = json.loads(text)
|
||||
if not isinstance(json_array, list):
|
||||
json_array = default
|
||||
except Exception:
|
||||
json_array = default
|
||||
|
||||
result = {"jsonArray": json_array}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
@ComponentFactory.register("string_to_json_object")
|
||||
class StringToJsonObjectComponent(Component):
|
||||
"""将字符串转换成JSON对象组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
text = self.resolve_param("text")
|
||||
default = self.resolve_param("default", {})
|
||||
|
||||
# 验证必要参数
|
||||
self.validate_required_params(["text"])
|
||||
|
||||
# 执行转换
|
||||
try:
|
||||
json_object = json.loads(text)
|
||||
if not isinstance(json_object, dict):
|
||||
json_object = default
|
||||
except Exception:
|
||||
json_object = default
|
||||
|
||||
result = {"jsonObject": json_object}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
@ComponentFactory.register("print")
|
||||
class PrintComponent(Component):
|
||||
"""打印组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
content = self.resolve_param("content")
|
||||
|
||||
# 验证必要参数
|
||||
self.validate_required_params(["content"])
|
||||
|
||||
# 执行打印
|
||||
print(content)
|
||||
|
||||
result = {"printed": True, "content": content}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
89
components/device_components.py
Normal file
89
components/device_components.py
Normal file
@ -0,0 +1,89 @@
|
||||
# components/device_components.py
|
||||
"""
|
||||
设备组件
|
||||
"""
|
||||
from typing import Dict, Any, Optional
|
||||
import time
|
||||
from core.component import Component, ComponentFactory
|
||||
from core.exceptions import ComponentError
|
||||
|
||||
@ComponentFactory.register("wait_modbus_value")
|
||||
class WaitModbusValueComponent(Component):
|
||||
"""通用等待Modbus值(Name)组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
target_value = self.resolve_param("target_value")
|
||||
name = self.resolve_param("name")
|
||||
address = self.resolve_param("address")
|
||||
description = self.resolve_param("description", "")
|
||||
timeout = self.resolve_param("timeout", 30000) # 默认30秒超时
|
||||
|
||||
# 验证必要参数
|
||||
self.validate_required_params(["target_value", "name"])
|
||||
|
||||
# 实际实现中,这里应该轮询Modbus设备直到值匹配或超时
|
||||
# 这里简化为返回模拟数据
|
||||
start_time = time.time() * 1000
|
||||
current_value = None
|
||||
success = False
|
||||
|
||||
# 模拟等待过程
|
||||
while (time.time() * 1000 - start_time) < timeout:
|
||||
# 模拟读取当前值
|
||||
current_value = target_value # 假设值已经匹配
|
||||
|
||||
if current_value == target_value:
|
||||
success = True
|
||||
break
|
||||
|
||||
# 等待一段时间再次检查
|
||||
time.sleep(0.1)
|
||||
|
||||
if not success:
|
||||
result = {
|
||||
"success": False,
|
||||
"timeout": True,
|
||||
"name": name,
|
||||
"address": address,
|
||||
"targetValue": target_value,
|
||||
"currentValue": current_value
|
||||
}
|
||||
else:
|
||||
result = {
|
||||
"success": True,
|
||||
"name": name,
|
||||
"address": address,
|
||||
"value": current_value
|
||||
}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
@ComponentFactory.register("write_modbus_value")
|
||||
class WriteModbusValueComponent(Component):
|
||||
"""通用写入Modbus值(Name)组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
write_value = self.resolve_param("write_value")
|
||||
name = self.resolve_param("name")
|
||||
address = self.resolve_param("address")
|
||||
description = self.resolve_param("description", "")
|
||||
|
||||
# 验证必要参数
|
||||
self.validate_required_params(["write_value", "name"])
|
||||
|
||||
# 实际实现中,这里应该调用Modbus设备API写入值
|
||||
# 这里简化为返回模拟数据
|
||||
result = {
|
||||
"success": True,
|
||||
"name": name,
|
||||
"address": address,
|
||||
"value": write_value
|
||||
}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
188
components/flow_components.py
Normal file
188
components/flow_components.py
Normal file
@ -0,0 +1,188 @@
|
||||
# components/flow_components.py
|
||||
"""
|
||||
流程控制组件
|
||||
"""
|
||||
from typing import Dict, Any, List, Optional
|
||||
from core.component import Component, ComponentFactory
|
||||
from core.exceptions import ComponentError
|
||||
|
||||
@ComponentFactory.register("if")
|
||||
class IfComponent(Component):
|
||||
"""If条件组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 注意:实际的条件逻辑在工作流执行器中实现
|
||||
# 这里只是记录条件表达式
|
||||
condition = self.resolve_param("condition")
|
||||
|
||||
# 验证必要参数
|
||||
self.validate_required_params(["condition"])
|
||||
|
||||
result = {"condition": condition}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
@ComponentFactory.register("if_else")
|
||||
class IfElseComponent(Component):
|
||||
"""If-Else条件组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 注意:实际的条件逻辑在工作流执行器中实现
|
||||
# 这里只是记录条件表达式
|
||||
condition = self.resolve_param("condition")
|
||||
|
||||
# 验证必要参数
|
||||
self.validate_required_params(["condition"])
|
||||
|
||||
result = {"condition": condition}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
@ComponentFactory.register("if_else_if")
|
||||
class IfElseIfComponent(Component):
|
||||
"""If-Else-If条件组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 注意:实际的条件逻辑在工作流执行器中实现
|
||||
# 这里只是记录条件表达式
|
||||
conditions = self.resolve_param("conditions", [])
|
||||
|
||||
# 验证必要参数
|
||||
self.validate_required_params(["conditions"])
|
||||
|
||||
result = {"conditions": conditions}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
@ComponentFactory.register("for_each")
|
||||
class ForEachComponent(Component):
|
||||
"""遍历数组组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 注意:实际的循环逻辑在工作流执行器中实现
|
||||
# 这里只是记录循环参数
|
||||
items = self.resolve_param("items")
|
||||
|
||||
# 验证必要参数
|
||||
self.validate_required_params(["items"])
|
||||
|
||||
result = {"items": items}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
@ComponentFactory.register("while")
|
||||
class WhileComponent(Component):
|
||||
"""While循环组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 注意:实际的循环逻辑在工作流执行器中实现
|
||||
# 这里只是记录循环条件
|
||||
condition = self.resolve_param("condition")
|
||||
max_iterations = self.resolve_param("max_iterations", 100)
|
||||
|
||||
# 验证必要参数
|
||||
self.validate_required_params(["condition"])
|
||||
|
||||
result = {
|
||||
"condition": condition,
|
||||
"max_iterations": max_iterations
|
||||
}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
@ComponentFactory.register("break")
|
||||
class BreakComponent(Component):
|
||||
"""Break组件,中断当前循环"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 注意:实际的中断逻辑在工作流执行器中实现
|
||||
# 这里只是记录中断意图
|
||||
result = {"break_requested": True}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
@ComponentFactory.register("return")
|
||||
class ReturnComponent(Component):
|
||||
"""Return组件,结束当前流程并返回结果"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
return_value = self.resolve_param("return_value")
|
||||
|
||||
# 注意:实际的返回逻辑在工作流执行器中实现
|
||||
# 这里只是记录返回值
|
||||
result = {"return_value": return_value, "return_requested": True}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
@ComponentFactory.register("delay")
|
||||
class DelayComponent(Component):
|
||||
"""延迟组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
milliseconds = self.resolve_param("milliseconds", 1000)
|
||||
|
||||
# 验证必要参数
|
||||
self.validate_required_params(["milliseconds"])
|
||||
|
||||
# 执行延迟
|
||||
import time
|
||||
time.sleep(milliseconds / 1000)
|
||||
|
||||
result = {"delayed_ms": milliseconds}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
@ComponentFactory.register("parallel_execute")
|
||||
class ParallelExecuteComponent(Component):
|
||||
"""并行执行组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 注意:实际的并行执行逻辑在工作流执行器中实现
|
||||
# 这里只是记录并行执行意图
|
||||
result = {"parallel_requested": True}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
@ComponentFactory.register("serial_execute")
|
||||
class SerialExecuteComponent(Component):
|
||||
"""串行执行组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 注意:实际的串行执行逻辑在工作流执行器中实现
|
||||
# 这里只是记录串行执行意图
|
||||
result = {"serial_requested": True}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
@ComponentFactory.register("throw_exception")
|
||||
class ThrowExceptionComponent(Component):
|
||||
"""抛出异常组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
error_message = self.resolve_param("error_message", "自定义异常")
|
||||
|
||||
# 抛出异常
|
||||
raise ComponentError(error_message)
|
104
components/http_components.py
Normal file
104
components/http_components.py
Normal file
@ -0,0 +1,104 @@
|
||||
# components/http_components.py
|
||||
"""
|
||||
HTTP请求组件
|
||||
"""
|
||||
import requests
|
||||
from typing import Dict, Any, Optional
|
||||
from core.component import Component, ComponentFactory
|
||||
from core.exceptions import ComponentError
|
||||
|
||||
|
||||
@ComponentFactory.register("http_get_request")
|
||||
class HttpGetRequestComponent(Component):
|
||||
"""HTTP GET请求组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
url = self.resolve_param("url")
|
||||
headers = self.resolve_param("headers", {})
|
||||
params = self.resolve_param("params", {})
|
||||
timeout = self.resolve_param("timeout", 30)
|
||||
|
||||
# 验证必要参数
|
||||
self.validate_required_params(["url"])
|
||||
|
||||
try:
|
||||
# 执行HTTP请求
|
||||
response = requests.get(url, headers=headers, params=params, timeout=timeout)
|
||||
response.raise_for_status()
|
||||
|
||||
# 处理响应
|
||||
try:
|
||||
response_data = response.json()
|
||||
except ValueError:
|
||||
response_data = response.text
|
||||
|
||||
result = {
|
||||
"response": response_data,
|
||||
"status_code": response.status_code,
|
||||
"headers": dict(response.headers)
|
||||
}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
except requests.RequestException as e:
|
||||
error_result = {
|
||||
"error": str(e),
|
||||
"status_code": getattr(e.response, "status_code", None) if hasattr(e, "response") else None
|
||||
}
|
||||
self.store_result(error_result)
|
||||
raise ComponentError(f"HTTP GET请求失败: {str(e)}")
|
||||
|
||||
@ComponentFactory.register("http_post_request")
|
||||
class HttpPostRequestComponent(Component):
|
||||
"""HTTP POST请求组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
url = self.resolve_param("url")
|
||||
data = self.resolve_param("data", {})
|
||||
headers = self.resolve_param("headers", {})
|
||||
content_type = self.resolve_param("content_type", "json")
|
||||
timeout = self.resolve_param("timeout", 30)
|
||||
|
||||
# 验证必要参数
|
||||
self.validate_required_params(["url"])
|
||||
|
||||
try:
|
||||
# 设置内容类型
|
||||
if content_type == "json" and "Content-Type" not in headers:
|
||||
headers["Content-Type"] = "application/json"
|
||||
|
||||
# 执行HTTP请求
|
||||
if content_type == "json":
|
||||
response = requests.post(url, json=data, headers=headers, timeout=timeout)
|
||||
else:
|
||||
response = requests.post(url, data=data, headers=headers, timeout=timeout)
|
||||
|
||||
response.raise_for_status()
|
||||
|
||||
# 处理响应
|
||||
try:
|
||||
response_data = response.json()
|
||||
except ValueError:
|
||||
response_data = response.text
|
||||
|
||||
result = {
|
||||
"response": response_data,
|
||||
"status_code": response.status_code,
|
||||
"headers": dict(response.headers)
|
||||
}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
except requests.RequestException as e:
|
||||
error_result = {
|
||||
"error": str(e),
|
||||
"status_code": getattr(e.response, "status_code", None) if hasattr(e, "response") else None
|
||||
}
|
||||
self.store_result(error_result)
|
||||
raise ComponentError(f"HTTP POST请求失败: {str(e)}")
|
176
components/robot_components.py
Normal file
176
components/robot_components.py
Normal file
@ -0,0 +1,176 @@
|
||||
# components/robot_components.py
|
||||
from typing import Dict, Any, List, Optional
|
||||
from core.component import Component, ComponentFactory
|
||||
from core.exceptions import ComponentError
|
||||
|
||||
@ComponentFactory.register("select_robot")
|
||||
class SelectRobotComponent(Component):
|
||||
"""选择执行机器人组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
priority = self.resolve_param("priority", "distance")
|
||||
robot_id = self.resolve_param("robot_id")
|
||||
tags = self.resolve_param("tags", [])
|
||||
robot_group = self.resolve_param("robot_group")
|
||||
|
||||
# 如果直接指定了机器人ID,则使用该机器人
|
||||
if robot_id:
|
||||
selected_robot = robot_id
|
||||
else:
|
||||
# 实际实现中,这里应该调用机器人调度系统API选择合适的机器人
|
||||
# 这里简化为返回模拟数据
|
||||
selected_robot = "robot_001"
|
||||
|
||||
# 设置执行机器人
|
||||
self.context.set_executing_robot(selected_robot)
|
||||
|
||||
result = {
|
||||
"selectedAgvId": selected_robot,
|
||||
"priority": priority
|
||||
}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
@ComponentFactory.register("get_robot_position")
|
||||
class GetRobotPositionComponent(Component):
|
||||
"""获取机器人位置组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
robot_id = self.resolve_param("robot_id")
|
||||
|
||||
# 验证必要参数
|
||||
self.validate_required_params(["robot_id"])
|
||||
|
||||
# 实际实现中,这里应该调用机器人系统API获取位置
|
||||
# 这里简化为返回模拟数据
|
||||
position = {
|
||||
"x": 100.5,
|
||||
"y": 200.3,
|
||||
"angle": 45.0,
|
||||
"station": "station_A"
|
||||
}
|
||||
|
||||
result = {
|
||||
"position": position,
|
||||
"station": position["station"]
|
||||
}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
# components/robot_components.py (继续)
|
||||
@ComponentFactory.register("robot_action")
|
||||
class RobotActionComponent(Component):
|
||||
"""机器人通用动作组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
target_station = self.resolve_param("target_station")
|
||||
robot_id = self.resolve_param("robot_id", self.context.executing_robot)
|
||||
goods_id = self.resolve_param("goods_id")
|
||||
speed = self.resolve_param("speed")
|
||||
|
||||
# 验证必要参数
|
||||
self.validate_required_params(["target_station"])
|
||||
|
||||
if not robot_id:
|
||||
raise ComponentError("未指定机器人ID,请先选择执行机器人或直接指定robot_id参数")
|
||||
|
||||
# 实际实现中,这里应该调用机器人控制API执行动作
|
||||
# 这里简化为返回模拟数据
|
||||
result = {
|
||||
"robotId": robot_id,
|
||||
"targetStation": target_station,
|
||||
"goodsId": goods_id,
|
||||
"status": "executing"
|
||||
}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
@ComponentFactory.register("change_robot_destination")
|
||||
class ChangeRobotDestinationComponent(Component):
|
||||
"""机器人更换目的地组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
target_station = self.resolve_param("target_station")
|
||||
robot_id = self.resolve_param("robot_id", self.context.executing_robot)
|
||||
goods_id = self.resolve_param("goods_id")
|
||||
|
||||
# 验证必要参数
|
||||
self.validate_required_params(["target_station"])
|
||||
|
||||
if not robot_id:
|
||||
raise ComponentError("未指定机器人ID,请先选择执行机器人或直接指定robot_id参数")
|
||||
|
||||
# 实际实现中,这里应该调用机器人控制API更改目的地
|
||||
# 这里简化为返回模拟数据
|
||||
result = {
|
||||
"robotId": robot_id,
|
||||
"newTargetStation": target_station,
|
||||
"goodsId": goods_id,
|
||||
"status": "destination_changed"
|
||||
}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
@ComponentFactory.register("get_robot_battery")
|
||||
class GetRobotBatteryComponent(Component):
|
||||
"""获取机器人电量组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
robot_id = self.resolve_param("robot_id", self.context.executing_robot)
|
||||
|
||||
# 验证必要参数
|
||||
if not robot_id:
|
||||
raise ComponentError("未指定机器人ID,请先选择执行机器人或直接指定robot_id参数")
|
||||
|
||||
# 实际实现中,这里应该调用机器人系统API获取电量
|
||||
# 这里简化为返回模拟数据
|
||||
battery_level = 85 # 电量百分比
|
||||
|
||||
result = {"batteryLevel": battery_level}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
@ComponentFactory.register("get_robot_pgv_code")
|
||||
class GetRobotPGVCodeComponent(Component):
|
||||
"""获取机器人PGV码组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
robot_id = self.resolve_param("robot_id", self.context.executing_robot)
|
||||
|
||||
# 验证必要参数
|
||||
if not robot_id:
|
||||
raise ComponentError("未指定机器人ID,请先选择执行机器人或直接指定robot_id参数")
|
||||
|
||||
# 实际实现中,这里应该调用机器人系统API获取PGV码
|
||||
# 这里简化为返回模拟数据
|
||||
pgv_code = {
|
||||
"code": "PGV12345",
|
||||
"timestamp": int(time.time() * 1000),
|
||||
"coordinates": {
|
||||
"x": 123.45,
|
||||
"y": 67.89
|
||||
},
|
||||
"status": "valid"
|
||||
}
|
||||
|
||||
result = {"codeInfo": pgv_code}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
74
components/script_components.py
Normal file
74
components/script_components.py
Normal file
@ -0,0 +1,74 @@
|
||||
# components/script_components.py
|
||||
"""
|
||||
脚本执行组件
|
||||
"""
|
||||
from typing import Dict, Any, Optional
|
||||
from core.component import Component, ComponentFactory
|
||||
from core.exceptions import ComponentError
|
||||
|
||||
@ComponentFactory.register("run_script")
|
||||
class RunScriptComponent(Component):
|
||||
"""脚本执行组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
script_code = self.resolve_param("script_code")
|
||||
params = self.resolve_param("params", {})
|
||||
|
||||
# 验证必要参数
|
||||
self.validate_required_params(["script_code"])
|
||||
|
||||
# 创建本地变量字典
|
||||
locals_dict = {
|
||||
"params": params,
|
||||
"context": self.context,
|
||||
"blocks": self.context.blocks,
|
||||
"variables": self.context.variables,
|
||||
"task": {
|
||||
"id": self.context.task_id,
|
||||
"variables": self.context.variables
|
||||
},
|
||||
"taskInputs": self.context.task_inputs
|
||||
}
|
||||
|
||||
try:
|
||||
# 执行脚本代码
|
||||
exec(script_code, {"__builtins__": __builtins__}, locals_dict)
|
||||
|
||||
# 获取结果
|
||||
result = {"result": locals_dict.get("result", None)}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
error_result = {"error": str(e)}
|
||||
self.store_result(error_result)
|
||||
raise ComponentError(f"脚本执行失败: {str(e)}")
|
||||
|
||||
@ComponentFactory.register("set_task_variables")
|
||||
class SetTaskVariablesComponent(Component):
|
||||
"""设置任务变量组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
variables = self.params.copy()
|
||||
|
||||
# 移除特殊参数
|
||||
if "id" in variables:
|
||||
del variables["id"]
|
||||
|
||||
updated_vars = {}
|
||||
|
||||
# 设置任务变量
|
||||
for name, value in variables.items():
|
||||
resolved_value = self.resolve_param(name)
|
||||
self.context.set_variable(name, resolved_value)
|
||||
updated_vars[name] = resolved_value
|
||||
|
||||
result = {"updated_variables": list(updated_vars.keys())}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
296
components/site_components.py
Normal file
296
components/site_components.py
Normal file
@ -0,0 +1,296 @@
|
||||
# components/site_components.py
|
||||
"""
|
||||
库位组件
|
||||
"""
|
||||
from typing import Dict, Any, List, Optional
|
||||
from core.component import Component, ComponentFactory
|
||||
from core.exceptions import ComponentError
|
||||
|
||||
@ComponentFactory.register("batch_set_site")
|
||||
class BatchSetSiteComponent(Component):
|
||||
"""批量设置库位组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
site_ids = self.resolve_param("site_ids", [])
|
||||
area_set = self.resolve_param("area_set")
|
||||
occupied = self.resolve_param("occupied")
|
||||
has_material = self.resolve_param("has_material")
|
||||
is_material_locked = self.resolve_param("is_material_locked")
|
||||
|
||||
# 验证必要参数
|
||||
self.validate_required_params(["site_ids"])
|
||||
|
||||
# 实际实现中,这里应该调用库位管理系统API批量设置库位
|
||||
# 这里简化为返回模拟结果
|
||||
result = {
|
||||
"affected_sites": site_ids,
|
||||
"success": True
|
||||
}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
@ComponentFactory.register("get_dense_site")
|
||||
class GetDenseSiteComponent(Component):
|
||||
"""获取密集库位组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
area_set = self.resolve_param("area_set")
|
||||
operation = self.resolve_param("operation") # 取/放
|
||||
goods = self.resolve_param("goods")
|
||||
auto_lock = self.resolve_param("auto_lock", False)
|
||||
retry = self.resolve_param("retry", False)
|
||||
|
||||
# 验证必要参数
|
||||
self.validate_required_params(["area_set", "operation"])
|
||||
|
||||
# 实际实现中,这里应该调用库位管理系统API获取密集库位
|
||||
# 这里简化为返回模拟结果
|
||||
site_id = "site_001"
|
||||
|
||||
result = {"siteId": site_id}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
@ComponentFactory.register("query_site")
|
||||
class QuerySiteComponent(Component):
|
||||
"""查询库位组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
site_id = self.resolve_param("site_id")
|
||||
goods = self.resolve_param("goods")
|
||||
has_goods = self.resolve_param("has_goods")
|
||||
is_locked = self.resolve_param("is_locked")
|
||||
is_inventory = self.resolve_param("is_inventory")
|
||||
retry_interval = self.resolve_param("retry_interval", 1000)
|
||||
|
||||
# 实际实现中,这里应该调用库位管理系统API查询库位
|
||||
# 这里简化为返回模拟结果
|
||||
site_info = {
|
||||
"id": site_id or "site_001",
|
||||
"hasGoods": has_goods if has_goods is not None else False,
|
||||
"isLocked": is_locked if is_locked is not None else False,
|
||||
"isInventory": is_inventory if is_inventory is not None else True,
|
||||
"goods": goods
|
||||
}
|
||||
|
||||
result = {"site": site_info}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
@ComponentFactory.register("lock_site")
|
||||
class LockSiteComponent(Component):
|
||||
"""锁定库位组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
site_id = self.resolve_param("site_id")
|
||||
reason = self.resolve_param("reason", "任务锁定")
|
||||
|
||||
# 验证必要参数
|
||||
self.validate_required_params(["site_id"])
|
||||
|
||||
# 实际实现中,这里应该调用库位管理系统API锁定库位
|
||||
# 这里简化为返回模拟结果
|
||||
result = {
|
||||
"siteId": site_id,
|
||||
"locked": True,
|
||||
"reason": reason
|
||||
}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
@ComponentFactory.register("unlock_site")
|
||||
class UnlockSiteComponent(Component):
|
||||
"""解锁库位组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
site_id = self.resolve_param("site_id")
|
||||
reason = self.resolve_param("reason", "任务解锁")
|
||||
|
||||
# 验证必要参数
|
||||
self.validate_required_params(["site_id"])
|
||||
|
||||
# 实际实现中,这里应该调用库位管理系统API解锁库位
|
||||
# 这里简化为返回模拟结果
|
||||
result = {
|
||||
"siteId": site_id,
|
||||
"locked": False,
|
||||
"reason": reason
|
||||
}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
@ComponentFactory.register("get_locked_sites_by_task")
|
||||
class GetLockedSitesByTaskComponent(Component):
|
||||
"""根据任务实例ID获取所有加锁库位组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
task_instance_id = self.resolve_param("task_instance_id")
|
||||
|
||||
# 验证必要参数
|
||||
self.validate_required_params(["task_instance_id"])
|
||||
|
||||
# 实际实现中,这里应该调用库位管理系统API获取任务锁定的库位
|
||||
# 这里简化为返回模拟结果
|
||||
site_ids = ["site_001", "site_002"]
|
||||
|
||||
result = {"siteIds": site_ids}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
@ComponentFactory.register("get_site_extension_property")
|
||||
class GetSiteExtensionPropertyComponent(Component):
|
||||
"""获取库位扩展属性值组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
site_id = self.resolve_param("site_id")
|
||||
property_name = self.resolve_param("property_name")
|
||||
|
||||
# 验证必要参数
|
||||
self.validate_required_params(["site_id", "property_name"])
|
||||
|
||||
# 实际实现中,这里应该调用库位管理系统API获取库位扩展属性
|
||||
# 这里简化为返回模拟结果
|
||||
value = f"属性值_{property_name}"
|
||||
|
||||
result = {"value": value}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
@ComponentFactory.register("set_site_extension_property")
|
||||
class SetSiteExtensionPropertyComponent(Component):
|
||||
"""设置库位扩展属性组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
site_id = self.resolve_param("site_id")
|
||||
property_name = self.resolve_param("property_name")
|
||||
property_value = self.resolve_param("property_value")
|
||||
|
||||
# 验证必要参数
|
||||
self.validate_required_params(["site_id", "property_name", "property_value"])
|
||||
|
||||
# 实际实现中,这里应该调用库位管理系统API设置库位扩展属性
|
||||
# 这里简化为返回模拟结果
|
||||
result = {
|
||||
"siteId": site_id,
|
||||
"propertyName": property_name,
|
||||
"propertyValue": property_value,
|
||||
"success": True
|
||||
}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
@ComponentFactory.register("set_site_goods")
|
||||
class SetSiteGoodsComponent(Component):
|
||||
"""设置库位货物组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
site_id = self.resolve_param("site_id")
|
||||
goods = self.resolve_param("goods")
|
||||
|
||||
# 验证必要参数
|
||||
self.validate_required_params(["site_id", "goods"])
|
||||
|
||||
# 实际实现中,这里应该调用库位管理系统API设置库位货物
|
||||
# 这里简化为返回模拟结果
|
||||
result = {
|
||||
"siteId": site_id,
|
||||
"goods": goods,
|
||||
"success": True
|
||||
}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
@ComponentFactory.register("set_site_empty")
|
||||
class SetSiteEmptyComponent(Component):
|
||||
"""设置库位为空组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
site_id = self.resolve_param("site_id")
|
||||
|
||||
# 验证必要参数
|
||||
self.validate_required_params(["site_id"])
|
||||
|
||||
# 实际实现中,这里应该调用库位管理系统API设置库位为空
|
||||
# 这里简化为返回模拟结果
|
||||
result = {
|
||||
"siteId": site_id,
|
||||
"success": True
|
||||
}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
@ComponentFactory.register("set_site_occupied")
|
||||
class SetSiteOccupiedComponent(Component):
|
||||
"""设置库位为占用组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
site_id = self.resolve_param("site_id")
|
||||
|
||||
# 验证必要参数
|
||||
self.validate_required_params(["site_id"])
|
||||
|
||||
# 实际实现中,这里应该调用库位管理系统API设置库位为占用
|
||||
# 这里简化为返回模拟结果
|
||||
result = {
|
||||
"siteId": site_id,
|
||||
"success": True
|
||||
}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
@ComponentFactory.register("set_site_tag")
|
||||
class SetSiteTagComponent(Component):
|
||||
"""设置库位标签组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
site_id = self.resolve_param("site_id")
|
||||
tag = self.resolve_param("tag")
|
||||
|
||||
# 验证必要参数
|
||||
self.validate_required_params(["site_id", "tag"])
|
||||
|
||||
# 实际实现中,这里应该调用库位管理系统API设置库位标签
|
||||
# 这里简化为返回模拟结果
|
||||
result = {
|
||||
"siteId": site_id,
|
||||
"tag": tag,
|
||||
"success": True
|
||||
}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
75
components/subtask_components.py
Normal file
75
components/subtask_components.py
Normal file
@ -0,0 +1,75 @@
|
||||
"""
|
||||
子任务组件
|
||||
"""
|
||||
# components/subtask_components.py
|
||||
from typing import Dict, Any, Optional
|
||||
from core.component import Component, ComponentFactory
|
||||
from core.exceptions import ComponentError
|
||||
from services.workflow_service import WorkflowService
|
||||
|
||||
@ComponentFactory.register("subtask")
|
||||
class SubtaskComponent(Component):
|
||||
"""子任务组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
subtask_id = self.resolve_param("subtask_id")
|
||||
async_execute = self.resolve_param("async_execute", False)
|
||||
input_params = {}
|
||||
|
||||
# 收集所有以input_开头的参数作为子任务输入
|
||||
for key, value in self.params.items():
|
||||
if key.startswith("input_"):
|
||||
param_name = key[6:] # 去掉"input_"前缀
|
||||
input_params[param_name] = self.resolve_param(key)
|
||||
|
||||
# 验证必要参数
|
||||
self.validate_required_params(["subtask_id"])
|
||||
|
||||
try:
|
||||
# 获取工作流服务
|
||||
workflow_service = WorkflowService()
|
||||
|
||||
# 获取子任务定义
|
||||
subtask_definition = workflow_service.get_workflow_by_id(subtask_id)
|
||||
|
||||
if not subtask_definition:
|
||||
raise ComponentError(f"找不到子任务: {subtask_id}")
|
||||
|
||||
# 执行子任务
|
||||
if async_execute:
|
||||
# 异步执行子任务
|
||||
# 实际实现中,这里应该启动一个新线程或任务来执行子任务
|
||||
# 这里简化为返回模拟数据
|
||||
result = {
|
||||
"subtaskId": subtask_id,
|
||||
"status": "started",
|
||||
"async": True,
|
||||
"inputParams": input_params
|
||||
}
|
||||
else:
|
||||
# 同步执行子任务
|
||||
subtask_result = workflow_service.execute_workflow(
|
||||
subtask_definition,
|
||||
input_params
|
||||
)
|
||||
|
||||
result = {
|
||||
"subtaskId": subtask_id,
|
||||
"status": subtask_result.get("task_status", "completed"),
|
||||
"async": False,
|
||||
"result": subtask_result
|
||||
}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
error_result = {
|
||||
"subtaskId": subtask_id,
|
||||
"status": "failed",
|
||||
"error": str(e)
|
||||
}
|
||||
self.store_result(error_result)
|
||||
raise ComponentError(f"子任务执行失败: {str(e)}")
|
142
components/task_components.py
Normal file
142
components/task_components.py
Normal file
@ -0,0 +1,142 @@
|
||||
# components/task_components.py
|
||||
"""
|
||||
任务组件
|
||||
"""
|
||||
from typing import Dict, Any, Optional
|
||||
from core.component import Component, ComponentFactory
|
||||
from core.exceptions import ComponentError
|
||||
|
||||
@ComponentFactory.register("cache_data")
|
||||
class CacheDataComponent(Component):
|
||||
"""缓存数据组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
key = self.resolve_param("key")
|
||||
value = self.resolve_param("value")
|
||||
|
||||
# 验证必要参数
|
||||
self.validate_required_params(["key"])
|
||||
|
||||
# 缓存数据
|
||||
cache_key = f"cache_{key}"
|
||||
self.context.set_variable(cache_key, value)
|
||||
|
||||
result = {"key": key}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
@ComponentFactory.register("clear_cache_data")
|
||||
class ClearCacheDataComponent(Component):
|
||||
"""清除缓存数据组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
key = self.resolve_param("key")
|
||||
|
||||
# 验证必要参数
|
||||
self.validate_required_params(["key"])
|
||||
|
||||
# 清除缓存数据
|
||||
cache_key = f"cache_{key}"
|
||||
if cache_key in self.context.variables:
|
||||
del self.context.variables[cache_key]
|
||||
|
||||
result = {"success": True}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
@ComponentFactory.register("get_cache_data")
|
||||
class GetCacheDataComponent(Component):
|
||||
"""获取缓存数据组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
key = self.resolve_param("key")
|
||||
|
||||
# 验证必要参数
|
||||
self.validate_required_params(["key"])
|
||||
|
||||
# 获取缓存数据
|
||||
cache_key = f"cache_{key}"
|
||||
value = self.context.get_variable(cache_key)
|
||||
|
||||
result = {"value": value}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
@ComponentFactory.register("set_task_status")
|
||||
class SetTaskStatusComponent(Component):
|
||||
"""设置任务状态组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
status = self.resolve_param("status")
|
||||
|
||||
# 验证必要参数
|
||||
self.validate_required_params(["status"])
|
||||
|
||||
# 设置任务状态
|
||||
self.context.set_task_status(status)
|
||||
|
||||
result = {"status": status}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
# components/task_components.py (继续)
|
||||
@ComponentFactory.register("jump_to_block")
|
||||
class JumpToBlockComponent(Component):
|
||||
"""跳到某个块组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
target_block_id = self.resolve_param("target_block_id")
|
||||
|
||||
# 验证必要参数
|
||||
self.validate_required_params(["target_block_id"])
|
||||
|
||||
# 注意:实际的跳转逻辑需要在工作流执行器中实现
|
||||
# 这里只是记录跳转意图
|
||||
result = {
|
||||
"target_block_id": target_block_id,
|
||||
"jump_requested": True
|
||||
}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
||||
|
||||
@ComponentFactory.register("get_task_input_param")
|
||||
class GetTaskInputParamComponent(Component):
|
||||
"""获取任务的输入参数组件"""
|
||||
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
# 获取参数
|
||||
task_instance_id = self.resolve_param("task_instance_id")
|
||||
param_name = self.resolve_param("param_name")
|
||||
|
||||
# 验证必要参数
|
||||
self.validate_required_params(["param_name"])
|
||||
|
||||
# 获取输入参数
|
||||
# 如果指定了任务实例ID,则需要从数据库或其他存储中获取
|
||||
# 这里简化为直接从当前任务的输入参数中获取
|
||||
if task_instance_id and task_instance_id != self.context.task_id:
|
||||
# 实际实现中,这里应该查询数据库获取指定任务的输入参数
|
||||
param_value = None
|
||||
else:
|
||||
param_value = self.context.task_inputs.get(param_name)
|
||||
|
||||
result = {"inputParamValue": param_value}
|
||||
|
||||
# 存储结果
|
||||
self.store_result(result)
|
||||
return result
|
0
config/__init__.py
Normal file
0
config/__init__.py
Normal file
BIN
config/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
config/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
config/__pycache__/api_config.cpython-312.pyc
Normal file
BIN
config/__pycache__/api_config.cpython-312.pyc
Normal file
Binary file not shown.
BIN
config/__pycache__/component_config.cpython-312.pyc
Normal file
BIN
config/__pycache__/component_config.cpython-312.pyc
Normal file
Binary file not shown.
BIN
config/__pycache__/component_detail_config.cpython-312.pyc
Normal file
BIN
config/__pycache__/component_detail_config.cpython-312.pyc
Normal file
Binary file not shown.
BIN
config/__pycache__/component_registry.cpython-312.pyc
Normal file
BIN
config/__pycache__/component_registry.cpython-312.pyc
Normal file
Binary file not shown.
BIN
config/__pycache__/database.cpython-312.pyc
Normal file
BIN
config/__pycache__/database.cpython-312.pyc
Normal file
Binary file not shown.
BIN
config/__pycache__/settings.cpython-312.pyc
Normal file
BIN
config/__pycache__/settings.cpython-312.pyc
Normal file
Binary file not shown.
BIN
config/__pycache__/task_config.cpython-312.pyc
Normal file
BIN
config/__pycache__/task_config.cpython-312.pyc
Normal file
Binary file not shown.
62
config/api_config.py
Normal file
62
config/api_config.py
Normal file
@ -0,0 +1,62 @@
|
||||
"""
|
||||
API响应配置文件
|
||||
包含API响应格式、状态码和消息模板等配置
|
||||
"""
|
||||
from typing import Dict, Any, Optional, Type
|
||||
|
||||
class ApiResponseModel:
|
||||
"""API响应模型配置"""
|
||||
CODE = int # 响应状态码
|
||||
MESSAGE = str # 响应消息
|
||||
DATA = None # 响应数据,可为任意类型
|
||||
|
||||
class ApiResponseCode:
|
||||
"""API响应状态码"""
|
||||
SUCCESS = 200 # 成功
|
||||
BAD_REQUEST = 400 # 请求错误
|
||||
UNAUTHORIZED = 401 # 未授权
|
||||
FORBIDDEN = 403 # 禁止访问
|
||||
NOT_FOUND = 404 # 资源不存在
|
||||
SERVER_ERROR = 500 # 服务器错误
|
||||
|
||||
class ApiResponseMessage:
|
||||
"""API响应消息模板"""
|
||||
# 通用消息
|
||||
SUCCESS = "操作成功"
|
||||
FAILED = "操作失败"
|
||||
SERVER_ERROR = "服务器内部错误"
|
||||
|
||||
# 任务相关消息
|
||||
class Task:
|
||||
"""任务相关消息"""
|
||||
CREATED = "任务创建成功"
|
||||
UPDATED = "任务更新成功"
|
||||
DELETED = "任务删除成功"
|
||||
EXECUTED = "任务执行成功"
|
||||
CANCELED = "任务取消成功"
|
||||
NOT_FOUND = "任务不存在"
|
||||
|
||||
# 工作流相关消息
|
||||
class Workflow:
|
||||
"""工作流相关消息"""
|
||||
SAVED = "工作流保存成功"
|
||||
VALIDATED = "工作流验证成功"
|
||||
NOT_FOUND = "工作流不存在"
|
||||
CREATED = "工作流创建成功"
|
||||
UPDATED = "工作流更新成功"
|
||||
DELETED = "工作流删除成功"
|
||||
EXECUTED = "工作流执行成功"
|
||||
IMPORTED = "工作流导入成功"
|
||||
EXPORTED = "工作流导出成功"
|
||||
|
||||
# 组件相关消息
|
||||
class Component:
|
||||
"""组件相关消息"""
|
||||
FETCHED = "获取组件类型成功"
|
||||
DISCOVERED = "自动发现并注册组件成功"
|
||||
|
||||
class ApiPagination:
|
||||
"""分页配置"""
|
||||
DEFAULT_PAGE = 1 # 默认页码
|
||||
DEFAULT_PAGE_SIZE = 10 # 默认每页记录数
|
||||
MAX_PAGE_SIZE = 100 # 最大每页记录数
|
127
config/component_config.py
Normal file
127
config/component_config.py
Normal file
@ -0,0 +1,127 @@
|
||||
"""
|
||||
组件配置文件
|
||||
包含组件发现、注册和分类的相关配置
|
||||
"""
|
||||
from typing import Dict, List, Any
|
||||
from data.models.component import ComponentCategoryEnum
|
||||
|
||||
class ComponentDiscovery:
|
||||
"""组件自动发现配置"""
|
||||
DEFAULT_PACKAGE = "components" # 默认组件包名
|
||||
AUTO_REGISTER = True # 是否在启动时自动注册所有组件
|
||||
|
||||
class ComponentCategory:
|
||||
"""组件类别配置"""
|
||||
# 组件类别定义
|
||||
HTTP_REQUEST = "HTTP请求"
|
||||
SCRIPT = "脚本"
|
||||
FLOW = "流程"
|
||||
ROBOT = "机器人调度"
|
||||
SITE = "库位"
|
||||
DEVICE = "设备"
|
||||
SUBTASK = "子任务"
|
||||
TASK = "任务"
|
||||
BASE = "基础"
|
||||
|
||||
# 组件类别映射配置
|
||||
# 用于将组件类型映射到对应的类别,便于前端展示
|
||||
@classmethod
|
||||
def get_mapping(cls) -> Dict[str, str]:
|
||||
"""获取组件类型到类别的映射"""
|
||||
return {
|
||||
# HTTP请求组件
|
||||
"http": cls.HTTP_REQUEST,
|
||||
|
||||
# 脚本组件
|
||||
"script": cls.SCRIPT,
|
||||
|
||||
# 流程控制组件
|
||||
"if": cls.FLOW,
|
||||
"if_else": cls.FLOW,
|
||||
"if_else_if": cls.FLOW,
|
||||
"for_each": cls.FLOW,
|
||||
"while": cls.FLOW,
|
||||
"break": cls.FLOW,
|
||||
"return": cls.FLOW,
|
||||
"delay": cls.FLOW,
|
||||
|
||||
# 机器人调度组件
|
||||
"robot": cls.ROBOT,
|
||||
|
||||
# 库位组件
|
||||
"site": cls.SITE,
|
||||
|
||||
# 设备组件
|
||||
"modbus": cls.DEVICE,
|
||||
|
||||
# 子任务组件
|
||||
"subtask": cls.SUBTASK,
|
||||
}
|
||||
|
||||
# 组件分类优先级
|
||||
# 控制前端组件面板中各分类的显示顺序
|
||||
@classmethod
|
||||
def get_priority(cls) -> List[str]:
|
||||
"""获取组件类别优先级列表"""
|
||||
return [
|
||||
cls.SUBTASK,
|
||||
cls.SCRIPT,
|
||||
cls.HTTP_REQUEST,
|
||||
cls.TASK,
|
||||
cls.FLOW,
|
||||
cls.BASE,
|
||||
cls.SITE,
|
||||
cls.ROBOT,
|
||||
cls.DEVICE
|
||||
]
|
||||
|
||||
class ComponentCategoryConfig:
|
||||
"""组件分类配置"""
|
||||
|
||||
@classmethod
|
||||
def get_category_name(cls, category_enum):
|
||||
"""获取分类名称"""
|
||||
names = {
|
||||
ComponentCategoryEnum.SUBTASK: "子任务",
|
||||
ComponentCategoryEnum.SCRIPT: "脚本",
|
||||
ComponentCategoryEnum.HTTP: "HTTP请求",
|
||||
ComponentCategoryEnum.TASK: "任务",
|
||||
ComponentCategoryEnum.FLOW: "流程",
|
||||
ComponentCategoryEnum.BASIC: "基础",
|
||||
ComponentCategoryEnum.STORAGE: "库位",
|
||||
ComponentCategoryEnum.ROBOT: "机器人调度",
|
||||
ComponentCategoryEnum.DEVICE: "设备"
|
||||
}
|
||||
return names.get(category_enum, category_enum.value)
|
||||
|
||||
@classmethod
|
||||
def get_category_description(cls, category_enum):
|
||||
"""获取分类描述"""
|
||||
descriptions = {
|
||||
ComponentCategoryEnum.SUBTASK: "可重用的子任务组件",
|
||||
ComponentCategoryEnum.SCRIPT: "执行脚本代码的组件",
|
||||
ComponentCategoryEnum.HTTP: "发送HTTP请求的组件",
|
||||
ComponentCategoryEnum.TASK: "任务管理相关组件",
|
||||
ComponentCategoryEnum.FLOW: "流程控制相关组件",
|
||||
ComponentCategoryEnum.BASIC: "基础功能组件",
|
||||
ComponentCategoryEnum.STORAGE: "库位管理相关组件",
|
||||
ComponentCategoryEnum.ROBOT: "机器人调度相关组件",
|
||||
ComponentCategoryEnum.DEVICE: "设备控制相关组件"
|
||||
}
|
||||
return descriptions.get(category_enum, f"{category_enum.value}类组件")
|
||||
|
||||
@classmethod
|
||||
def get_category_order(cls, category_enum):
|
||||
"""获取分类排序"""
|
||||
orders = {
|
||||
ComponentCategoryEnum.BASIC: 1,
|
||||
ComponentCategoryEnum.FLOW: 2,
|
||||
ComponentCategoryEnum.TASK: 3,
|
||||
ComponentCategoryEnum.SUBTASK: 4,
|
||||
ComponentCategoryEnum.SCRIPT: 5,
|
||||
ComponentCategoryEnum.HTTP: 6,
|
||||
ComponentCategoryEnum.ROBOT: 7,
|
||||
ComponentCategoryEnum.STORAGE: 8,
|
||||
ComponentCategoryEnum.DEVICE: 9
|
||||
}
|
||||
return orders.get(category_enum, 99)
|
472
config/component_detail_config.py
Normal file
472
config/component_detail_config.py
Normal file
@ -0,0 +1,472 @@
|
||||
"""
|
||||
组件详细配置文件
|
||||
包含各种组件类型及其子组件的配置信息
|
||||
"""
|
||||
from typing import Dict, List, Any, Optional
|
||||
|
||||
class ScriptComponentConfig:
|
||||
"""脚本组件配置"""
|
||||
|
||||
# 脚本组件类型
|
||||
RUN_SCRIPT = "run_script" # 运行脚本
|
||||
SET_VARIABLES = "set_variables" # 设置task.variables
|
||||
|
||||
# 脚本组件详细配置
|
||||
@classmethod
|
||||
def get_components(cls) -> List[Dict[str, Any]]:
|
||||
"""获取脚本组件列表"""
|
||||
return [
|
||||
{
|
||||
"type": "script",
|
||||
"sub_type": cls.RUN_SCRIPT,
|
||||
"name": "运行脚本",
|
||||
"description": "执行JavaScript代码并返回结果",
|
||||
"icon": "code", # 图标名称,前端可用
|
||||
"params": [
|
||||
{
|
||||
"name": "function_name",
|
||||
"label": "函数名",
|
||||
"type": "string",
|
||||
"required": False,
|
||||
"description": "定义脚本中的主函数名称",
|
||||
"value_types": [
|
||||
{
|
||||
"type": "simple",
|
||||
"label": "简单值",
|
||||
"default": True
|
||||
},
|
||||
{
|
||||
"type": "expression",
|
||||
"label": "表达式",
|
||||
"default": False
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"label": "函数参数",
|
||||
"type": "array",
|
||||
"required": False,
|
||||
"description": "传递给脚本的参数",
|
||||
"value_types": [
|
||||
{
|
||||
"type": "simple",
|
||||
"label": "简单值",
|
||||
"default": True
|
||||
},
|
||||
{
|
||||
"type": "expression",
|
||||
"label": "表达式",
|
||||
"default": False
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "script",
|
||||
"sub_type": cls.SET_VARIABLES,
|
||||
"name": "设置task.variables",
|
||||
"description": "设置和管理任务变量",
|
||||
"icon": "variable",
|
||||
"params": [
|
||||
{
|
||||
"name": "function_name",
|
||||
"label": "函数名",
|
||||
"type": "string",
|
||||
"required": False,
|
||||
"description": "定义脚本中的主函数名称",
|
||||
"value_types": [
|
||||
{
|
||||
"type": "simple",
|
||||
"label": "简单值",
|
||||
"default": True
|
||||
},
|
||||
{
|
||||
"type": "expression",
|
||||
"label": "表达式",
|
||||
"default": False
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"label": "函数参数",
|
||||
"type": "array",
|
||||
"required": False,
|
||||
"description": "传递给脚本的参数",
|
||||
"value_types": [
|
||||
{
|
||||
"type": "simple",
|
||||
"label": "简单值",
|
||||
"default": True
|
||||
},
|
||||
{
|
||||
"type": "expression",
|
||||
"label": "表达式",
|
||||
"default": False
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
class HttpComponentConfig:
|
||||
"""HTTP请求组件配置"""
|
||||
|
||||
# HTTP请求组件类型
|
||||
HTTP_REQUEST = "http_request" # HTTP请求
|
||||
|
||||
# HTTP请求组件详细配置
|
||||
@classmethod
|
||||
def get_components(cls) -> List[Dict[str, Any]]:
|
||||
"""获取HTTP请求组件列表"""
|
||||
return [
|
||||
{
|
||||
"type": "http",
|
||||
"sub_type": cls.HTTP_REQUEST,
|
||||
"name": "HTTP请求",
|
||||
"description": "发送HTTP请求并处理响应",
|
||||
"icon": "http",
|
||||
"params": [
|
||||
{
|
||||
"name": "method",
|
||||
"label": "请求方法",
|
||||
"type": "select",
|
||||
"options": ["GET", "POST", "PUT", "DELETE", "PATCH"],
|
||||
"required": True,
|
||||
"description": "HTTP请求方法"
|
||||
},
|
||||
{
|
||||
"name": "url",
|
||||
"label": "请求URL",
|
||||
"type": "string",
|
||||
"required": True,
|
||||
"description": "请求的目标URL"
|
||||
},
|
||||
{
|
||||
"name": "headers",
|
||||
"label": "请求头",
|
||||
"type": "object",
|
||||
"required": False,
|
||||
"description": "HTTP请求头"
|
||||
},
|
||||
{
|
||||
"name": "body",
|
||||
"label": "请求体",
|
||||
"type": "object",
|
||||
"required": False,
|
||||
"description": "HTTP请求体"
|
||||
},
|
||||
{
|
||||
"name": "timeout",
|
||||
"label": "超时时间",
|
||||
"type": "number",
|
||||
"required": False,
|
||||
"description": "请求超时时间(毫秒)"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
class FlowComponentConfig:
|
||||
"""流程控制组件配置"""
|
||||
|
||||
# 流程控制组件类型
|
||||
IF = "if" # 条件判断
|
||||
IF_ELSE = "if_else" # 条件分支
|
||||
FOR_EACH = "for_each" # 循环遍历
|
||||
WHILE = "while" # 条件循环
|
||||
|
||||
# 流程控制组件详细配置
|
||||
@classmethod
|
||||
def get_components(cls) -> List[Dict[str, Any]]:
|
||||
"""获取流程控制组件列表"""
|
||||
return [
|
||||
{
|
||||
"type": "flow",
|
||||
"sub_type": cls.IF,
|
||||
"name": "条件判断",
|
||||
"description": "根据条件执行不同的操作",
|
||||
"icon": "branch",
|
||||
"params": [
|
||||
{
|
||||
"name": "condition",
|
||||
"label": "条件表达式",
|
||||
"type": "expression",
|
||||
"required": True,
|
||||
"description": "条件判断表达式"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "flow",
|
||||
"sub_type": cls.IF_ELSE,
|
||||
"name": "条件分支",
|
||||
"description": "根据条件执行不同的分支",
|
||||
"icon": "branch-multiple",
|
||||
"params": [
|
||||
{
|
||||
"name": "condition",
|
||||
"label": "条件表达式",
|
||||
"type": "expression",
|
||||
"required": True,
|
||||
"description": "条件判断表达式"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "flow",
|
||||
"sub_type": cls.FOR_EACH,
|
||||
"name": "循环遍历",
|
||||
"description": "遍历数组或对象的每个元素",
|
||||
"icon": "loop",
|
||||
"params": [
|
||||
{
|
||||
"name": "collection",
|
||||
"label": "集合表达式",
|
||||
"type": "expression",
|
||||
"required": True,
|
||||
"description": "要遍历的数组或对象"
|
||||
},
|
||||
{
|
||||
"name": "item_name",
|
||||
"label": "元素变量名",
|
||||
"type": "string",
|
||||
"required": True,
|
||||
"description": "当前元素的变量名"
|
||||
},
|
||||
{
|
||||
"name": "index_name",
|
||||
"label": "索引变量名",
|
||||
"type": "string",
|
||||
"required": False,
|
||||
"description": "当前索引的变量名"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "flow",
|
||||
"sub_type": cls.WHILE,
|
||||
"name": "条件循环",
|
||||
"description": "当条件为真时重复执行",
|
||||
"icon": "loop-circular",
|
||||
"params": [
|
||||
{
|
||||
"name": "condition",
|
||||
"label": "条件表达式",
|
||||
"type": "expression",
|
||||
"required": True,
|
||||
"description": "循环条件表达式"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
class SubtaskComponentConfig:
|
||||
"""子任务组件配置"""
|
||||
|
||||
# 子任务组件类型
|
||||
SUBTASK = "subtask" # 子任务
|
||||
|
||||
# 子任务组件详细配置
|
||||
@classmethod
|
||||
def get_components(cls) -> List[Dict[str, Any]]:
|
||||
"""获取子任务组件列表"""
|
||||
return [
|
||||
{
|
||||
"type": "subtask",
|
||||
"sub_type": cls.SUBTASK,
|
||||
"name": "子任务",
|
||||
"description": "执行已定义的任务作为子任务",
|
||||
"icon": "subtask",
|
||||
"params": [
|
||||
{
|
||||
"name": "task_id",
|
||||
"label": "选择子任务",
|
||||
"type": "select",
|
||||
"required": True,
|
||||
"description": "选择要执行的子任务(从已创建的任务中选择)",
|
||||
"data_source": "available_subtasks", # 指示前端从API返回的available_subtasks字段获取数据
|
||||
"display_field": "name", # 显示任务名称
|
||||
"value_field": "task_id" # 使用任务ID作为值
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"label": "任务参数",
|
||||
"type": "object",
|
||||
"required": False,
|
||||
"description": "传递给子任务的参数"
|
||||
},
|
||||
{
|
||||
"name": "wait_complete",
|
||||
"label": "等待完成",
|
||||
"type": "boolean",
|
||||
"required": False,
|
||||
"default": True,
|
||||
"description": "是否等待子任务完成后再继续执行"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
# 组件配置管理类
|
||||
class ComponentDetailConfig:
|
||||
"""组件详细配置管理"""
|
||||
|
||||
# 组件类型中文名称映射
|
||||
@classmethod
|
||||
def get_component_type_names(cls) -> Dict[str, str]:
|
||||
"""获取组件类型的中文名称映射"""
|
||||
return {
|
||||
# 基础类型
|
||||
"script": "脚本",
|
||||
"http": "HTTP请求",
|
||||
"flow": "流程",
|
||||
"robot": "机器人调度",
|
||||
"site": "库位",
|
||||
"device": "设备",
|
||||
"subtask": "子任务",
|
||||
"task": "任务",
|
||||
"basic": "基础",
|
||||
|
||||
# 脚本组件
|
||||
"run_script": "运行脚本",
|
||||
"set_task_variables": "设置任务变量",
|
||||
"runscript": "运行脚本",
|
||||
"settaskvariables": "设置任务变量",
|
||||
|
||||
# HTTP请求组件
|
||||
"http_get_request": "GET请求",
|
||||
"http_post_request": "POST请求",
|
||||
"httpgetrequest": "GET请求",
|
||||
"httppostrequest": "POST请求",
|
||||
|
||||
# 流程控制组件
|
||||
"if": "条件判断",
|
||||
"if_else": "条件分支",
|
||||
"if_else_if": "多条件分支",
|
||||
"for_each": "循环遍历",
|
||||
"while": "条件循环",
|
||||
"break": "跳出循环",
|
||||
"return": "返回",
|
||||
"delay": "延时",
|
||||
"parallel_execute": "并行执行",
|
||||
"serial_execute": "串行执行",
|
||||
"throw_exception": "抛出异常",
|
||||
"foreach": "循环遍历",
|
||||
"ifelse": "条件分支",
|
||||
"ifelseif": "多条件分支",
|
||||
"parallelexecute": "并行执行",
|
||||
"serialexecute": "串行执行",
|
||||
"throwexception": "抛出异常",
|
||||
|
||||
# 机器人调度组件
|
||||
"select_robot": "选择机器人",
|
||||
"get_robot_position": "获取机器人位置",
|
||||
"robot_action": "机器人动作",
|
||||
"change_robot_destination": "更改机器人目的地",
|
||||
"get_robot_battery": "获取机器人电量",
|
||||
"get_robot_pgv_code": "获取机器人PGV码",
|
||||
"changerobotdestination": "更改机器人目的地",
|
||||
"getrobotbattery": "获取机器人电量",
|
||||
"getrobotpgvcode": "获取机器人PGV码",
|
||||
"getrobotposition": "获取机器人位置",
|
||||
"robotaction": "机器人动作",
|
||||
"selectrobot": "选择机器人",
|
||||
|
||||
# 库位组件
|
||||
"batch_set_site": "批量设置库位",
|
||||
"get_dense_site": "获取密集库位",
|
||||
"query_site": "查询库位",
|
||||
"lock_site": "锁定库位",
|
||||
"unlock_site": "解锁库位",
|
||||
"get_locked_sites_by_task": "获取任务锁定的库位",
|
||||
"get_site_extension_property": "获取库位扩展属性",
|
||||
"set_site_extension_property": "设置库位扩展属性",
|
||||
"set_site_goods": "设置库位货物",
|
||||
"set_site_empty": "设置库位为空",
|
||||
"set_site_occupied": "设置库位为占用",
|
||||
"set_site_tag": "设置库位标签",
|
||||
"batchsetsite": "批量设置库位",
|
||||
"getdensesite": "获取密集库位",
|
||||
"getlockedsitesbytask": "获取任务锁定的库位",
|
||||
"getsiteextensionproperty": "获取库位扩展属性",
|
||||
"locksite": "锁定库位",
|
||||
"querysite": "查询库位",
|
||||
"setsiteempty": "设置库位为空",
|
||||
"setsiteextensionproperty": "设置库位扩展属性",
|
||||
"setsitegoods": "设置库位货物",
|
||||
"setsiteoccupied": "设置库位为占用",
|
||||
"setsitetag": "设置库位标签",
|
||||
"unlocksite": "解锁库位",
|
||||
|
||||
# 任务组件
|
||||
"cache_data": "缓存数据",
|
||||
"clear_cache_data": "清除缓存数据",
|
||||
"get_cache_data": "获取缓存数据",
|
||||
"set_task_status": "设置任务状态",
|
||||
"jump_to_block": "跳转到块",
|
||||
"get_task_input_param": "获取任务输入参数",
|
||||
"cachedata": "缓存数据",
|
||||
"clearcachedata": "清除缓存数据",
|
||||
"getcachedata": "获取缓存数据",
|
||||
"gettaskinputparam": "获取任务输入参数",
|
||||
"jumptoblock": "跳转到块",
|
||||
"settaskstatus": "设置任务状态",
|
||||
|
||||
# 基础组件
|
||||
"check_task_instance_id_exists": "检查任务实例ID是否存在",
|
||||
"create_unique_id": "创建唯一ID",
|
||||
"current_timestamp": "当前时间戳",
|
||||
"current_time": "当前时间",
|
||||
"execute_sql": "执行SQL",
|
||||
"query_sql": "查询SQL",
|
||||
"string_md5_encrypt": "字符串MD5加密",
|
||||
"string_to_json_array": "字符串转JSON数组",
|
||||
"string_to_json_object": "字符串转JSON对象",
|
||||
"print": "打印",
|
||||
"checktaskinstanceidexists": "检查任务实例ID是否存在",
|
||||
"createuniqueid": "创建唯一ID",
|
||||
"currenttime": "当前时间",
|
||||
"currenttimestamp": "当前时间戳",
|
||||
"executesql": "执行SQL",
|
||||
"querysql": "查询SQL",
|
||||
"stringmd5encrypt": "字符串MD5加密",
|
||||
"stringtojsonarray": "字符串转JSON数组",
|
||||
"stringtojsonobject": "字符串转JSON对象",
|
||||
|
||||
# 设备组件
|
||||
"wait_modbus_value": "等待Modbus值",
|
||||
"write_modbus_value": "写入Modbus值",
|
||||
"waitmodbusvalue": "等待Modbus值",
|
||||
"writemodbusvalue": "写入Modbus值"
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def get_all_components(cls) -> List[Dict[str, Any]]:
|
||||
"""获取所有组件详细配置"""
|
||||
all_components = []
|
||||
|
||||
# 添加子任务组件(放在第一位)
|
||||
all_components.extend(SubtaskComponentConfig.get_components())
|
||||
|
||||
# 添加脚本组件
|
||||
all_components.extend(ScriptComponentConfig.get_components())
|
||||
|
||||
# 添加HTTP请求组件
|
||||
all_components.extend(HttpComponentConfig.get_components())
|
||||
|
||||
# 添加流程控制组件
|
||||
all_components.extend(FlowComponentConfig.get_components())
|
||||
|
||||
# 可以继续添加其他类型的组件...
|
||||
|
||||
return all_components
|
||||
|
||||
@classmethod
|
||||
def get_components_by_type(cls, component_type: str) -> List[Dict[str, Any]]:
|
||||
"""根据组件类型获取组件列表"""
|
||||
all_components = cls.get_all_components()
|
||||
return [comp for comp in all_components if comp["type"] == component_type]
|
23
config/component_registry.py
Normal file
23
config/component_registry.py
Normal file
@ -0,0 +1,23 @@
|
||||
"""
|
||||
组件注册表
|
||||
"""
|
||||
# config/component_registry.py
|
||||
from core.component import ComponentFactory
|
||||
import importlib
|
||||
from config.settings import COMPONENT_PACKAGES
|
||||
from utils.logger import get_logger
|
||||
|
||||
# 获取日志记录器
|
||||
logger = get_logger(__name__)
|
||||
|
||||
def register_all_components():
|
||||
"""注册所有组件"""
|
||||
|
||||
for package_name in COMPONENT_PACKAGES:
|
||||
try:
|
||||
ComponentFactory.auto_discover(package_name)
|
||||
logger.info(f"自动注册组件包: {package_name}")
|
||||
except ImportError:
|
||||
logger.error(f"导入组件包失败: {package_name}")
|
||||
except Exception as e:
|
||||
logger.error(f"注册组件包失败: {package_name}, 错误: {str(e)}")
|
347
config/database.py
Normal file
347
config/database.py
Normal file
@ -0,0 +1,347 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
数据库连接配置模块
|
||||
包含数据库连接参数和SQLAlchemy配置,以及Redis缓存配置
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import sessionmaker, scoped_session
|
||||
|
||||
class ConfigDict:
|
||||
"""配置字典类,支持通过点号访问配置项"""
|
||||
def __init__(self, **kwargs):
|
||||
for key, value in kwargs.items():
|
||||
if isinstance(value, dict):
|
||||
setattr(self, key, ConfigDict(**value))
|
||||
else:
|
||||
setattr(self, key, value)
|
||||
|
||||
def get(self, key, default=None):
|
||||
return getattr(self, key, default)
|
||||
|
||||
def to_dict(self):
|
||||
result = {}
|
||||
for key, value in self.__dict__.items():
|
||||
if isinstance(value, ConfigDict):
|
||||
result[key] = value.to_dict()
|
||||
else:
|
||||
result[key] = value
|
||||
return result
|
||||
|
||||
# 数据库连接配置
|
||||
DB_CONFIG = ConfigDict(
|
||||
default=dict(
|
||||
dialect='mysql',
|
||||
driver='pymysql',
|
||||
username='root',
|
||||
password='root',
|
||||
host='localhost',
|
||||
port=3306,
|
||||
database='tianfeng_task',
|
||||
charset='utf8mb4'
|
||||
),
|
||||
test=dict(
|
||||
dialect='sqlite',
|
||||
database=':memory:'
|
||||
)
|
||||
)
|
||||
|
||||
# Redis缓存配置
|
||||
REDIS_CONFIG = ConfigDict(
|
||||
default=dict(
|
||||
host='localhost',
|
||||
port=6379,
|
||||
db=0,
|
||||
password=None,
|
||||
prefix='tianfeng:',
|
||||
socket_timeout=5,
|
||||
socket_connect_timeout=5,
|
||||
decode_responses=True
|
||||
),
|
||||
test=dict(
|
||||
host='localhost',
|
||||
port=6379,
|
||||
db=1,
|
||||
password=None,
|
||||
prefix='tianfeng_test:',
|
||||
decode_responses=True
|
||||
)
|
||||
)
|
||||
|
||||
# 当前环境,可通过环境变量设置
|
||||
ENV = os.environ.get('TIANFENG_ENV', 'default')
|
||||
|
||||
# 根据环境获取数据库配置
|
||||
db_conf = getattr(DB_CONFIG, ENV)
|
||||
|
||||
# 构建数据库连接URL
|
||||
if db_conf.dialect == 'sqlite':
|
||||
DATABASE_URL = f"{db_conf.dialect}:///{db_conf.database}"
|
||||
else:
|
||||
DATABASE_URL = (
|
||||
f"{db_conf.dialect}+{db_conf.driver}://"
|
||||
f"{db_conf.username}:{db_conf.password}@"
|
||||
f"{db_conf.host}:{db_conf.port}/{db_conf.database}?"
|
||||
f"charset={db_conf.charset}"
|
||||
)
|
||||
|
||||
# 创建数据库引擎
|
||||
engine = create_engine(
|
||||
DATABASE_URL,
|
||||
pool_size=20,
|
||||
max_overflow=0,
|
||||
pool_recycle=3600,
|
||||
pool_pre_ping=True,
|
||||
echo=False # 设置为True可以显示SQL语句,用于调试
|
||||
)
|
||||
|
||||
# 创建会话工厂
|
||||
SessionFactory = sessionmaker(bind=engine)
|
||||
|
||||
# 创建线程安全的会话
|
||||
db_session = scoped_session(SessionFactory)
|
||||
|
||||
# 创建基类
|
||||
Base = declarative_base()
|
||||
Base.query = db_session.query_property()
|
||||
|
||||
# 数据库配置类
|
||||
class DBConfig:
|
||||
"""数据库配置类,提供数据库相关的配置和方法"""
|
||||
config = DB_CONFIG
|
||||
env = ENV
|
||||
url = DATABASE_URL
|
||||
engine = engine
|
||||
session = db_session
|
||||
base = Base
|
||||
|
||||
@classmethod
|
||||
def get_config(cls):
|
||||
"""获取当前环境的数据库配置"""
|
||||
return getattr(cls.config, cls.env)
|
||||
|
||||
@classmethod
|
||||
def get_session(cls):
|
||||
"""获取数据库会话"""
|
||||
return cls.session
|
||||
|
||||
@classmethod
|
||||
def init_db(cls):
|
||||
"""
|
||||
初始化数据库
|
||||
创建所有表
|
||||
"""
|
||||
# 导入所有模型,确保它们已注册到Base
|
||||
import data.models
|
||||
|
||||
# 首先尝试创建数据库(如果不存在)
|
||||
if cls.get_config().dialect != 'sqlite':
|
||||
from sqlalchemy import text
|
||||
# 创建一个不指定数据库的连接
|
||||
db_conf = cls.get_config()
|
||||
temp_url = (
|
||||
f"{db_conf.dialect}+{db_conf.driver}://"
|
||||
f"{db_conf.username}:{db_conf.password}@"
|
||||
f"{db_conf.host}:{db_conf.port}/"
|
||||
f"?charset={db_conf.charset}"
|
||||
)
|
||||
temp_engine = create_engine(temp_url)
|
||||
with temp_engine.connect() as conn:
|
||||
conn.execute(text(f"CREATE DATABASE IF NOT EXISTS {db_conf.database} CHARACTER SET {db_conf.charset} COLLATE {db_conf.charset}_unicode_ci;"))
|
||||
conn.commit()
|
||||
temp_engine.dispose()
|
||||
|
||||
# 创建所有表
|
||||
cls.base.metadata.create_all(bind=cls.engine)
|
||||
|
||||
@classmethod
|
||||
def shutdown_session(cls, exception=None):
|
||||
"""
|
||||
关闭会话
|
||||
在应用程序关闭时调用
|
||||
"""
|
||||
cls.session.remove()
|
||||
|
||||
# 缓存配置类
|
||||
class CacheConfig:
|
||||
"""缓存配置类,提供Redis缓存相关的配置和方法"""
|
||||
config = REDIS_CONFIG
|
||||
env = ENV
|
||||
_redis_client = None
|
||||
|
||||
@classmethod
|
||||
def get_config(cls):
|
||||
"""获取当前环境的Redis配置"""
|
||||
return getattr(cls.config, cls.env)
|
||||
|
||||
@classmethod
|
||||
def get_redis_client(cls):
|
||||
"""获取Redis客户端实例"""
|
||||
if cls._redis_client is None:
|
||||
try:
|
||||
import redis
|
||||
redis_conf = cls.get_config()
|
||||
cls._redis_client = redis.Redis(
|
||||
host=redis_conf.host,
|
||||
port=redis_conf.port,
|
||||
db=redis_conf.db,
|
||||
password=redis_conf.password,
|
||||
socket_timeout=getattr(redis_conf, 'socket_timeout', 5),
|
||||
socket_connect_timeout=getattr(redis_conf, 'socket_connect_timeout', 5),
|
||||
decode_responses=getattr(redis_conf, 'decode_responses', True)
|
||||
)
|
||||
except ImportError:
|
||||
raise ImportError("Redis package is not installed. Please install it with 'pip install redis'")
|
||||
except Exception as e:
|
||||
print(f"Error connecting to Redis: {e}")
|
||||
return None
|
||||
return cls._redis_client
|
||||
|
||||
@classmethod
|
||||
def get_key(cls, key):
|
||||
"""获取带前缀的缓存键"""
|
||||
prefix = getattr(cls.get_config(), 'prefix', 'tianfeng:')
|
||||
return f"{prefix}{key}"
|
||||
|
||||
@classmethod
|
||||
def set(cls, key, value, expire=None):
|
||||
"""
|
||||
设置缓存
|
||||
|
||||
Args:
|
||||
key (str): 缓存键
|
||||
value (any): 缓存值,非字符串类型会被JSON序列化
|
||||
expire (int, optional): 过期时间(秒)
|
||||
|
||||
Returns:
|
||||
bool: 是否设置成功
|
||||
"""
|
||||
redis_client = cls.get_redis_client()
|
||||
if not redis_client:
|
||||
return False
|
||||
|
||||
if not isinstance(value, (str, int, float, bool)):
|
||||
value = json.dumps(value)
|
||||
|
||||
full_key = cls.get_key(key)
|
||||
if expire:
|
||||
return redis_client.setex(full_key, expire, value)
|
||||
else:
|
||||
return redis_client.set(full_key, value)
|
||||
|
||||
@classmethod
|
||||
def get(cls, key, default=None):
|
||||
"""
|
||||
获取缓存
|
||||
|
||||
Args:
|
||||
key (str): 缓存键
|
||||
default (any, optional): 默认值
|
||||
|
||||
Returns:
|
||||
any: 缓存值或默认值
|
||||
"""
|
||||
redis_client = cls.get_redis_client()
|
||||
if not redis_client:
|
||||
return default
|
||||
|
||||
full_key = cls.get_key(key)
|
||||
value = redis_client.get(full_key)
|
||||
|
||||
if value is None:
|
||||
return default
|
||||
|
||||
# 尝试解析JSON
|
||||
try:
|
||||
if value.startswith('{') or value.startswith('['):
|
||||
return json.loads(value)
|
||||
except (json.JSONDecodeError, AttributeError):
|
||||
pass
|
||||
|
||||
return value
|
||||
|
||||
@classmethod
|
||||
def delete(cls, key):
|
||||
"""
|
||||
删除缓存
|
||||
|
||||
Args:
|
||||
key (str): 缓存键
|
||||
|
||||
Returns:
|
||||
bool: 是否删除成功
|
||||
"""
|
||||
redis_client = cls.get_redis_client()
|
||||
if not redis_client:
|
||||
return False
|
||||
|
||||
full_key = cls.get_key(key)
|
||||
return redis_client.delete(full_key) > 0
|
||||
|
||||
@classmethod
|
||||
def exists(cls, key):
|
||||
"""
|
||||
检查缓存是否存在
|
||||
|
||||
Args:
|
||||
key (str): 缓存键
|
||||
|
||||
Returns:
|
||||
bool: 是否存在
|
||||
"""
|
||||
redis_client = cls.get_redis_client()
|
||||
if not redis_client:
|
||||
return False
|
||||
|
||||
full_key = cls.get_key(key)
|
||||
return redis_client.exists(full_key) > 0
|
||||
|
||||
@classmethod
|
||||
def ttl(cls, key):
|
||||
"""
|
||||
获取缓存剩余过期时间
|
||||
|
||||
Args:
|
||||
key (str): 缓存键
|
||||
|
||||
Returns:
|
||||
int: 剩余秒数,-1表示永不过期,-2表示不存在
|
||||
"""
|
||||
redis_client = cls.get_redis_client()
|
||||
if not redis_client:
|
||||
return -2
|
||||
|
||||
full_key = cls.get_key(key)
|
||||
return redis_client.ttl(full_key)
|
||||
|
||||
@classmethod
|
||||
def clear_all(cls):
|
||||
"""
|
||||
清除当前环境下的所有缓存
|
||||
|
||||
Returns:
|
||||
bool: 是否清除成功
|
||||
"""
|
||||
redis_client = cls.get_redis_client()
|
||||
if not redis_client:
|
||||
return False
|
||||
|
||||
prefix = getattr(cls.get_config(), 'prefix', 'tianfeng:')
|
||||
keys = redis_client.keys(f"{prefix}*")
|
||||
if keys:
|
||||
return redis_client.delete(*keys) > 0
|
||||
return True
|
||||
|
||||
# 兼容旧代码的函数
|
||||
def init_db():
|
||||
"""初始化数据库(兼容旧代码)"""
|
||||
DBConfig.init_db()
|
||||
|
||||
def shutdown_session(exception=None):
|
||||
"""关闭会话(兼容旧代码)"""
|
||||
DBConfig.shutdown_session(exception)
|
108
config/settings.py
Normal file
108
config/settings.py
Normal file
@ -0,0 +1,108 @@
|
||||
# config/settings.py
|
||||
"""
|
||||
应用全局配置文件
|
||||
包含应用基本信息、服务配置、数据库配置、日志配置等
|
||||
"""
|
||||
import os
|
||||
from dataclasses import dataclass
|
||||
from typing import List, Dict, Any
|
||||
|
||||
# 应用基本信息
|
||||
class AppConfig:
|
||||
"""应用基本配置"""
|
||||
NAME = "天风任务模块" # 应用名称
|
||||
VERSION = "1.0.0" # 应用版本号
|
||||
DEBUG = os.environ.get("DEBUG", "True").lower() == "true" # 调试模式开关,可通过环境变量设置
|
||||
|
||||
# 服务配置
|
||||
class ServerConfig:
|
||||
"""服务器配置"""
|
||||
HOST = os.environ.get("HOST", "0.0.0.0") # 服务监听地址
|
||||
PORT = int(os.environ.get("PORT", 8000)) # 服务监听端口
|
||||
RELOAD = AppConfig.DEBUG # 是否启用热重载,默认与DEBUG模式一致
|
||||
|
||||
# API配置
|
||||
class ApiConfig:
|
||||
"""API配置"""
|
||||
PREFIX = "/api/v1"
|
||||
TITLE = "天风任务模块API" # API文档标题
|
||||
DESCRIPTION = "AMR调度系统中天风任务模块的API接口" # API文档描述
|
||||
VERSION = AppConfig.VERSION # API版本号
|
||||
|
||||
# CORS配置(跨域资源共享)
|
||||
class CorsConfig:
|
||||
"""CORS配置"""
|
||||
ALLOW_ORIGINS = ["*"] # 允许的源,* 表示允许所有源
|
||||
ALLOW_CREDENTIALS = True # 是否允许携带凭证
|
||||
ALLOW_METHODS = ["*"] # 允许的HTTP方法
|
||||
ALLOW_HEADERS = ["*"] # 允许的HTTP头
|
||||
|
||||
# 数据库配置
|
||||
class DatabaseConfig:
|
||||
"""数据库配置"""
|
||||
HOST = os.environ.get("DB_HOST", "localhost") # 数据库主机
|
||||
PORT = int(os.environ.get("DB_PORT", 3306)) # 数据库端口
|
||||
USER = os.environ.get("DB_USER", "root") # 数据库用户名
|
||||
PASSWORD = os.environ.get("DB_PASSWORD", "password") # 数据库密码
|
||||
DATABASE = os.environ.get("DB_NAME", "tianfeng_task") # 数据库名
|
||||
CHARSET = "utf8mb4" # 字符集
|
||||
|
||||
@classmethod
|
||||
def as_dict(cls) -> Dict[str, Any]:
|
||||
"""返回数据库配置字典"""
|
||||
return {
|
||||
"host": cls.HOST,
|
||||
"port": cls.PORT,
|
||||
"user": cls.USER,
|
||||
"password": cls.PASSWORD,
|
||||
"database": cls.DATABASE,
|
||||
"charset": cls.CHARSET
|
||||
}
|
||||
|
||||
# 日志配置
|
||||
class LogConfig:
|
||||
"""日志配置"""
|
||||
LEVEL = os.environ.get("LOG_LEVEL", "INFO") # 日志级别
|
||||
FORMAT = "%(asctime)s - %(name)s - %(levelname)s - %(message)s" # 日志格式
|
||||
FILE = os.environ.get("LOG_FILE", "logs/tianfeng_task.log") # 日志文件路径
|
||||
|
||||
@classmethod
|
||||
def as_dict(cls) -> Dict[str, Any]:
|
||||
"""返回日志配置字典"""
|
||||
return {
|
||||
"level": cls.LEVEL,
|
||||
"format": cls.FORMAT,
|
||||
"file": cls.FILE
|
||||
}
|
||||
|
||||
# 组件配置
|
||||
class ComponentConfig:
|
||||
"""组件配置"""
|
||||
PACKAGES = ["components"] # 组件包路径
|
||||
|
||||
# 响应码配置
|
||||
class ResponseCode:
|
||||
"""响应码配置"""
|
||||
SUCCESS = 200 # 成功
|
||||
BAD_REQUEST = 400 # 请求错误
|
||||
UNAUTHORIZED = 401 # 未授权
|
||||
FORBIDDEN = 403 # 禁止访问
|
||||
NOT_FOUND = 404 # 资源不存在
|
||||
SERVER_ERROR = 500 # 服务器错误
|
||||
|
||||
# 任务状态配置
|
||||
class TaskStatus:
|
||||
"""任务状态配置"""
|
||||
PENDING = "待执行" # 待执行
|
||||
RUNNING = "执行中" # 执行中
|
||||
COMPLETED = "已完成" # 已完成
|
||||
FAILED = "执行失败" # 执行失败
|
||||
CANCELED = "已取消" # 已取消
|
||||
PAUSED = "暂停中" # 暂停中
|
||||
WAITING = "等待中" # 等待中
|
||||
|
||||
# 导出日志配置字典,供其他模块使用
|
||||
LOG_CONFIG = LogConfig.as_dict()
|
||||
|
||||
# 导出组件包列表,供组件注册使用
|
||||
COMPONENT_PACKAGES = ComponentConfig.PACKAGES
|
290
config/task_config.py
Normal file
290
config/task_config.py
Normal file
@ -0,0 +1,290 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
任务配置模块
|
||||
包含任务类型、状态等配置信息
|
||||
"""
|
||||
|
||||
from enum import Enum
|
||||
|
||||
# 任务类型枚举
|
||||
class TaskType(str, Enum):
|
||||
NORMAL = "NORMAL"
|
||||
SCHEDULED = "SCHEDULED"
|
||||
|
||||
# 任务状态枚举
|
||||
class TaskStatus(str, Enum):
|
||||
PENDING = "PENDING"
|
||||
RUNNING = "RUNNING"
|
||||
COMPLETED = "COMPLETED"
|
||||
CANCELLED = "CANCELLED"
|
||||
FAILED = "FAILED"
|
||||
PAUSED = "PAUSED"
|
||||
WAITING = "WAITING"
|
||||
|
||||
# 任务类型配置
|
||||
class TaskTypeConfig:
|
||||
"""任务类型配置"""
|
||||
NORMAL = TaskType.NORMAL.value
|
||||
SCHEDULED = TaskType.SCHEDULED.value
|
||||
|
||||
# 任务类型详细信息
|
||||
DETAILS = {
|
||||
NORMAL: {
|
||||
"name": "普通任务",
|
||||
"description": "立即执行的一次性任务"
|
||||
},
|
||||
SCHEDULED: {
|
||||
"name": "定时任务",
|
||||
"description": "按照预设时间执行的任务"
|
||||
}
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def get_name(cls, task_type_key):
|
||||
"""
|
||||
获取任务类型的显示名称
|
||||
|
||||
Args:
|
||||
task_type_key (str): 任务类型的键名
|
||||
|
||||
Returns:
|
||||
str: 任务类型的显示名称
|
||||
"""
|
||||
return cls.DETAILS.get(task_type_key, {}).get("name", task_type_key)
|
||||
|
||||
@classmethod
|
||||
def get_description(cls, task_type_key):
|
||||
"""
|
||||
获取任务类型的描述
|
||||
|
||||
Args:
|
||||
task_type_key (str): 任务类型的键名
|
||||
|
||||
Returns:
|
||||
str: 任务类型的描述
|
||||
"""
|
||||
return cls.DETAILS.get(task_type_key, {}).get("description", "")
|
||||
|
||||
@classmethod
|
||||
def get_all(cls):
|
||||
"""
|
||||
获取所有任务类型
|
||||
|
||||
Returns:
|
||||
list: 任务类型列表,每个元素包含key、name和description
|
||||
"""
|
||||
task_types = []
|
||||
for key, config in cls.DETAILS.items():
|
||||
task_types.append({
|
||||
"key": key,
|
||||
"name": config["name"],
|
||||
"description": config["description"]
|
||||
})
|
||||
return task_types
|
||||
|
||||
|
||||
# 任务状态配置
|
||||
class TaskStatusConfig:
|
||||
"""任务状态配置"""
|
||||
PENDING = TaskStatus.PENDING.value
|
||||
RUNNING = TaskStatus.RUNNING.value
|
||||
COMPLETED = TaskStatus.COMPLETED.value
|
||||
CANCELLED = TaskStatus.CANCELLED.value
|
||||
FAILED = TaskStatus.FAILED.value
|
||||
PAUSED = TaskStatus.PAUSED.value
|
||||
WAITING = TaskStatus.WAITING.value
|
||||
|
||||
# 任务状态详细信息
|
||||
DETAILS = {
|
||||
PENDING: {
|
||||
"name": "待执行",
|
||||
"description": "任务已创建但尚未开始执行"
|
||||
},
|
||||
RUNNING: {
|
||||
"name": "执行中",
|
||||
"description": "任务正在执行"
|
||||
},
|
||||
COMPLETED: {
|
||||
"name": "已完成",
|
||||
"description": "任务已成功完成"
|
||||
},
|
||||
CANCELLED: {
|
||||
"name": "已取消",
|
||||
"description": "任务被用户取消"
|
||||
},
|
||||
FAILED: {
|
||||
"name": "执行失败",
|
||||
"description": "任务执行过程中出现错误"
|
||||
},
|
||||
PAUSED: {
|
||||
"name": "暂停中",
|
||||
"description": "任务被暂停"
|
||||
},
|
||||
WAITING: {
|
||||
"name": "等待中",
|
||||
"description": "任务正在等待某些条件满足"
|
||||
}
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def get_name(cls, task_status_key):
|
||||
"""
|
||||
获取任务状态的显示名称
|
||||
|
||||
Args:
|
||||
task_status_key (str): 任务状态的键名
|
||||
|
||||
Returns:
|
||||
str: 任务状态的显示名称
|
||||
"""
|
||||
return cls.DETAILS.get(task_status_key, {}).get("name", task_status_key)
|
||||
|
||||
@classmethod
|
||||
def get_description(cls, task_status_key):
|
||||
"""
|
||||
获取任务状态的描述
|
||||
|
||||
Args:
|
||||
task_status_key (str): 任务状态的键名
|
||||
|
||||
Returns:
|
||||
str: 任务状态的描述
|
||||
"""
|
||||
return cls.DETAILS.get(task_status_key, {}).get("description", "")
|
||||
|
||||
@classmethod
|
||||
def get_all(cls):
|
||||
"""
|
||||
获取所有任务状态
|
||||
|
||||
Returns:
|
||||
list: 任务状态列表,每个元素包含key、name和description
|
||||
"""
|
||||
task_statuses = []
|
||||
for key, config in cls.DETAILS.items():
|
||||
task_statuses.append({
|
||||
"key": key,
|
||||
"name": config["name"],
|
||||
"description": config["description"]
|
||||
})
|
||||
return task_statuses
|
||||
|
||||
|
||||
# 默认值配置
|
||||
class DefaultConfig:
|
||||
"""默认值配置"""
|
||||
TASK_DESCRIPTION = "" # 默认任务描述(备注)
|
||||
TEMPLATE_DESCRIPTION = "用户自有模板" # 默认模板描述
|
||||
|
||||
|
||||
# 为了向后兼容,保留原有的函数和变量
|
||||
TASK_TYPE_CONFIG = TaskTypeConfig.DETAILS
|
||||
TASK_STATUS_CONFIG = TaskStatusConfig.DETAILS
|
||||
|
||||
# 任务类型常量
|
||||
TASK_TYPE_NORMAL = TaskTypeConfig.NORMAL
|
||||
TASK_TYPE_SCHEDULED = TaskTypeConfig.SCHEDULED
|
||||
|
||||
# 默认值配置
|
||||
DEFAULT_TASK_DESCRIPTION = DefaultConfig.TASK_DESCRIPTION
|
||||
DEFAULT_TEMPLATE_DESCRIPTION = DefaultConfig.TEMPLATE_DESCRIPTION
|
||||
|
||||
# 获取任务类型名称
|
||||
def get_task_type_name(task_type_key):
|
||||
"""
|
||||
获取任务类型的显示名称
|
||||
|
||||
Args:
|
||||
task_type_key (str): 任务类型的键名
|
||||
|
||||
Returns:
|
||||
str: 任务类型的显示名称
|
||||
"""
|
||||
return TaskTypeConfig.get_name(task_type_key)
|
||||
|
||||
# 获取任务类型描述
|
||||
def get_task_type_description(task_type_key):
|
||||
"""
|
||||
获取任务类型的描述
|
||||
|
||||
Args:
|
||||
task_type_key (str): 任务类型的键名
|
||||
|
||||
Returns:
|
||||
str: 任务类型的描述
|
||||
"""
|
||||
return TaskTypeConfig.get_description(task_type_key)
|
||||
|
||||
# 获取任务状态名称
|
||||
def get_task_status_name(task_status_key):
|
||||
"""
|
||||
获取任务状态的显示名称
|
||||
|
||||
Args:
|
||||
task_status_key (str): 任务状态的键名
|
||||
|
||||
Returns:
|
||||
str: 任务状态的显示名称
|
||||
"""
|
||||
return TaskStatusConfig.get_name(task_status_key)
|
||||
|
||||
# 获取任务状态描述
|
||||
def get_task_status_description(task_status_key):
|
||||
"""
|
||||
获取任务状态的描述
|
||||
|
||||
Args:
|
||||
task_status_key (str): 任务状态的键名
|
||||
|
||||
Returns:
|
||||
str: 任务状态的描述
|
||||
"""
|
||||
return TaskStatusConfig.get_description(task_status_key)
|
||||
|
||||
# 获取所有任务类型
|
||||
def get_all_task_types():
|
||||
"""
|
||||
获取所有任务类型
|
||||
|
||||
Returns:
|
||||
list: 任务类型列表,每个元素包含key、name和description
|
||||
"""
|
||||
return TaskTypeConfig.get_all()
|
||||
|
||||
# 获取所有任务状态
|
||||
def get_all_task_statuses():
|
||||
"""
|
||||
获取所有任务状态
|
||||
|
||||
Returns:
|
||||
list: 任务状态列表,每个元素包含key、name和description
|
||||
"""
|
||||
return TaskStatusConfig.get_all()
|
||||
|
||||
# 获取任务类型枚举
|
||||
def get_task_type_enum(task_type_key):
|
||||
"""
|
||||
获取任务类型的枚举值
|
||||
|
||||
Args:
|
||||
task_type_key (str): 任务类型的键名
|
||||
|
||||
Returns:
|
||||
str: 任务类型的枚举值(小写)
|
||||
"""
|
||||
return task_type_key.lower()
|
||||
|
||||
# 获取任务状态枚举
|
||||
def get_task_status_enum(task_status_key):
|
||||
"""
|
||||
获取任务状态的枚举值
|
||||
|
||||
Args:
|
||||
task_status_key (str): 任务状态的键名
|
||||
|
||||
Returns:
|
||||
str: 任务状态的枚举值(小写)
|
||||
"""
|
||||
return task_status_key.lower()
|
0
core/__init__.py
Normal file
0
core/__init__.py
Normal file
BIN
core/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
core/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
core/__pycache__/component.cpython-312.pyc
Normal file
BIN
core/__pycache__/component.cpython-312.pyc
Normal file
Binary file not shown.
BIN
core/__pycache__/context.cpython-312.pyc
Normal file
BIN
core/__pycache__/context.cpython-312.pyc
Normal file
Binary file not shown.
BIN
core/__pycache__/exceptions.cpython-312.pyc
Normal file
BIN
core/__pycache__/exceptions.cpython-312.pyc
Normal file
Binary file not shown.
BIN
core/__pycache__/workflow.cpython-312.pyc
Normal file
BIN
core/__pycache__/workflow.cpython-312.pyc
Normal file
Binary file not shown.
115
core/component.py
Normal file
115
core/component.py
Normal file
@ -0,0 +1,115 @@
|
||||
# core/component.py
|
||||
"""
|
||||
组件基类和工厂
|
||||
"""
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Dict, Any, Type, Callable, List, Optional
|
||||
import importlib
|
||||
import inspect
|
||||
import pkgutil
|
||||
from .context import TaskContext
|
||||
from .exceptions import ComponentError, ParameterError
|
||||
from utils.logger import get_logger
|
||||
|
||||
# 获取日志记录器
|
||||
logger = get_logger(__name__)
|
||||
|
||||
class Component(ABC):
|
||||
"""组件基类"""
|
||||
|
||||
def __init__(self, block_id: str, params: Dict[str, Any]):
|
||||
self.block_id = block_id
|
||||
self.params = params
|
||||
self.context = TaskContext.get_instance()
|
||||
|
||||
@abstractmethod
|
||||
def execute(self) -> Dict[str, Any]:
|
||||
"""执行组件逻辑"""
|
||||
pass
|
||||
|
||||
def resolve_param(self, param_name: str, default=None) -> Any:
|
||||
"""解析参数值,处理变量引用"""
|
||||
if param_name not in self.params:
|
||||
return default
|
||||
|
||||
value = self.params[param_name]
|
||||
|
||||
# 处理变量引用 ${xxx}
|
||||
if isinstance(value, str) and value.startswith("${") and value.endswith("}"):
|
||||
var_path = value[2:-1].split(".")
|
||||
current = self.context.variables
|
||||
|
||||
for part in var_path:
|
||||
if isinstance(current, dict) and part in current:
|
||||
current = current[part]
|
||||
else:
|
||||
return default # 变量不存在,返回默认值
|
||||
|
||||
return current
|
||||
|
||||
# 处理块引用 blocks.bX.xxx
|
||||
if isinstance(value, str) and value.startswith("blocks."):
|
||||
parts = value.split(".")
|
||||
if len(parts) >= 3 and parts[0] == "blocks" and parts[1] in self.context.blocks:
|
||||
block_data = self.context.blocks[parts[1]]
|
||||
if len(parts) == 3 and parts[2] in block_data:
|
||||
return block_data[parts[2]]
|
||||
|
||||
return value
|
||||
|
||||
def store_result(self, result: Dict[str, Any]) -> None:
|
||||
"""存储执行结果"""
|
||||
self.context.set_block_result(self.block_id, result)
|
||||
|
||||
def validate_required_params(self, required_params: List[str]) -> None:
|
||||
"""验证必要参数"""
|
||||
for param in required_params:
|
||||
if param not in self.params or self.params[param] is None:
|
||||
raise ParameterError(f"缺少必要参数: {param}")
|
||||
|
||||
class ComponentFactory:
|
||||
"""组件工厂,负责创建组件实例"""
|
||||
|
||||
_components: Dict[str, Type[Component]] = {}
|
||||
|
||||
@classmethod
|
||||
def register(cls, component_type: str) -> Callable:
|
||||
"""注册组件类的装饰器"""
|
||||
def decorator(component_class: Type[Component]) -> Type[Component]:
|
||||
cls._components[component_type] = component_class
|
||||
logger.info(f"注册组件: {component_type} -> {component_class.__name__}")
|
||||
return component_class
|
||||
return decorator
|
||||
|
||||
@classmethod
|
||||
def create(cls, block_id: str, component_type: str, params: Dict[str, Any]) -> Component:
|
||||
"""创建组件实例"""
|
||||
if component_type not in cls._components:
|
||||
raise ComponentError(f"未知的组件类型: {component_type}")
|
||||
|
||||
component_class = cls._components[component_type]
|
||||
return component_class(block_id, params)
|
||||
|
||||
@classmethod
|
||||
def get_component_types(cls) -> List[str]:
|
||||
"""获取所有已注册的组件类型"""
|
||||
return list(cls._components.keys())
|
||||
|
||||
@classmethod
|
||||
def auto_discover(cls, package_name: str) -> None:
|
||||
"""自动发现并注册组件"""
|
||||
logger.info(f"自动发现组件: {package_name}")
|
||||
package = importlib.import_module(package_name)
|
||||
for _, module_name, is_pkg in pkgutil.iter_modules(package.__path__, package.__name__ + '.'):
|
||||
if not is_pkg:
|
||||
try:
|
||||
module = importlib.import_module(module_name)
|
||||
for name, obj in inspect.getmembers(module):
|
||||
if (inspect.isclass(obj) and issubclass(obj, Component) and
|
||||
obj != Component and not inspect.isabstract(obj)):
|
||||
# 从类名推断组件类型
|
||||
component_type = obj.__name__.replace('Component', '').lower()
|
||||
cls._components[component_type] = obj
|
||||
logger.info(f"自动注册组件: {component_type} -> {obj.__name__}")
|
||||
except Exception as e:
|
||||
logger.error(f"加载组件模块失败: {module_name}, 错误: {str(e)}")
|
94
core/context.py
Normal file
94
core/context.py
Normal file
@ -0,0 +1,94 @@
|
||||
# core/context.py
|
||||
"""
|
||||
任务上下文
|
||||
"""
|
||||
from typing import Dict, Any, Optional
|
||||
import threading
|
||||
import time
|
||||
|
||||
class TaskContext:
|
||||
"""
|
||||
任务上下文,存储任务变量和执行结果
|
||||
使用线程本地存储确保线程安全
|
||||
"""
|
||||
|
||||
_thread_local = threading.local()
|
||||
|
||||
@classmethod
|
||||
def get_instance(cls):
|
||||
"""获取当前线程的上下文实例"""
|
||||
if not hasattr(cls._thread_local, 'instance'):
|
||||
cls._thread_local.instance = TaskContext()
|
||||
return cls._thread_local.instance
|
||||
|
||||
def __init__(self):
|
||||
self.task_id: Optional[str] = None
|
||||
self.workflow_id: Optional[str] = None
|
||||
self.variables: Dict[str, Any] = {} # 任务变量
|
||||
self.blocks: Dict[str, Any] = {} # 块执行结果
|
||||
self.task_inputs: Dict[str, Any] = {} # 任务输入参数
|
||||
self.task_status: str = "pending" # 任务状态
|
||||
self.start_time: Optional[int] = None # 开始时间
|
||||
self.end_time: Optional[int] = None # 结束时间
|
||||
self.executing_robot: Optional[str] = None # 执行机器人
|
||||
|
||||
def reset(self):
|
||||
"""重置上下文"""
|
||||
self.__init__()
|
||||
|
||||
def initialize_task(self, task_id: str, workflow_id: str, inputs: Dict[str, Any] = None):
|
||||
"""初始化任务"""
|
||||
self.task_id = task_id
|
||||
self.workflow_id = workflow_id
|
||||
self.start_time = int(time.time() * 1000)
|
||||
self.task_status = "executing"
|
||||
|
||||
if inputs:
|
||||
self.task_inputs = inputs.copy()
|
||||
self.variables["taskInputs"] = self.task_inputs
|
||||
|
||||
def set_variable(self, name: str, value: Any):
|
||||
"""设置变量值"""
|
||||
self.variables[name] = value
|
||||
|
||||
def get_variable(self, name: str, default=None):
|
||||
"""获取变量值"""
|
||||
return self.variables.get(name, default)
|
||||
|
||||
def set_block_result(self, block_id: str, result: Dict[str, Any]):
|
||||
"""设置块执行结果"""
|
||||
self.blocks[block_id] = result
|
||||
|
||||
def get_block_result(self, block_id: str, default=None):
|
||||
"""获取块执行结果"""
|
||||
return self.blocks.get(block_id, default)
|
||||
|
||||
def set_task_status(self, status: str):
|
||||
"""设置任务状态"""
|
||||
self.task_status = status
|
||||
self.variables["taskStatus"] = status
|
||||
|
||||
def set_executing_robot(self, robot_id: str):
|
||||
"""设置执行机器人"""
|
||||
self.executing_robot = robot_id
|
||||
self.variables["executingRobot"] = robot_id
|
||||
|
||||
def complete_task(self, status: str = "completed"):
|
||||
"""完成任务"""
|
||||
self.task_status = status
|
||||
self.variables["taskStatus"] = status
|
||||
self.end_time = int(time.time() * 1000)
|
||||
|
||||
def to_dict(self):
|
||||
"""转换为字典"""
|
||||
return {
|
||||
"task_id": self.task_id,
|
||||
"workflow_id": self.workflow_id,
|
||||
"variables": self.variables,
|
||||
"blocks": self.blocks,
|
||||
"task_inputs": self.task_inputs,
|
||||
"task_status": self.task_status,
|
||||
"start_time": self.start_time,
|
||||
"end_time": self.end_time,
|
||||
"executing_robot": self.executing_robot
|
||||
}
|
31
core/exceptions.py
Normal file
31
core/exceptions.py
Normal file
@ -0,0 +1,31 @@
|
||||
# core/exceptions.py
|
||||
"""
|
||||
自定义异常
|
||||
"""
|
||||
class TianfengTaskError(Exception):
|
||||
"""天风任务模块基础异常"""
|
||||
pass
|
||||
|
||||
class ComponentError(TianfengTaskError):
|
||||
"""组件执行异常"""
|
||||
pass
|
||||
|
||||
class ParameterError(ComponentError):
|
||||
"""参数错误异常"""
|
||||
pass
|
||||
|
||||
class WorkflowError(TianfengTaskError):
|
||||
"""工作流执行异常"""
|
||||
pass
|
||||
|
||||
class DatabaseError(TianfengTaskError):
|
||||
"""数据库异常"""
|
||||
pass
|
||||
|
||||
class ValidationError(TianfengTaskError):
|
||||
"""验证异常"""
|
||||
pass
|
||||
|
||||
class ApiError(TianfengTaskError):
|
||||
"""API异常"""
|
||||
pass
|
0
core/intelligence/__init__.py
Normal file
0
core/intelligence/__init__.py
Normal file
3
core/intelligence/embedding.py
Normal file
3
core/intelligence/embedding.py
Normal file
@ -0,0 +1,3 @@
|
||||
"""
|
||||
向量化核心功能
|
||||
"""
|
3
core/intelligence/vector_store.py
Normal file
3
core/intelligence/vector_store.py
Normal file
@ -0,0 +1,3 @@
|
||||
"""
|
||||
向量存储核心功能
|
||||
"""
|
287
core/workflow.py
Normal file
287
core/workflow.py
Normal file
@ -0,0 +1,287 @@
|
||||
# core/workflow.py
|
||||
"""
|
||||
工作流定义和执行
|
||||
"""
|
||||
from typing import Dict, Any, List, Optional
|
||||
import time
|
||||
import json
|
||||
import uuid
|
||||
from .context import TaskContext
|
||||
from .component import ComponentFactory
|
||||
from .exceptions import WorkflowError, ComponentError
|
||||
from utils.logger import get_logger
|
||||
|
||||
# 获取日志记录器
|
||||
logger = get_logger(__name__)
|
||||
|
||||
class WorkflowDefinition:
|
||||
"""工作流定义"""
|
||||
|
||||
def __init__(self, workflow_id: Optional[str] = None, name: str = "", workflow_type: str = "normal"):
|
||||
self.workflow_id = workflow_id or f"wf_{uuid.uuid4().hex}"
|
||||
self.name = name
|
||||
self.workflow_type = workflow_type # normal或scheduled
|
||||
self.variables: Dict[str, Any] = {}
|
||||
self.blocks: List[Dict[str, Any]] = []
|
||||
self.schedule: Optional[Dict[str, Any]] = None # 定时任务的调度信息
|
||||
self.version: str = "1.0"
|
||||
self.description: str = ""
|
||||
self.created_at: int = int(time.time() * 1000)
|
||||
self.updated_at: int = int(time.time() * 1000)
|
||||
|
||||
def add_block(self, block: Dict[str, Any]) -> None:
|
||||
"""添加块"""
|
||||
self.blocks.append(block)
|
||||
|
||||
def set_variables(self, variables: Dict[str, Any]) -> None:
|
||||
"""设置变量"""
|
||||
self.variables = variables.copy()
|
||||
|
||||
def set_schedule(self, schedule: Dict[str, Any]) -> None:
|
||||
"""设置调度信息"""
|
||||
self.schedule = schedule
|
||||
self.workflow_type = "scheduled"
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""转换为字典"""
|
||||
return {
|
||||
"workflow_id": self.workflow_id,
|
||||
"name": self.name,
|
||||
"workflow_type": self.workflow_type,
|
||||
"variables": self.variables,
|
||||
"blocks": self.blocks,
|
||||
"schedule": self.schedule,
|
||||
"version": self.version,
|
||||
"description": self.description,
|
||||
"created_at": self.created_at,
|
||||
"updated_at": self.updated_at
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data: Dict[str, Any]) -> 'WorkflowDefinition':
|
||||
"""从字典创建工作流定义"""
|
||||
workflow = cls(
|
||||
workflow_id=data.get("workflow_id"),
|
||||
name=data.get("name", ""),
|
||||
workflow_type=data.get("workflow_type", "normal")
|
||||
)
|
||||
workflow.variables = data.get("variables", {})
|
||||
workflow.blocks = data.get("blocks", [])
|
||||
workflow.schedule = data.get("schedule")
|
||||
workflow.version = data.get("version", "1.0")
|
||||
workflow.description = data.get("description", "")
|
||||
workflow.created_at = data.get("created_at", int(time.time() * 1000))
|
||||
workflow.updated_at = data.get("updated_at", int(time.time() * 1000))
|
||||
return workflow
|
||||
|
||||
def to_json(self) -> str:
|
||||
"""转换为JSON字符串"""
|
||||
return json.dumps(self.to_dict())
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json_str: str) -> 'WorkflowDefinition':
|
||||
"""从JSON字符串创建工作流定义"""
|
||||
data = json.loads(json_str)
|
||||
return cls.from_dict(data)
|
||||
|
||||
class WorkflowExecutor:
|
||||
"""工作流执行器"""
|
||||
|
||||
def __init__(self):
|
||||
self.component_factory = ComponentFactory
|
||||
|
||||
def execute(self, workflow: WorkflowDefinition, task_inputs: Dict[str, Any] = None) -> Dict[str, Any]:
|
||||
"""执行工作流"""
|
||||
# 获取上下文
|
||||
context = TaskContext.get_instance()
|
||||
|
||||
# 重置上下文
|
||||
context.reset()
|
||||
|
||||
# 生成任务ID
|
||||
task_id = f"task_{uuid.uuid4().hex}"
|
||||
|
||||
# 初始化任务
|
||||
context.initialize_task(task_id, workflow.workflow_id, task_inputs)
|
||||
|
||||
# 设置任务变量
|
||||
context.variables.update(workflow.variables)
|
||||
|
||||
logger.info(f"开始执行工作流: {workflow.name} (ID: {workflow.workflow_id}), 任务ID: {task_id}")
|
||||
|
||||
try:
|
||||
# 执行组件
|
||||
self._execute_blocks(workflow.blocks)
|
||||
|
||||
# 完成任务
|
||||
context.complete_task("completed")
|
||||
|
||||
logger.info(f"工作流执行完成: {workflow.name} (ID: {workflow.workflow_id}), 任务ID: {task_id}")
|
||||
|
||||
# 返回执行结果
|
||||
return context.to_dict()
|
||||
|
||||
except Exception as e:
|
||||
# 设置任务状态为执行失败
|
||||
context.complete_task("failed")
|
||||
|
||||
error_msg = f"工作流执行失败: {str(e)}"
|
||||
logger.error(f"{error_msg}, 工作流: {workflow.name} (ID: {workflow.workflow_id}), 任务ID: {task_id}")
|
||||
|
||||
# 返回错误信息
|
||||
result = context.to_dict()
|
||||
result["error"] = error_msg
|
||||
return result
|
||||
|
||||
def _execute_blocks(self, blocks: List[Dict[str, Any]]) -> None:
|
||||
"""执行一系列组件"""
|
||||
context = TaskContext.get_instance()
|
||||
|
||||
for block in blocks:
|
||||
block_id = block["id"]
|
||||
block_type = block["type"]
|
||||
params = block.get("params", {})
|
||||
|
||||
logger.debug(f"执行组件: {block_id} ({block_type})")
|
||||
|
||||
# 特殊处理流程控制组件
|
||||
if block_type == "if_condition":
|
||||
self._execute_if_condition(block_id, params)
|
||||
elif block_type == "for_each":
|
||||
self._execute_for_each(block_id, params)
|
||||
elif block_type == "while_loop":
|
||||
self._execute_while_loop(block_id, params)
|
||||
else:
|
||||
# 创建并执行普通组件
|
||||
try:
|
||||
component = self.component_factory.create(block_id, block_type, params)
|
||||
result = component.execute()
|
||||
logger.debug(f"组件 {block_id} ({block_type}) 执行成功")
|
||||
except Exception as e:
|
||||
error_msg = f"组件 {block_id} ({block_type}) 执行失败: {str(e)}"
|
||||
logger.error(error_msg)
|
||||
context.set_block_result(block_id, {"error": error_msg})
|
||||
|
||||
# 检查是否需要中断执行
|
||||
if block.get("stop_on_error", True):
|
||||
raise ComponentError(error_msg)
|
||||
|
||||
def _execute_if_condition(self, block_id: str, params: Dict[str, Any]) -> None:
|
||||
"""执行条件组件"""
|
||||
context = TaskContext.get_instance()
|
||||
|
||||
try:
|
||||
# 计算条件表达式
|
||||
condition_expr = params.get("condition", "False")
|
||||
condition_value = eval(condition_expr, {"__builtins__": {}}, {
|
||||
"context": context,
|
||||
"blocks": context.blocks,
|
||||
"variables": context.variables
|
||||
})
|
||||
|
||||
# 存储条件结果
|
||||
context.set_block_result(block_id, {"condition": condition_value})
|
||||
|
||||
# 根据条件执行相应分支
|
||||
if condition_value and "true_blocks" in params:
|
||||
self._execute_blocks(params["true_blocks"])
|
||||
elif not condition_value and "false_blocks" in params:
|
||||
self._execute_blocks(params["false_blocks"])
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"条件组件 {block_id} 执行失败: {str(e)}"
|
||||
logger.error(error_msg)
|
||||
context.set_block_result(block_id, {"error": error_msg})
|
||||
raise ComponentError(error_msg)
|
||||
|
||||
def _execute_for_each(self, block_id: str, params: Dict[str, Any]) -> None:
|
||||
"""执行循环组件"""
|
||||
context = TaskContext.get_instance()
|
||||
|
||||
try:
|
||||
# 获取循环项
|
||||
items_expr = params.get("items", "[]")
|
||||
items = eval(items_expr, {"__builtins__": {}}, {
|
||||
"context": context,
|
||||
"blocks": context.blocks,
|
||||
"variables": context.variables
|
||||
})
|
||||
|
||||
results = []
|
||||
|
||||
# 遍历执行循环体
|
||||
for index, item in enumerate(items):
|
||||
# 设置循环变量
|
||||
context.set_variable("loop_item", item)
|
||||
context.set_variable("loop_index", index)
|
||||
|
||||
# 执行循环体
|
||||
if "loop_blocks" in params:
|
||||
self._execute_blocks(params["loop_blocks"])
|
||||
|
||||
# 收集结果
|
||||
if "result_var" in params:
|
||||
result_var = params["result_var"]
|
||||
if result_var in context.variables:
|
||||
results.append(context.get_variable(result_var))
|
||||
|
||||
# 存储循环结果
|
||||
context.set_block_result(block_id, {
|
||||
"count": len(items),
|
||||
"results": results
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"循环组件 {block_id} 执行失败: {str(e)}"
|
||||
logger.error(error_msg)
|
||||
context.set_block_result(block_id, {"error": error_msg})
|
||||
raise ComponentError(error_msg)
|
||||
|
||||
def _execute_while_loop(self, block_id: str, params: Dict[str, Any]) -> None:
|
||||
"""执行while循环组件"""
|
||||
context = TaskContext.get_instance()
|
||||
|
||||
try:
|
||||
max_iterations = params.get("max_iterations", 100)
|
||||
iteration = 0
|
||||
results = []
|
||||
|
||||
# 循环执行
|
||||
while iteration < max_iterations:
|
||||
# 计算条件表达式
|
||||
condition_expr = params.get("condition", "False")
|
||||
condition_value = eval(condition_expr, {"__builtins__": {}}, {
|
||||
"context": context,
|
||||
"blocks": context.blocks,
|
||||
"variables": context.variables
|
||||
})
|
||||
|
||||
if not condition_value:
|
||||
break
|
||||
|
||||
# 设置循环变量
|
||||
context.set_variable("loop_iteration", iteration)
|
||||
|
||||
# 执行循环体
|
||||
if "loop_blocks" in params:
|
||||
self._execute_blocks(params["loop_blocks"])
|
||||
|
||||
# 收集结果
|
||||
if "result_var" in params:
|
||||
result_var = params["result_var"]
|
||||
if result_var in context.variables:
|
||||
results.append(context.get_variable(result_var))
|
||||
|
||||
iteration += 1
|
||||
|
||||
# 存储循环结果
|
||||
context.set_block_result(block_id, {
|
||||
"iterations": iteration,
|
||||
"results": results
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"While循环组件 {block_id} 执行失败: {str(e)}"
|
||||
logger.error(error_msg)
|
||||
context.set_block_result(block_id, {"error": error_msg})
|
||||
raise ComponentError(error_msg)
|
11
data/__init__.py
Normal file
11
data/__init__.py
Normal file
@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
数据模块
|
||||
包含数据库模型和数据访问功能
|
||||
"""
|
||||
|
||||
from data.session import initialize_database, get_session, session_scope
|
||||
|
||||
__all__ = ['initialize_database', 'get_session', 'session_scope']
|
BIN
data/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
data/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
data/__pycache__/session.cpython-312.pyc
Normal file
BIN
data/__pycache__/session.cpython-312.pyc
Normal file
Binary file not shown.
0
data/migrations/__init__.py
Normal file
0
data/migrations/__init__.py
Normal file
0
data/migrations/migration_manager.py
Normal file
0
data/migrations/migration_manager.py
Normal file
0
data/migrations/versions.py
Normal file
0
data/migrations/versions.py
Normal file
42
data/models/__init__.py
Normal file
42
data/models/__init__.py
Normal file
@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
数据模型模块
|
||||
包含所有数据库模型的定义
|
||||
"""
|
||||
|
||||
from data.models.base import BaseModel
|
||||
from data.models.component import ComponentCategory, ComponentType, Component
|
||||
from data.models.task import Task
|
||||
from data.models.task_version import TaskVersion
|
||||
from data.models.task_record import TaskRecord
|
||||
from data.models.task_flow_block import TaskFlowBlock, BlockType
|
||||
from data.models.component_parameter import (
|
||||
ComponentParameterDefinition,
|
||||
ComponentParameterValue,
|
||||
ParameterType,
|
||||
ParameterValueFormat
|
||||
)
|
||||
from data.models.task_variable import TaskVariableDefinition
|
||||
from data.models.task_backup import TaskBackup
|
||||
from data.models.task_edit_history import TaskEditHistory
|
||||
|
||||
__all__ = [
|
||||
'BaseModel',
|
||||
'ComponentCategory',
|
||||
'ComponentType',
|
||||
'Component',
|
||||
'Task',
|
||||
'TaskVersion',
|
||||
'TaskRecord',
|
||||
'TaskFlowBlock',
|
||||
'BlockType',
|
||||
'ComponentParameterDefinition',
|
||||
'ComponentParameterValue',
|
||||
'ParameterType',
|
||||
'ParameterValueFormat',
|
||||
'TaskVariableDefinition',
|
||||
'TaskBackup',
|
||||
'TaskEditHistory'
|
||||
]
|
BIN
data/models/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
data/models/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
data/models/__pycache__/base.cpython-312.pyc
Normal file
BIN
data/models/__pycache__/base.cpython-312.pyc
Normal file
Binary file not shown.
BIN
data/models/__pycache__/component.cpython-312.pyc
Normal file
BIN
data/models/__pycache__/component.cpython-312.pyc
Normal file
Binary file not shown.
BIN
data/models/__pycache__/component_parameter.cpython-312.pyc
Normal file
BIN
data/models/__pycache__/component_parameter.cpython-312.pyc
Normal file
Binary file not shown.
BIN
data/models/__pycache__/task.cpython-312.pyc
Normal file
BIN
data/models/__pycache__/task.cpython-312.pyc
Normal file
Binary file not shown.
BIN
data/models/__pycache__/task_backup.cpython-312.pyc
Normal file
BIN
data/models/__pycache__/task_backup.cpython-312.pyc
Normal file
Binary file not shown.
BIN
data/models/__pycache__/task_edit_history.cpython-312.pyc
Normal file
BIN
data/models/__pycache__/task_edit_history.cpython-312.pyc
Normal file
Binary file not shown.
BIN
data/models/__pycache__/task_flow_block.cpython-312.pyc
Normal file
BIN
data/models/__pycache__/task_flow_block.cpython-312.pyc
Normal file
Binary file not shown.
BIN
data/models/__pycache__/task_record.cpython-312.pyc
Normal file
BIN
data/models/__pycache__/task_record.cpython-312.pyc
Normal file
Binary file not shown.
BIN
data/models/__pycache__/task_variable.cpython-312.pyc
Normal file
BIN
data/models/__pycache__/task_variable.cpython-312.pyc
Normal file
Binary file not shown.
BIN
data/models/__pycache__/task_version.cpython-312.pyc
Normal file
BIN
data/models/__pycache__/task_version.cpython-312.pyc
Normal file
Binary file not shown.
BIN
data/models/__pycache__/user_operation.cpython-312.pyc
Normal file
BIN
data/models/__pycache__/user_operation.cpython-312.pyc
Normal file
Binary file not shown.
127
data/models/base.py
Normal file
127
data/models/base.py
Normal file
@ -0,0 +1,127 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
基础模型模块
|
||||
包含所有模型共用的字段和方法
|
||||
"""
|
||||
|
||||
import datetime
|
||||
from sqlalchemy import Column, Integer, DateTime, Boolean
|
||||
from config.database import Base
|
||||
|
||||
class BaseModel(Base):
|
||||
"""
|
||||
基础模型类
|
||||
包含所有模型共用的字段和方法
|
||||
"""
|
||||
__abstract__ = True # 声明为抽象类,不会创建表
|
||||
|
||||
id = Column(Integer, primary_key=True, autoincrement=True, comment='主键ID')
|
||||
created_at = Column(DateTime, default=datetime.datetime.now, comment='创建时间')
|
||||
updated_at = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now, comment='更新时间')
|
||||
is_deleted = Column(Boolean, default=False, comment='是否删除(软删除标记)')
|
||||
|
||||
def to_dict(self):
|
||||
"""
|
||||
将模型转换为字典
|
||||
用于API响应
|
||||
"""
|
||||
result = {}
|
||||
for column in self.__table__.columns:
|
||||
value = getattr(self, column.name)
|
||||
# 处理日期时间类型
|
||||
if isinstance(value, datetime.datetime):
|
||||
value = value.strftime('%Y-%m-%d %H:%M:%S')
|
||||
result[column.name] = value
|
||||
return result
|
||||
|
||||
def to_json(self, fields=None, exclude=None, timestamp_format='ms'):
|
||||
"""
|
||||
将模型转换为JSON友好的字典
|
||||
|
||||
Args:
|
||||
fields (list, optional): 需要包含的字段列表,为None则包含所有字段
|
||||
exclude (list, optional): 需要排除的字段列表
|
||||
timestamp_format (str, optional): 时间戳格式,可选值:'ms'(毫秒时间戳), 'iso'(ISO格式), 'str'(字符串格式)
|
||||
|
||||
Returns:
|
||||
dict: 包含指定字段的字典
|
||||
"""
|
||||
result = {}
|
||||
|
||||
# 获取所有列名
|
||||
columns = [column.name for column in self.__table__.columns]
|
||||
|
||||
# 如果指定了fields,则只包含这些字段
|
||||
if fields:
|
||||
columns = [col for col in columns if col in fields]
|
||||
|
||||
# 排除指定的字段
|
||||
if exclude:
|
||||
columns = [col for col in columns if col not in exclude]
|
||||
|
||||
# 获取字段值
|
||||
for column in columns:
|
||||
value = getattr(self, column)
|
||||
|
||||
# 处理日期时间类型
|
||||
if isinstance(value, datetime.datetime):
|
||||
if timestamp_format == 'ms':
|
||||
# 转换为毫秒时间戳
|
||||
value = int(value.timestamp() * 1000)
|
||||
elif timestamp_format == 'iso':
|
||||
# 转换为ISO格式
|
||||
value = value.isoformat()
|
||||
else:
|
||||
# 转换为字符串格式
|
||||
value = value.strftime('%Y-%m-%d %H:%M:%S')
|
||||
|
||||
# 处理枚举类型
|
||||
elif hasattr(value, 'name') and hasattr(value, 'value'):
|
||||
# 如果是枚举类型,返回其值
|
||||
value = value.value
|
||||
|
||||
result[column] = value
|
||||
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def get_by_id(cls, id):
|
||||
"""
|
||||
根据ID获取记录
|
||||
"""
|
||||
return cls.query.filter(cls.id == id, cls.is_deleted == False).first()
|
||||
|
||||
@classmethod
|
||||
def get_all(cls, page=1, per_page=20):
|
||||
"""
|
||||
获取所有记录(分页)
|
||||
"""
|
||||
return cls.query.filter(cls.is_deleted == False).paginate(page=page, per_page=per_page)
|
||||
|
||||
def save(self):
|
||||
"""
|
||||
保存记录
|
||||
"""
|
||||
from config.database import db_session
|
||||
db_session.add(self)
|
||||
db_session.commit()
|
||||
return self
|
||||
|
||||
def delete(self):
|
||||
"""
|
||||
删除记录(软删除)
|
||||
"""
|
||||
self.is_deleted = True
|
||||
self.save()
|
||||
return self
|
||||
|
||||
def hard_delete(self):
|
||||
"""
|
||||
硬删除记录
|
||||
"""
|
||||
from config.database import db_session
|
||||
db_session.delete(self)
|
||||
db_session.commit()
|
||||
return self
|
135
data/models/component.py
Normal file
135
data/models/component.py
Normal file
@ -0,0 +1,135 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
组件模型模块
|
||||
包含组件、组件类型和组件分类相关的数据模型
|
||||
"""
|
||||
|
||||
import enum
|
||||
from sqlalchemy import Column, Integer, String, Text, Boolean, Enum, ForeignKey, JSON
|
||||
from sqlalchemy.orm import relationship
|
||||
from data.models.base import BaseModel
|
||||
|
||||
class ComponentCategoryEnum(enum.Enum):
|
||||
"""
|
||||
组件分类枚举
|
||||
"""
|
||||
SUBTASK = 'subtask' # 子任务
|
||||
SCRIPT = 'script' # 脚本
|
||||
HTTP = 'http' # HTTP请求
|
||||
TASK = 'task' # 任务
|
||||
FLOW = 'flow' # 流程
|
||||
BASIC = 'basic' # 基础
|
||||
STORAGE = 'storage' # 库位
|
||||
ROBOT = 'robot' # 机器人调度
|
||||
DEVICE = 'device' # 设备
|
||||
|
||||
class ComponentCategory(BaseModel):
|
||||
"""
|
||||
组件分类模型
|
||||
表示组件的分类
|
||||
"""
|
||||
__tablename__ = 'component_categories'
|
||||
|
||||
name = Column(String(50), nullable=False, comment='分类名称')
|
||||
code = Column(Enum(ComponentCategoryEnum), nullable=False, unique=True, comment='分类编码')
|
||||
description = Column(String(500), nullable=True, comment='分类描述')
|
||||
icon = Column(String(100), nullable=True, comment='分类图标')
|
||||
order = Column(Integer, default=0, comment='排序顺序')
|
||||
|
||||
# 关联关系
|
||||
types = relationship('ComponentType', back_populates='category', cascade='all, delete-orphan')
|
||||
|
||||
def __repr__(self):
|
||||
return f"<ComponentCategory(id={self.id}, name='{self.name}', code='{self.code}')>"
|
||||
|
||||
@classmethod
|
||||
def get_by_code(cls, code):
|
||||
"""
|
||||
根据编码获取分类
|
||||
"""
|
||||
return cls.query.filter(cls.code == code, cls.is_deleted == False).first()
|
||||
|
||||
class ComponentType(BaseModel):
|
||||
"""
|
||||
组件类型模型
|
||||
表示组件的类型
|
||||
"""
|
||||
__tablename__ = 'component_types'
|
||||
|
||||
category_id = Column(Integer, ForeignKey('component_categories.id'), nullable=False, comment='分类ID')
|
||||
name = Column(String(100), nullable=False, comment='类型名称')
|
||||
code = Column(String(50), nullable=False, unique=True, comment='类型编码')
|
||||
description = Column(String(500), nullable=True, comment='类型描述')
|
||||
icon = Column(String(100), nullable=True, comment='类型图标')
|
||||
order = Column(Integer, default=0, comment='排序顺序')
|
||||
is_system = Column(Boolean, default=False, comment='是否为系统组件')
|
||||
|
||||
# 关联关系
|
||||
category = relationship('ComponentCategory', back_populates='types')
|
||||
components = relationship('Component', back_populates='type', cascade='all, delete-orphan')
|
||||
parameter_definitions = relationship('ComponentParameterDefinition', back_populates='component_type', cascade='all, delete-orphan')
|
||||
|
||||
def __repr__(self):
|
||||
return f"<ComponentType(id={self.id}, name='{self.name}', code='{self.code}')>"
|
||||
|
||||
@classmethod
|
||||
def get_by_code(cls, code):
|
||||
"""
|
||||
根据编码获取类型
|
||||
"""
|
||||
return cls.query.filter(cls.code == code, cls.is_deleted == False).first()
|
||||
|
||||
@classmethod
|
||||
def get_by_category(cls, category_id):
|
||||
"""
|
||||
根据分类获取类型列表
|
||||
"""
|
||||
return cls.query.filter(cls.category_id == category_id, cls.is_deleted == False).order_by(cls.order).all()
|
||||
|
||||
class Component(BaseModel):
|
||||
"""
|
||||
组件模型
|
||||
表示一个具体的组件
|
||||
"""
|
||||
__tablename__ = 'components'
|
||||
|
||||
type_id = Column(Integer, ForeignKey('component_types.id'), nullable=False, comment='类型ID')
|
||||
name = Column(String(100), nullable=False, comment='组件名称')
|
||||
code = Column(String(50), nullable=False, unique=True, comment='组件编码')
|
||||
description = Column(String(500), nullable=True, comment='组件描述')
|
||||
icon = Column(String(100), nullable=True, comment='组件图标')
|
||||
version = Column(String(20), nullable=False, default='1.0.0', comment='组件版本')
|
||||
is_system = Column(Boolean, default=False, comment='是否为系统组件')
|
||||
is_enabled = Column(Boolean, default=True, comment='是否启用')
|
||||
config_schema = Column(JSON, nullable=True, comment='配置模式(JSON Schema)')
|
||||
default_config = Column(JSON, nullable=True, comment='默认配置')
|
||||
implementation = Column(Text, nullable=True, comment='组件实现代码')
|
||||
|
||||
# 关联关系
|
||||
type = relationship('ComponentType', back_populates='components')
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Component(id={self.id}, name='{self.name}', code='{self.code}')>"
|
||||
|
||||
@classmethod
|
||||
def get_by_code(cls, code):
|
||||
"""
|
||||
根据编码获取组件
|
||||
"""
|
||||
return cls.query.filter(cls.code == code, cls.is_deleted == False).first()
|
||||
|
||||
@classmethod
|
||||
def get_by_type(cls, type_id):
|
||||
"""
|
||||
根据类型获取组件列表
|
||||
"""
|
||||
return cls.query.filter(cls.type_id == type_id, cls.is_enabled == True, cls.is_deleted == False).all()
|
||||
|
||||
@classmethod
|
||||
def get_system_components(cls):
|
||||
"""
|
||||
获取所有系统组件
|
||||
"""
|
||||
return cls.query.filter(cls.is_system == True, cls.is_enabled == True, cls.is_deleted == False).all()
|
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