VWED_server/docs/CODING_STANDARDS.md

73 KiB
Raw Blame History

一、模块定位

为平台提供 可扩展的 Python 脚本执行引擎 的能力,支持:

  • 文件创建与管理:支持目录和脚本文件的创建、编辑、删除操作
  • 脚本运行控制:提供脚本启动、停止、重启等生命周期管理
  • 实时日志推送:通过 WebSocket 实时推送脚本执行日志和输出结果
  • 日志记录存储:将脚本执行过程、结果、异常等信息持久化存储
  • 接口集成功能:与 VWED 任务系统深度集成,支持任务流程中调用脚本
  • 自定义模块扩展:支持注册自定义接口、函数、事件监听器等
  • 函数注册调用:支持在 VWED 任务中通过脚本模块引用和运行注册的函数
  • 多脚本并发服务:每个脚本独立运行,支持多脚本同时提供服务
  • 企业级安全沙箱:提供安全的脚本执行环境和权限控制

二、基于现有架构的脚本引擎框架

VWED脚本引擎架构基于现有项目结构
├── 🌐 API接口层 (routes/)
│   ├── script_engine_api.py        # REST API接口
│   ├── script_websocket_api.py     # WebSocket API接口  
│   └── model/script_engine_model.py # 请求/响应模型
├── 🔧 业务服务层 (services/)
│   ├── script_engine_service.py      # 核心引擎服务
│   ├── script_registry_service.py    # 注册中心服务
│   ├── script_builtin_functions.py   # 内置函数服务
│   ├── script_security_sandbox.py    # 安全沙箱服务
│   └── script_websocket_logger.py    # WebSocket日志服务
├── 🔗 任务集成层 (services/execution/handlers/)
│   └── script_engine_integration.py  # 任务流程集成
├── 💾 数据模型层 (data/models/)
│   ├── script_project.py             # 脚本项目模型
│   ├── script_file.py                # 脚本文件模型
│   ├── script_api_registration.py    # API注册记录模型
│   ├── script_function_registration.py # 函数注册记录模型
│   ├── script_execution_log.py       # 执行日志模型
│   └── script_registry.py            # 脚本注册模型
├── ⚙️ 配置层 (config/)
│   └── components/script.json        # 脚本组件配置
└── 📊 任务流程集成
    ├── SCRIPT_ENGINE_EXECUTE执行脚本
    ├── SCRIPT_ENGINE_CALL_FUNCTION调用注册函数
    └── SCRIPT_ENGINE_HTTP_REQUESTHTTP请求组件

三、脚本引擎核心特性

特性名称 描述 技术实现
文件管理系统 支持脚本文件和目录的CRUD操作 基于文件系统的目录结构管理,支持多层级目录
脚本生命周期管理 脚本的启动、停止、重启等状态控制 ScriptEngine服务管理脚本进程生命周期
实时日志推送 WebSocket实时推送脚本执行日志和结果 ScriptWebSocketLogger服务实现实时通信
日志持久化存储 脚本执行过程和结果的数据库存储 执行日志保存到数据库,支持历史查询
任务系统集成 与VWED任务流程深度集成调用 script.py处理器支持任务中调用脚本函数
函数动态加载 动态加载并执行脚本中的指定函数 importlib.util实现模块动态加载和函数调用
参数传递机制 支持多种类型参数传递给脚本函数 支持dict、list、str等类型参数智能解析
异步执行支持 支持异步函数的执行和结果等待 inspect检测协程并使用asyncio.await处理
变量上下文管理 脚本执行结果保存到任务上下文 TaskContext管理任务变量支持跨组件传递
错误处理机制 完善的异常捕获和错误信息反馈 异常信息记录到日志,返回详细错误描述

📗需求文档PRD

一、目标用户

  • 实施工程师
  • 自动化工程师
  • 会 Python 的业务开发人员

二、基于现有架构的功能需求

编号 需求名称 描述 当前实现状态
BR01 脚本文件创建管理 支持脚本文件和目录的创建、编辑、删除 已实现 - 基于文件系统的CRUD操作
BR02 脚本执行控制 脚本的启动、停止、状态监控 已实现 - ScriptEngine生命周期管理
BR03 函数动态调用 动态加载脚本文件并执行指定函数 已实现 - importlib.util动态加载机制
BR04 参数传递支持 支持多种数据类型的参数传递 已实现 - dict/list/str参数智能解析
BR05 异步执行支持 支持异步函数的执行和结果等待 已实现 - inspect+asyncio异步处理
BR06 实时日志推送 WebSocket实时推送执行日志和结果 已实现 - WebSocket实时通信
BR07 日志持久化 执行过程和结果的数据库存储 已实现 - 执行日志数据库记录
BR08 任务系统集成 在VWED任务流程中调用脚本函数 已实现 - script.py处理器集成
BR09 变量上下文管理 脚本执行结果保存到任务变量 已实现 - TaskContext变量管理
BR10 错误处理机制 完善的异常捕获和错误信息反馈 已实现 - 多层异常处理和日志记录

三、非功能需求(不变)

类型 要求
性能 脚本加载 < 1s接口响应 < 200ms
安全 脚本沙箱运行,禁止访问系统文件、网络(除白名单)
可维护性 脚本统一存储支持版本控制Git
可扩展性 后续可支持 Python3 虚拟环境、pip 安装依赖
兼容性 支持 Chrome、Edge、Firefox

📙开发文档TDD

一、基于现有架构的技术选型

架构层级 技术选型 具体实现
API接口层 FastAPI + WebSocket routes/script_engine_api.py + routes/script_websocket_api.py
业务服务层 Service Pattern + 依赖注入 services/script_engine_service.py 等服务模块
数据模型层 SQLAlchemy ORM + Enum data/models/script_*.py + data/enum/
任务集成层 VWED任务执行系统 services/execution/handlers/script_engine_integration.py
安全防护层 Python沙箱 + 权限控制 services/script_security_sandbox.py
配置管理层 JSON配置 + 组件系统 config/components/script.json
日志监控层 WebSocket实时推送 + 结构化日志 services/script_websocket_logger.py
数据库层 MySQL/PostgreSQL + 连接池 data/session.py + migrations/

二、基于现有架构的脚本引擎设计

2.1 可扩展框架整体架构

┌───────────────────────────────────────────────────────────────────────────────────────────┐
│                       前端编辑界面 (Monaco Editor)                        │
│  ┌───────────────┬───────────────┬───────────────┬───────────────┐    │
│  │  脚本编辑器   │   实时日志     │   调试控台     │   性能监控     │    │
│  │- Python语法高亮│- WebSocket日志│- remote-pdb   │- CPU/内存      │    │
│  │- 代码智能补全  │- 等级过滤     │- 断点调试     │- 脚本执行状态  │    │
│  │- 语法检查&提示 │- 日志导出     │- 变量监控     │- 资源使用统计  │    │
│  └───────────────┴───────────────┴───────────────┴───────────────┘    │
└───────────────────────────────────────────────────────────────────────────────────────────┘
                                      │ HTTP/WebSocket API
                                      ▼
┌───────────────────────────────────────────────────────────────────────────────────────────┐
│           🔌 接口服务层 (scripts/interfaces/) - 可扩展协议支持           │
│  ┌───────────────┬───────────────┬───────────────┬───────────────┐  │
│  │ ScriptEngineAPI │ScriptWebSocketAPI│ScriptManagementAPI│   协议扩展   │  │
│  │- 脚本执行控制  │- 实时日志推送  │- 文件管理       │- gRPC/MQTT     │  │
│  │- 状态监控      │- 调试通信      │- 权限管理       │- TCP/UDP       │  │
│  │- API注册管理   │- 性能指标      │- 版本控制       │- 自定义协议   │  │
│  └───────────────┴───────────────┴───────────────┴───────────────┘  │
└───────────────────────────────────────────────────────────────────────────────────────────┘
                                      │ 服务调用
                                      ▼
┌───────────────────────────────────────────────────────────────────────────────────────────┐
│        🔧 业务服务层 (scripts/services/) - 服务注册与发现          │
│  ┌─────────────┬────────────┬────────────┬────────────┐  │
│  │ScriptEngine │ Registry    │BuiltinFunctions│WebSocketLogger│  │
│  │- 多脚本并发 │- 资源注册   │- VWED对象模块│- 日志监控    │  │
│  │- 生命周期管理│- script_id隔离│- 扩展模块加载│- 实时推送    │  │
│  │- 资源清理    │- 动态注销   │- 热更新支持  │- 日志聚合    │  │
│  └─────────────┴────────────┴────────────┴────────────┘  │
└───────────────────────────────────────────────────────────────────────────────────────────┘
                                      │ 模块加载
                                      ▼
┌───────────────────────────────────────────────────────────────────────────────────────────┐
│     🧩 模块扩展层 (scripts/modules/) - 每个模块独立可扩展      │
│  ┌──────────────────────────────────────────────────────────────────────────────┐  │
│  │                      内置模块生态 (VWED对象系统)                      │  │
│  │  ┌────────────┬───────────┬───────────┬────────────┐    │  │
│  │  │ Core模块   │ Task模块  │Device模块 │ Comm模块  │    │  │
│  │  │- api        │- task       │- device     │- http       │    │  │
│  │  │- function   │- timer      │- robot      │- websocket  │    │  │
│  │  │- event      │- scheduler  │- modbus     │- tcp        │    │  │
│  │  └────────────┴───────────┴───────────┴────────────┘    │  │
│  │  ┌────────────┬───────────┬───────────┬────────────┐    │  │
│  │  │Storage模块│ Log模块   │Utility模块│ 自定义模块 │    │  │
│  │  │- storage    │- log        │- mail       │- AI模块     │    │  │
│  │  │- database   │- alert      │- crypto     │- 视觉模块   │    │  │
│  │  │- cache      │- audit      │- file       │- 算法模块   │    │  │
│  │  └────────────┴───────────┴───────────┴────────────┘    │  │
│  └──────────────────────────────────────────────────────────────────────────────┘  │
└───────────────────────────────────────────────────────────────────────────────────────────┘
                                      │
                                      ▼
┌───────────────────────────────────────────────────────────────────────────────────────────┐
│          ⚙️ 核心引擎层 (scripts/engine/) - 多脚本并发管理           │
│  ┌───────────────────────────────────────────────────────────────────────────┐  │
│  │                     VWEDGlobalRegistry (全局注册中心)                    │  │
│  │  ┌────────────┬────────────┬────────────┬────────────┐    │  │
│  │  │MultiScript   │SecuritySandbox│LifecycleManager│ ResourceTracker│    │  │
│  │  │Engine        │  安全沙箱     │  生命周期管理  │   资源跟踪   │    │  │
│  │  │-多脚本并发 │- 模块白名单   │- boot()入口函数│- script_id隔离│    │  │
│  │  │-script_id隔离│- 资源限制     │- 异常处理      │- 资源自动回收│    │  │
│  │  │-状态管理   │- 权限控制     │- 重启机制      │- 性能监控    │    │  │
│  │  └────────────┴────────────┴────────────┴────────────┘    │  │
│  └───────────────────────────────────────────────────────────────────────────┐  │
└───────────────────────────────────────────────────────────────────────────────────────────┘
                                      │
                                      ▼
┌───────────────────────────────────────────────────────────────────────────────────────────┐
│           💾 数据模型层 (scripts/models/) - 持久化与存储            │
│  ┌───────────────┬───────────────┬───────────────┬───────────────┐  │
│  │ ScriptProject │  ScriptFile   │ExecutionLog   │APIRegistration │  │
│  │- 项目管理     │- 文件存储     │- 执行记录     │- API注册记录  │  │
│  │- 目录结构     │- 版本管理     │- 错误日志     │- 函数注册记录│  │
│  │- 权限控制     │- 内容检索     │- 性能指标     │- 路由注册记录│  │
│  └───────────────┴───────────────┴───────────────┴───────────────┘  │
└───────────────────────────────────────────────────────────────────────────────────────────┘

2.2 注册中心详细机制

2.2.1 API接口注册与访问流程

【在线脚本注册阶段】
┌─────────────────────────────────────────────────────────┐
│  在线脚本: VWED.api.register_route()                     │
│  def boot():                                           │
│      VWED.api.register_route(                         │
│          path="/script-api/custom",                   │
│          method="POST",                               │
│          handler=my_custom_handler                    │
│      )                                                │
└─────────────────────┬───────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────┐
│               全局注册中心存储                           │
│  api_routes = {                                        │
│      "/script-api/custom": {                          │
│          "method": "POST",                            │
│          "handler": my_custom_handler,                │
│          "script_id": "script_001",                   │
│          "registered_at": "2024-01-01 12:00:00"       │
│      }                                                │
│  }                                                    │
└─────────────────────┬───────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────┐
│              FastAPI 动态路由注册                        │
│  @app.route("/script-api/{path:path}", methods=ALL)    │
│  async def dynamic_script_route(path, request):        │
│      # 查找注册表                                       │
│      route_info = registry.get_api_route(path)        │
│      if route_info:                                   │
│          return await route_info["handler"](request)  │
│      else:                                            │
│          return {"error": "接口未注册"}                  │
└─────────────────────────────────────────────────────────┘

【HTTP请求块访问阶段】
┌─────────────────────────────────────────────────────────┐
│  任务流程中的HTTP请求块 (htttp_request.py)               │
│  async def execute():                                  │
│      url = "http://localhost:8000/script-api/custom"  │
│      response = await http_client.post(url, data={})  │
│      # 这会触发上面的动态路由                            │
└─────────────────────┬───────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────┐
│              执行用户注册的函数                          │
│  def my_custom_handler(request):                       │
│      # 用户自定义的业务逻辑                              │
│      data = request.json()                            │
│      result = process_business_logic(data)            │
│      return {"success": True, "data": result}         │
└─────────────────────────────────────────────────────────┘

【脚本执行块调用阶段】
┌─────────────────────────────────────────────────────────┐
│  任务流程中的脚本执行块 (script.py)                      │
│  async def _execute_script():                          │
│      # 除了加载文件函数外,还可以调用注册的函数            │
│      registered_func = registry.get_registered_function(│
│          function_name                                │
│      )                                                │
│      if registered_func:                              │
│          return await registered_func(function_args)  │
└─────────────────────────────────────────────────────────┘

2.2.2 自定义函数注册机制

【函数注册】
VWED.function.register(name="calculate_distance", handler=calc_func)
            │
            ▼
┌─────────────────────────────────────────────────────────┐
│               函数注册表                                │
│  registered_functions = {                              │
│      "calculate_distance": {                          │
│          "handler": calc_func,                        │
│          "script_id": "script_001",                   │
│          "description": "计算两点距离",                 │
│          "params": ["point1", "point2"]               │
│      }                                                │
│  }                                                    │
└─────────────────────┬───────────────────────────────────┘
                     │
                     ▼
【函数调用】
┌─────────────────────────────────────────────────────────┐
│  script.py 执行块                                      │
│  function_name = "calculate_distance"                  │
│  registered_func = registry.get_function(function_name)│
│  result = await registered_func(function_args)        │
└─────────────────────────────────────────────────────────┘

2.2.3 事件系统集成

【事件注册】
VWED.event.listen(event_name="task_completed", handler=on_complete)
            │
            ▼
┌─────────────────────────────────────────────────────────┐
│              事件监听器注册表                            │
│  event_listeners = {                                   │
│      "task_completed": [                              │
│          {                                            │
│              "handler": on_complete,                  │
│              "script_id": "script_001",               │
│              "priority": 1                            │
│          }                                            │
│      ]                                                │
│  }                                                    │
└─────────────────────┬───────────────────────────────────┘
                     │
                     ▼
【事件触发】
┌─────────────────────────────────────────────────────────┐
│  系统其他模块触发事件                                    │
│  from services.script_engine import script_registry   │
│  await script_registry.emit_event(                    │
│      "task_completed",                                │
│      {"task_id": 123, "status": "success"}           │
│  )                                                    │
└─────────────────────────────────────────────────────────┘

三、脚本服务生命周期与功能实现

3.1 文件创建与管理功能

  1. 目录创建

    • 支持在 scripts/user_save/ 目录下创建多层级目录结构
    • 提供目录重命名、删除等管理功能
  2. 脚本文件创建

    • 在指定目录下创建 .py 脚本文件
    • 提供文件重命名、删除、复制等操作
    • 支持文件内容的在线编辑和保存
  3. 脚本模板生成

    • 新建脚本时自动生成标准模板代码
    • 包含常用的函数定义和注释结构

3.2 脚本执行控制功能

  1. 脚本启动运行

    • 通过 API 接口启动指定脚本文件执行
    • 支持传递启动参数和配置信息
    • 实时监控脚本运行状态
  2. 脚本停止控制

    • 支持强制停止正在运行的脚本
    • 优雅关闭和资源清理机制
    • 停止后状态反馈和日志记录
  3. 脚本重启功能

    • 支持脚本的重启操作
    • 保持配置参数的重启机制

3.3 实时日志推送与记录

  1. WebSocket 实时推送

    • 脚本执行过程中的标准输出实时推送
    • 错误信息和异常堆栈的实时推送
    • 执行状态变化的实时通知
  2. 日志分级管理

    • 支持 DEBUG、INFO、WARNING、ERROR 等日志级别
    • 按级别过滤和显示日志信息
    • 日志格式标准化和时间戳记录
  3. 日志持久化存储

    • 执行日志保存到数据库表中
    • 支持历史日志查询和检索
    • 执行结果和异常信息的结构化存储

3.4 任务系统集成调用

  1. VWED 任务流程集成

    • 在任务流程中通过脚本组件调用 Python 函数
    • 支持 SCRIPTSET_TASK_VARIABLES 两种调用模式
    • 与任务执行引擎无缝集成
  2. 函数动态调用机制

    # 当前实现:固定加载 scripts/user_save/test1.py
    script_file = "scripts/user_save/test1.py"
    
    # 动态加载模块
    spec = importlib.util.spec_from_file_location("user_script", script_file)
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)
    
    # 调用指定函数
    func = getattr(module, function_name)
    result = func(function_args)
    
  3. 参数传递机制

    • 字典参数{"key": "value"} → 作为关键字参数传递
    • 列表参数[arg1, arg2] → 作为位置参数传递
    • 字符串参数:支持 eval() 解析为 Python 对象
    • 智能参数判断:根据函数签名自动选择传递方式
  4. 异步函数支持

    # 检测异步函数并等待结果
    if inspect.iscoroutine(result_value):
        result_value = await result_value
    

3.5 变量上下文管理

  1. 任务变量设置

    • SET_TASK_VARIABLES 模式:将函数返回值设置为任务变量
    • 支持字典结果的键值对分别设置为变量
    • 非字典结果设置为 scriptResult 变量
  2. 变量持久化

    • 任务变量保存到数据库的 taskrecord
    • JSON 格式存储变量数据
    • 支持跨组件的变量传递和引用
  3. 上下文传递

    • TaskContext 管理任务执行过程中的变量状态
    • 变量在任务流程中的传递和更新
    • 变量作用域和生命周期管理

四、统一 VWED 对象模块系统Python 版)

4.1 设计理念

所有功能都通过统一的 VWED.内置对象模块.具体内置对象(参数1参数2) 形式调用,包括:

  • 内置函数注册
  • 接口注册
  • 事件监听
  • 定时任务
  • 设备通信
  • 系统操作

4.2 核心对象模块结构

对象模块 说明 示例
VWED.api 接口注册模块 VWED.api.register_route()
VWED.function 自定义函数注册模块 VWED.function.register()
VWED.event 事件监听模块 VWED.event.listen()
VWED.task 任务管理模块 VWED.task.create()
VWED.device 设备通信模块 VWED.device.modbus()
VWED.robot 机器人控制模块 VWED.robot.get_status()
VWED.timer 定时任务模块 VWED.timer.schedule()
VWED.log 日志输出模块 VWED.log.info()
VWED.http HTTP 请求模块 VWED.http.post()
VWED.storage 库位管理模块 VWED.storage.get_location()
VWED.mail 邮件发送模块 VWED.mail.send()

4.3 统一对象模块 API

4.3.1 接口注册模块 (VWED.api)

# 注册 HTTP 接口
VWED.api.register_route(path="/script-api/move", method="POST", handler=move_handler)

# 注册 WebSocket 接口  
VWED.api.register_websocket(path="/ws/script", handler=ws_handler)

# 注册 TCP 接口
VWED.api.register_tcp(port=9999, handler=tcp_handler)

4.3.2 自定义函数注册模块 (VWED.function)

# 注册自定义函数
VWED.function.register(name="calculate_distance", handler=calc_distance_func, 
                      description="计算两点间距离", params=["point1", "point2"])

# 注册业务逻辑函数
VWED.function.register(name="warehouse_allocation", handler=allocate_warehouse,
                      description="仓库分配逻辑", params=["goods_type", "quantity"])

# 注册数据处理函数
VWED.function.register(name="format_sensor_data", handler=format_data_func,
                      description="格式化传感器数据", params=["raw_data"])

# 获取已注册函数列表
functions = VWED.function.list_all()

# 调用注册的函数内部使用通常在script.py中调用
result = VWED.function.call(name="calculate_distance", args={"point1": [0,0], "point2": [3,4]})

4.3.3 事件监听模块 (VWED.event)

# 监听系统事件
VWED.event.listen(event_name="task_done", handler=on_task_done)

# 监听自定义事件
VWED.event.listen(event_name="custom_event", handler=on_custom)

# 触发事件
VWED.event.emit(event_name="custom_event", data={"msg": "test"})

4.3.3 任务管理模块 (VWED.task)

# 创建任务
VWED.task.create(task_label="move", input_params={"to": "A001"})

# 获取任务状态
VWED.task.get_status(task_id=123)

# 停止任务
VWED.task.stop(task_id=123)

4.3.4 设备通信模块 (VWED.device)

# Modbus 通信
VWED.device.modbus(ip="192.168.1.100", port=502, register=100, value=1)

# OPC 通信
VWED.device.opc(server_url="opc.tcp://127.0.0.1:4840", node_id="ns=2;i=2")

# PLC 通信
VWED.device.plc(ip="192.168.1.50", rack=0, slot=1, db_number=1, start=0, size=4)

4.3.5 机器人控制模块 (VWED.robot)

# 获取机器人状态
VWED.robot.get_status(robot_id="robot_001")

# 获取所有机器人
VWED.robot.get_all()

# 调度机器人
VWED.robot.dispatch(robot_id="robot_001", task_id=123)

4.3.6 定时任务模块 (VWED.timer)

# 定时执行
VWED.timer.schedule(interval=5, handler=timer_handler, repeat=True)

# 延时执行
VWED.timer.delay(seconds=10, handler=delay_handler)

# 取消定时任务
VWED.timer.cancel(timer_id="timer_001")

4.3.7 日志模块 (VWED.log)

# 各种日志级别
VWED.log.info(tag="script", message="脚本启动成功")
VWED.log.debug(tag="debug", message="调试信息")
VWED.log.warning(tag="warn", message="警告信息")
VWED.log.error(tag="error", message="错误信息")

4.3.8 HTTP 请求模块 (VWED.http)

# GET 请求
VWED.http.get(url="http://api.example.com/data", headers={})

# POST 请求
VWED.http.post(url="http://api.example.com/submit", json={"key": "value"})

# PUT/DELETE 请求
VWED.http.put(url="http://api.example.com/update", json={})
VWED.http.delete(url="http://api.example.com/delete")

4.3.9 库位管理模块 (VWED.storage)

# 获取库位信息
VWED.storage.get_location(location_code="A001")

# 锁定库位
VWED.storage.lock_location(location_code="A001", task_id=123)

# 释放库位
VWED.storage.unlock_location(location_code="A001")

4.3.10 邮件模块 (VWED.mail)

# 发送邮件
VWED.mail.send(to="admin@example.com", subject="告警", body="系统异常")

# 发送带附件邮件
VWED.mail.send_with_attachment(to="admin@example.com", subject="报告", 
                              body="请查看附件", attachment_path="/tmp/report.pdf")

4.4 注册清理机制

# 脚本停止时自动清理所有注册项
def script_stop_cleanup():
    """脚本停止时调用,清理所有注册的内容"""
    VWED.api.clear_all()      # 清理接口注册
    VWED.event.clear_all()    # 清理事件监听
    VWED.timer.clear_all()    # 清理定时任务
    VWED.log.info("script", "脚本注册项已全部清理")

五、调试能力Python 版)

  • 支持 remote_pdb.set_trace()
  • 启动脚本时自动开启调试端口(如 5555
  • 前端弹出调试地址:telnet 127.0.0.1 5555
  • 支持断点、变量查看、单步执行

六、脚本目录结构Python 版)

data/VWED/python_script/
├── common/                    # 通用工具目录
│   ├── utils.py               # 通用工具脚本
│   └── config.py              # 配置管理脚本
├── warehouse/                 # 仓库管理目录
│   ├── controller.py          # 仓库控制脚本
│   └── allocation.py          # 库位分配脚本
├── robot/                     # 机器人控制目录
│   ├── dispatcher.py          # 机器人调度脚本
│   └── monitor.py             # 机器人监控脚本
├── sensors/                   # 传感器处理目录
│   ├── data_processor.py      # 数据处理脚本
│   └── alarm_handler.py       # 告警处理脚本
└── boot.py                    # 主启动脚本

创建规则

  • 创建目录create_script(directory="新目录名")
  • 创建文件create_script(directory="所属目录", filename="脚本名.py")

注意:所有用户共享同一套脚本文件,任何用户的修改都会影响所有用户。


## 八、共享脚本架构

### 8.1 单用户共享模式架构

```text
┌─────────────────────────────────────────────────────────────┐
│                    全局注册中心                              │
│  - 统一API路由        - 统一函数注册      - 统一事件监听      │
│  - 所有用户共享       - 脚本版本控制      - 最后修改覆盖      │
└─────────────────────┬───────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│                  统一脚本存储                                │
│  data/VWED/python_script/                                  │
│  ├── boot.py                                               │
│  ├── task_manager.py                                       │  
│  ├── sensor_processor.py                                   │
│  └── utils.py                                              │
└─────────────────────────────────────────────────────────────┘

8.2 多脚本服务引擎

class VWEDGlobalRegistry:
    """VWED 全局注册中心(支持多脚本独立服务)"""
    def __init__(self):
        self.api_routes = {}            # HTTP接口注册: {route_key: route_info}
        self.websocket_routes = {}      # WebSocket接口注册
        self.tcp_servers = {}           # TCP服务注册
        self.event_listeners = {}       # 事件监听器注册: {event_name: [listener_info]}
        self.timers = {}               # 定时任务注册
        self.registered_functions = {}  # 自定义函数注册: {function_name: function_info}
        self.active_scripts = {}        # 活跃脚本: {script_id: script_info}
        self.current_script_id = None   # 当前执行的脚本ID
    
    def register_api_route(self, path: str, method: str, handler, script_id: str = None):
        """注册API接口按脚本隔离"""
        script_id = script_id or self.current_script_id
        route_key = f"{method}:{path}"
        self.api_routes[route_key] = {
            "path": path,
            "method": method,
            "handler": handler,
            "script_id": script_id,
            "registered_at": datetime.now().isoformat()
        }
        
    def register_function(self, name: str, handler, script_id: str = None, 
                         description: str = "", params: list = None):
        """注册自定义函数(按脚本隔离)"""
        script_id = script_id or self.current_script_id
        self.registered_functions[name] = {
            "handler": handler,
            "script_id": script_id,
            "description": description,
            "params": params or [],
            "registered_at": datetime.now().isoformat()
        }
    
    def register_event_listener(self, event_name: str, handler, script_id: str = None, priority: int = 1):
        """注册事件监听器(按脚本隔离)"""
        script_id = script_id or self.current_script_id
        if event_name not in self.event_listeners:
            self.event_listeners[event_name] = []
        
        self.event_listeners[event_name].append({
            "handler": handler,
            "script_id": script_id,
            "priority": priority,
            "registered_at": datetime.now().isoformat()
        })
    
    def get_api_route(self, path: str, method: str = "GET"):
        """获取API路由"""
        route_key = f"{method}:{path}"
        return self.api_routes.get(route_key)
    
    def get_registered_function(self, name: str):
        """获取注册的函数"""
        func_info = self.registered_functions.get(name)
        return func_info["handler"] if func_info else None
    
    def clear_script_registrations(self, script_id: str):
        """清理特定脚本的所有注册项"""
        # 清理API路由
        api_routes_to_remove = [k for k, v in self.api_routes.items() if v.get("script_id") == script_id]
        for route_key in api_routes_to_remove:
            del self.api_routes[route_key]
        
        # 清理函数注册
        functions_to_remove = [k for k, v in self.registered_functions.items() if v.get("script_id") == script_id]
        for func_name in functions_to_remove:
            del self.registered_functions[func_name]
        
        # 清理事件监听器
        for event_name in self.event_listeners:
            self.event_listeners[event_name] = [
                listener for listener in self.event_listeners[event_name] 
                if listener.get("script_id") != script_id
            ]
        
        # 清理定时任务
        timers_to_remove = [k for k, v in self.timers.items() if v.get("script_id") == script_id]
        for timer_id in timers_to_remove:
            del self.timers[timer_id]
        
        # 从活跃脚本列表移除
        if script_id in self.active_scripts:
            del self.active_scripts[script_id]
    
    def clear_all(self):
        """清理所有注册项"""
        self.api_routes.clear()
        self.websocket_routes.clear()
        self.tcp_servers.clear()
        self.event_listeners.clear()
        self.timers.clear()
        self.registered_functions.clear()
        self.active_scripts.clear()
    
    def set_current_script(self, script_id: str):
        """设置当前执行的脚本ID"""
        self.current_script_id = script_id
    
    def add_active_script(self, script_id: str, script_path: str):
        """添加活跃脚本记录"""
        self.active_scripts[script_id] = {
            "script_path": script_path,
            "started_at": datetime.now().isoformat(),
            "status": "running"
        }
    
    def get_script_registrations(self, script_id: str):
        """获取特定脚本的注册统计"""
        api_count = len([v for v in self.api_routes.values() if v.get("script_id") == script_id])
        function_count = len([v for v in self.registered_functions.values() if v.get("script_id") == script_id])
        event_count = sum([
            len([l for l in listeners if l.get("script_id") == script_id]) 
            for listeners in self.event_listeners.values()
        ])
        timer_count = len([v for v in self.timers.values() if v.get("script_id") == script_id])
        
        return {
            "apis": api_count,
            "functions": function_count,
            "events": event_count,
            "timers": timer_count
        }

# 全局注册实例
_global_registry = VWEDGlobalRegistry()


class MultiScriptEngine:
    """多脚本服务引擎"""
    
    def __init__(self):
        self.script_store = GlobalScriptStore()
        self.running_scripts = {}  # {script_id: script_info}
    
    def start_script_service(self, script_path: str):
        """启动脚本服务 - 支持多脚本并发"""
        try:
            # 1. 验证脚本文件存在
            full_script_path = f"data/VWED/python_script/{script_path}"
            if not os.path.exists(full_script_path):
                return {"success": False, "error": f"脚本文件不存在: {script_path}"}
            
            # 2. 生成唯一脚本ID
            script_id = f"{script_path}_{int(time.time() * 1000)}"
            
            # 3. 检查是否已有同名脚本在运行
            existing_script = self.find_running_script_by_path(script_path)
            if existing_script:
                return {
                    "success": False, 
                    "error": f"脚本 {script_path} 已在运行中script_id: {existing_script['script_id']}"
                }
            
            # 4. 设置全局注册中心上下文
            _global_registry.set_current_script(script_id)
            _global_registry.add_active_script(script_id, script_path)
            
            # 5. 注入VWED模块
            script_globals = {
                'VWED': VWEDObjectFactory(),
                '__name__': '__main__'
            }
            
            # 6. 执行脚本
            with open(full_script_path, 'r', encoding='utf-8') as f:
                script_code = f.read()
            
            exec(script_code, script_globals)
            
            # 7. 调用boot函数注册服务
            if 'boot' in script_globals:
                script_globals['boot']()
            else:
                _global_registry.clear_script_registrations(script_id)
                return {"success": False, "error": "脚本缺少boot()入口函数"}
            
            # 8. 记录运行中的脚本
            self.running_scripts[script_id] = {
                "script_path": script_path,
                "started_at": datetime.now().isoformat(),
                "status": "running"
            }
            
            # 9. 保存脚本到数据库
            self.script_store.save_script(script_path, script_code)
            
            # 10. 获取注册统计
            registrations = _global_registry.get_script_registrations(script_id)
            
            return {
                "success": True, 
                "script_id": script_id,
                "message": f"脚本服务启动成功,已注册 {registrations['apis']} 个接口,{registrations['functions']} 个函数,{registrations['events']} 个事件监听器"
            }
            
        except Exception as e:
            if script_id:
                _global_registry.clear_script_registrations(script_id)
            return {"success": False, "error": f"脚本服务启动失败: {str(e)}"}
    
    def stop_script_service(self, script_id: str):
        """停止特定脚本服务 - 关键功能"""
        try:
            if script_id not in self.running_scripts:
                return {"success": False, "error": f"脚本 {script_id} 未运行"}
            
            # 获取注册统计
            registrations = _global_registry.get_script_registrations(script_id)
            
            # 清理该脚本的所有注册项 - 这是关键步骤
            _global_registry.clear_script_registrations(script_id)
            
            # 从运行列表移除
            script_info = self.running_scripts.pop(script_id)
            
            return {
                "success": True,
                "message": f"脚本服务已停止 ({script_info['script_path']}),已清理 {registrations['apis']} 个接口、{registrations['functions']} 个函数、{registrations['events']} 个事件监听器"
            }
            
        except Exception as e:
            return {"success": False, "error": f"停止脚本服务失败: {str(e)}"}
    
    def get_running_scripts(self):
        """获取所有运行中的脚本"""
        result = []
        for script_id, script_info in self.running_scripts.items():
            registrations = _global_registry.get_script_registrations(script_id)
            result.append({
                "script_id": script_id,
                "script_path": script_info["script_path"],
                "started_at": script_info["started_at"],
                "status": script_info["status"],
                "registrations": registrations
            })
        return result
    
    def find_running_script_by_path(self, script_path: str):
        """根据脚本路径查找运行中的脚本"""
        for script_id, script_info in self.running_scripts.items():
            if script_info["script_path"] == script_path:
                return {"script_id": script_id, **script_info}
        return None
    
    def create_script(self, directory: str, filename: str = None):
        """创建脚本或目录"""
        try:
            base_path = "data/VWED/python_script"
            
            if filename is None:
                # 创建目录
                dir_path = os.path.join(base_path, directory)
                os.makedirs(dir_path, exist_ok=True)
                return {"success": True, "message": f"目录创建成功: {directory}"}
            else:
                # 创建文件
                dir_path = os.path.join(base_path, directory)
                os.makedirs(dir_path, exist_ok=True)
                
                file_path = os.path.join(dir_path, filename)
                if not filename.endswith('.py'):
                    filename += '.py'
                    file_path += '.py'
                
                # 创建默认脚本模板
                default_content = f'''def boot():
    """脚本入口函数"""
    VWED.log.info("script", "脚本 {filename} 启动成功")
    
    # 在这里注册您的接口、函数和事件监听器
    # 示例:
    # VWED.api.register_route("/test", "GET", test_handler)
    # VWED.function.register("test_func", test_function)
    
    pass

def test_handler(request):
    """示例API处理函数"""
    return {{"message": "Hello from script API", "script": "{filename}"}}

def test_function(args):
    """示例自定义函数"""
    return {{"result": "Function executed", "args": args}}
'''
                
                with open(file_path, 'w', encoding='utf-8') as f:
                    f.write(default_content)
                
                return {"success": True, "message": f"文件创建成功: {directory}/{filename}"}
                
        except Exception as e:
            return {"success": False, "error": f"创建失败: {str(e)}"}


class GlobalScriptStore:
    """全局脚本存储管理"""
    
    def save_script(self, script_name: str, content: str):
        """保存脚本到文件系统和数据库"""
        # 1. 保存到文件系统
        script_path = f"data/VWED/python_script/{script_name}"
        with open(script_path, 'w', encoding='utf-8') as f:
            f.write(content)
        
        # 2. 保存到数据库(版本控制,但只保留最新版本)
        self._save_to_database(script_name, content)
    
    def _save_to_database(self, script_name: str, content: str):
        """保存到数据库(覆盖旧版本)"""
        # 这里实现数据库保存逻辑
        # 系统只保留最后一个版本,不考虑多版本管理
        pass

8.3 多脚本服务访问控制机制

# FastAPI 多脚本路由处理器
@app.api_route("/script-api/{path:path}", methods=["GET", "POST", "PUT", "DELETE"])
async def multi_script_route(path: str, request: Request):
    """多脚本路由处理器"""
    
    # 1. 检查是否有任何脚本在运行
    if not multi_script_engine.running_scripts:
        return {"error": "没有脚本服务在运行,请先启动脚本服务"}
    
    # 2. 从全局注册中心获取路由
    api_path = "/" + path
    route_info = _global_registry.get_api_route(api_path, request.method)
    
    if route_info:
        # 3. 检查该路由对应的脚本是否仍在运行
        script_id = route_info.get("script_id")
        if script_id not in multi_script_engine.running_scripts:
            return {"error": f"路由 {api_path} 对应的脚本服务已停止"}
        
        try:
            handler = route_info["handler"]
            
            # 构建请求数据
            request_data = {
                "method": request.method,
                "path": path,
                "script_id": script_id,  # 添加脚本ID信息
                "headers": dict(request.headers),
                "query_params": dict(request.query_params)
            }
            
            if request.method in ["POST", "PUT", "PATCH"]:
                try:
                    request_data["json"] = await request.json()
                except:
                    request_data["body"] = await request.body()
            
            # 调用处理函数
            if asyncio.iscoroutinefunction(handler):
                result = await handler(request_data)
            else:
                result = handler(request_data)
                
            return result
            
        except Exception as e:
            return {"error": f"脚本接口执行失败: {str(e)}"}
    else:
        return {"error": f"接口 {request.method} {api_path} 未注册"}

# 脚本管理API
@app.get("/script-engine/running")
async def get_running_scripts():
    """获取所有运行中的脚本"""
    return multi_script_engine.get_running_scripts()

@app.post("/script-engine/start")
async def start_script_service(request: dict):
    """启动脚本服务"""
    script_path = request.get("script_path")
    if not script_path:
        return {"success": False, "error": "缺少script_path参数"}
    
    return multi_script_engine.start_script_service(script_path)

@app.post("/script-engine/stop")
async def stop_script_service(request: dict):
    """停止脚本服务"""
    script_id = request.get("script_id")
    if not script_id:
        return {"success": False, "error": "缺少script_id参数"}
    
    return multi_script_engine.stop_script_service(script_id)

九、关键功能说明

9.1 多脚本服务启动的重要性

多脚本服务启动是整个脚本引擎的核心功能:

  1. 独立注册每个脚本启动时生成唯一的script_id其接口、函数、事件带有该ID标记注册到全局注册中心
  2. 并发运行:支持多个脚本同时运行服务,每个脚本的资源相互独立
  3. 外部访问控制:只有运行中的脚本资源才能被外部系统访问
  4. 防重复启动:同一脚本路径不能重复启动,确保资源的唯一性

9.2 按脚本ID清理的关键作用

停止特定脚本服务是精确资源清理的关键:

  1. 精确清理根据script_id只清理该脚本注册的接口、函数、事件和定时任务
  2. 其他服务不受影响:停止某个脚本不会影响其他正在运行的脚本服务
  3. 访问控制:该脚本的资源立即不可访问,但其他脚本的资源继续正常服务
  4. 状态一致性:确保注册中心的资源状态与实际运行脚本保持同步

9.3 多脚本资源隔离机制

每个脚本的资源完全独立:

  1. 接口隔离不同脚本可以注册相同路径的接口通过script_id区分
  2. 函数隔离不同脚本的同名函数通过script_id管理后注册的会覆盖前面的
  3. 事件隔离事件监听器按script_id分组停止脚本时精确清理对应监听器
  4. 定时任务隔离定时任务带有script_id标记支持按脚本独立管理

9.4 目录和文件创建机制

支持灵活的脚本组织结构:

  1. 目录创建create_script(directory="新目录名")
  2. 文件创建create_script(directory="所属目录", filename="脚本名.py")
  3. 模板生成新创建的脚本文件包含默认的boot()函数和示例代码

十、多脚本服务示例

10.1 并发运行示例

# 脚本A: warehouse/controller.py
def boot():
    VWED.api.register_route("/warehouse/status", "GET", get_warehouse_status)
    VWED.function.register("allocate_storage", allocate_storage_func)

# 脚本B: robot/dispatcher.py  
def boot():
    VWED.api.register_route("/robot/dispatch", "POST", dispatch_robot)
    VWED.function.register("get_robot_status", get_robot_status_func)

# 两个脚本可以同时运行,提供不同的服务
# A脚本: script_id = "warehouse/controller.py_1640000000000"
# B脚本: script_id = "robot/dispatcher.py_1640000001000"

10.2 脚本服务管理操作

# 启动仓库控制脚本
POST /script-engine/start
{
    "script_path": "warehouse/controller.py"
}

# 启动机器人调度脚本
POST /script-engine/start  
{
    "script_path": "robot/dispatcher.py"
}

# 查看运行中的脚本
GET /script-engine/running
# 返回:
[
    {
        "script_id": "warehouse/controller.py_1640000000000",
        "script_path": "warehouse/controller.py", 
        "started_at": "2024-01-01T10:00:00",
        "status": "running",
        "registrations": {"apis": 1, "functions": 1, "events": 0, "timers": 0}
    },
    {
        "script_id": "robot/dispatcher.py_1640000001000",
        "script_path": "robot/dispatcher.py",
        "started_at": "2024-01-01T10:01:00", 
        "status": "running",
        "registrations": {"apis": 1, "functions": 1, "events": 0, "timers": 0}
    }
]

# 停止仓库控制脚本(机器人脚本继续运行)
POST /script-engine/stop
{
    "script_id": "warehouse/controller.py_1640000000000"
}

十一、框架扩展指南

11.1 新增内置模块扩展步骤

步骤1创建模块接口定义

# scripts/modules/ai_module.py
from abc import ABC, abstractmethod

class BaseVWEDAIModule(ABC):
    """AI模块基础接口"""
    
    @abstractmethod
    def predict(self, data, model_name: str = "default"):
        """预测接口"""
        pass
    
    @abstractmethod
    def train(self, training_data, model_config: dict):
        """训练接口"""
        pass
    
    @abstractmethod
    def get_available_models(self):
        """获取可用模型列表"""
        pass

class VWEDAIModule(BaseVWEDAIModule):
    """AI模块实现"""
    
    def __init__(self, registry):
        self.registry = registry
        self.models = {}
    
    def predict(self, data, model_name: str = "default"):
        """执行预测"""
        if model_name not in self.models:
            raise ValueError(f"模型 {model_name} 未找到")
        
        # 实现预测逻辑
        return {"prediction": "result", "confidence": 0.95}
    
    def train(self, training_data, model_config: dict):
        """训练模型"""
        model_name = model_config.get("name", "trained_model")
        # 实现训练逻辑
        self.models[model_name] = {"status": "trained", "accuracy": 0.98}
        return {"success": True, "model_name": model_name}
    
    def get_available_models(self):
        """获取可用模型"""
        return list(self.models.keys())

步骤2注册到VWED对象工厂

# scripts/services/script_builtin_functions.py
class VWEDObjectFactory:
    """VWED对象工厂 - 支持动态模块扩展"""
    
    def __init__(self):
        self._modules = {}
        self._load_core_modules()
        self._load_extended_modules()  # 加载扩展模块
    
    def _load_extended_modules(self):
        """加载扩展模块"""
        # AI模块
        from scripts.modules.ai_module import VWEDAIModule
        self._modules['ai'] = VWEDAIModule(self.registry)
        
        # 视觉模块
        from scripts.modules.vision_module import VWEDVisionModule
        self._modules['vision'] = VWEDVisionModule(self.registry)
        
        # 算法模块
        from scripts.modules.algorithm_module import VWEDAlgorithmModule
        self._modules['algorithm'] = VWEDAlgorithmModule(self.registry)
    
    def register_custom_module(self, module_name: str, module_instance):
        """注册自定义模块"""
        self._modules[module_name] = module_instance
    
    def __getattr__(self, name):
        if name in self._modules:
            return self._modules[name]
        raise AttributeError(f"'VWED' object has no attribute '{name}'")

步骤3脚本中使用新模块

# 脚本示例ai_processor.py
def boot():
    """AI处理脚本入口"""
    VWED.log.info("ai", "AI处理脚本启动")
    
    # 注册AI预测接口
    VWED.api.register_route("/ai/predict", "POST", ai_predict_handler)
    
    # 注册AI训练接口
    VWED.api.register_route("/ai/train", "POST", ai_train_handler)
    
    # 注册模型管理函数
    VWED.function.register("get_model_info", get_model_info_func)

def ai_predict_handler(request):
    """AI预测处理器"""
    data = request.get("json", {})
    model_name = data.get("model", "default")
    input_data = data.get("data")
    
    # 使用VWED.ai模块
    result = VWED.ai.predict(input_data, model_name)
    return {"success": True, "result": result}

def ai_train_handler(request):
    """AI训练处理器"""
    data = request.get("json", {})
    training_data = data.get("training_data")
    model_config = data.get("config", {})
    
    # 使用VWED.ai模块
    result = VWED.ai.train(training_data, model_config)
    return {"success": True, "result": result}

def get_model_info_func(args):
    """获取模型信息"""
    models = VWED.ai.get_available_models()
    return {"available_models": models}

11.2 协议扩展指南

扩展gRPC协议支持

# scripts/interfaces/grpc_interface.py
import grpc
from concurrent import futures

class ScriptGRPCAPI:
    """脚本gRPC接口"""
    
    def __init__(self, registry):
        self.registry = registry
        self.server = None
    
    def register_grpc_service(self, service_name: str, service_impl, port: int = 50051):
        """注册gRPC服务"""
        script_id = self.registry.current_script_id
        
        if not self.server:
            self.server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
        
        # 注册服务到gRPC服务器
        # add_ServicenameServicer_to_server(service_impl, self.server)
        
        # 记录到注册中心
        self.registry.grpc_services[f"{service_name}:{port}"] = {
            "service_impl": service_impl,
            "port": port,
            "script_id": script_id,
            "registered_at": datetime.now().isoformat()
        }
        
        self.server.add_insecure_port(f'[::]:{port}')
        self.server.start()
        
        return {"success": True, "service": service_name, "port": port}

11.3 安全策略扩展

企业级安全扩展

# scripts/engine/advanced_security.py
class EnterpriseSecuritySandbox(ScriptSecuritySandbox):
    """企业级安全沙箱"""
    
    def __init__(self):
        super().__init__()
        self.enterprise_policies = {
            "ip_whitelist": [],
            "domain_blacklist": [],
            "api_rate_limit": 100,  # 每分钟请求数
            "resource_quota": {
                "max_memory": "512MB",
                "max_cpu_percent": 30,
                "max_execution_time": 300  # 秒
            }
        }
    
    def validate_network_access(self, target_host: str, target_port: int) -> bool:
        """验证网络访问权限"""
        # 检查IP白名单
        if self.enterprise_policies["ip_whitelist"]:
            if target_host not in self.enterprise_policies["ip_whitelist"]:
                return False
        
        # 检查域名黑名单
        for blocked_domain in self.enterprise_policies["domain_blacklist"]:
            if blocked_domain in target_host:
                return False
        
        return super().validate_network_access(target_host, target_port)
    
    def check_api_rate_limit(self, script_id: str) -> bool:
        """检查API调用频率限制"""
        # 实现频率限制逻辑
        return True
    
    def monitor_resource_usage(self, script_id: str) -> dict:
        """监控资源使用情况"""
        # 实现资源监控逻辑
        return {
            "memory_usage": "128MB",
            "cpu_usage": "15%",
            "execution_time": 120
        }

11.4 日志目标扩展

多目标日志系统

# scripts/services/advanced_logger.py
class MultiTargetLogger(ScriptWebSocketLoggerService):
    """多目标日志服务"""
    
    def __init__(self):
        super().__init__()
        self.log_targets = {
            "file": FileLogTarget(),
            "database": DatabaseLogTarget(),
            "elasticsearch": ElasticsearchLogTarget(),
            "webhook": WebhookLogTarget()
        }
    
    def log(self, level: str, tag: str, message: str, script_id: str = None):
        """多目标日志输出"""
        log_entry = {
            "timestamp": datetime.now().isoformat(),
            "level": level,
            "tag": tag,
            "message": message,
            "script_id": script_id
        }
        
        # 发送到所有启用的日志目标
        for target_name, target in self.log_targets.items():
            if target.is_enabled():
                target.write_log(log_entry)
        
        # 继续原有的WebSocket推送
        super().log(level, tag, message, script_id)

class DatabaseLogTarget:
    """数据库日志目标"""
    
    def write_log(self, log_entry: dict):
        # 写入数据库
        pass
    
    def is_enabled(self) -> bool:
        return True

class ElasticsearchLogTarget:
    """Elasticsearch日志目标"""
    
    def write_log(self, log_entry: dict):
        # 写入Elasticsearch
        pass
    
    def is_enabled(self) -> bool:
        return False  # 可配置

十二、当前实现状态与技术细节

12.1 script.py 处理器实现分析

基于 services/execution/handlers/script.py 的当前实现,脚本引擎具备以下技术特性:

12.1.1 双模式处理器设计

# 两种脚本处理模式
@register_handler(ScriptBlockName.SET_TASK_VARIABLES)  # 变量设置模式
@register_handler(ScriptBlockName.SCRIPT)              # 脚本执行模式

12.1.2 动态模块加载机制

# 固定脚本路径(当前实现)
script_file = "scripts/user_save/test1.py"

# 动态加载实现
spec = importlib.util.spec_from_file_location("user_script", script_file)
module = importlib.util.module_from_spec(spec)
sys.modules["user_script"] = module
spec.loader.exec_module(module)

12.1.3 智能参数传递算法

# 参数类型智能识别
if isinstance(function_args, dict):
    kwargs = function_args
elif isinstance(function_args, list):
    args = function_args
elif isinstance(function_args, str):
    function_args = eval(function_args)

# 函数签名检测
sig = inspect.signature(func)
params = list(sig.parameters.keys())
if len(params) == 1 and isinstance(function_args, dict) and not args:
    result_value = func(function_args)  # 单参数字典传递
else:
    result_value = func(*args, **kwargs)  # 标准参数传递

12.1.4 异步执行检测机制

# 协程检测和等待
if inspect.iscoroutine(result_value):
    import asyncio
    result_value = await result_value

12.1.5 变量上下文管理

# SET_TASK_VARIABLES 模式:多变量设置
if isinstance(result_value, dict):
    for key, value in result_value.items():
        context.set_variable(key, value)
else:
    context.set_variable("scriptResult", result_value)

# SCRIPT 模式:单一结果变量
context.set_variable("scriptResult", result_value)

12.1.6 数据库持久化机制

# 变量保存到数据库
variables_json = json.dumps(context.variables, ensure_ascii=False)
stmt = update(VWEDTaskRecord).where(VWEDTaskRecord.id == task_record_id).values(
    variables=variables_json
)

12.2 当前架构的优势与局限

优势:

  1. 简单高效:固定脚本路径,减少配置复杂度
  2. 类型智能:自动识别参数类型和传递方式
  3. 异步支持:完整的协程检测和执行机制
  4. 双模式设计:灵活的变量设置和脚本执行模式
  5. 深度集成:与 VWED 任务系统无缝集成

当前局限:

  1. 固定路径:脚本路径硬编码为 scripts/user_save/test1.py
  2. 单文件限制:无法动态选择不同的脚本文件
  3. 缺少文件管理:没有脚本文件的创建、编辑、删除功能
  4. 无日志推送:缺少 WebSocket 实时日志推送功能
  5. 无生命周期管理:缺少脚本启动、停止、状态监控

12.3 扩展改进建议

12.3.1 动态脚本路径支持

# 建议改进:支持动态脚本路径
script_file = input_params.get("scriptPath", "scripts/user_save/test1.py")

12.3.2 文件管理功能扩展

  • 添加脚本文件 CRUD 操作 API
  • 支持目录结构管理
  • 实现在线编辑器集成

12.3.3 实时日志推送集成

  • 集成 WebSocket 日志推送服务
  • 脚本执行过程的实时监控
  • 执行状态和结果的实时反馈

12.3.4 生命周期管理扩展

  • 脚本进程的启动、停止控制
  • 运行状态监控和管理
  • 资源使用情况追踪

十三、最佳实践与注意事项

13.1 脚本开发最佳实践

推荐的脚本结构

# 标准脚本模板
import asyncio
from typing import Dict, Any

def boot():
    """脚本入口函数 - 必须实现"""
    VWED.log.info("script", "脚本启动中...")
    
    # 1. 注册API接口
    register_apis()
    
    # 2. 注册自定义函数
    register_functions()
    
    # 3. 注册事件监听器
    register_events()
    
    # 4. 启动定时任务
    start_timers()
    
    VWED.log.info("script", "脚本启动完成")

def register_apis():
    """注册API接口"""
    VWED.api.register_route("/health", "GET", health_check)
    VWED.api.register_route("/process", "POST", process_data)

def register_functions():
    """注册自定义函数"""
    VWED.function.register("calculate", calculate_func, "数据计算函数")
    VWED.function.register("validate", validate_func, "数据验证函数")

def register_events():
    """注册事件监听器"""
    VWED.event.listen("data_received", on_data_received)
    VWED.event.listen("task_completed", on_task_completed)

def start_timers():
    """启动定时任务"""
    VWED.timer.schedule(interval=60, handler=periodic_cleanup, repeat=True)

# API处理函数
def health_check(request: Dict[str, Any]) -> Dict[str, Any]:
    """健康检查接口"""
    return {"status": "healthy", "timestamp": datetime.now().isoformat()}

async def process_data(request: Dict[str, Any]) -> Dict[str, Any]:
    """数据处理接口(支持异步)"""
    data = request.get("json", {})
    
    try:
        # 数据验证
        if not validate_input(data):
            return {"error": "数据验证失败"}
        
        # 数据处理
        result = await process_business_logic(data)
        
        # 触发事件
        VWED.event.emit("data_processed", {"result": result})
        
        return {"success": True, "result": result}
        
    except Exception as e:
        VWED.log.error("script", f"数据处理失败: {str(e)}")
        return {"error": str(e)}

# 自定义函数
def calculate_func(args: Dict[str, Any]) -> Dict[str, Any]:
    """计算函数"""
    # 实现计算逻辑
    return {"result": "calculated_value"}

# 事件处理函数
def on_data_received(event_data: Dict[str, Any]):
    """数据接收事件处理"""
    VWED.log.info("event", f"收到数据: {event_data}")

# 定时任务函数
def periodic_cleanup():
    """定期清理任务"""
    VWED.log.info("timer", "执行定期清理")

13.2 性能优化建议

  1. 异步编程优先使用异步函数处理IO密集型操作
  2. 资源管理:及时释放不需要的资源,避免内存泄漏
  3. 缓存策略:合理使用缓存减少重复计算
  4. 批量处理:对于大量数据处理,采用批量处理方式
  5. 连接池:使用连接池管理数据库和外部服务连接

13.3 安全注意事项

  1. 输入验证:严格验证所有外部输入
  2. 权限控制:实施最小权限原则
  3. 敏感数据:不在脚本中硬编码敏感信息
  4. 网络访问:只访问必要的外部资源
  5. 日志安全:避免在日志中记录敏感信息

13.4 故障排查指南

常见问题及解决方案

问题类型 症状 可能原因 解决方案
脚本启动失败 boot()函数报错 语法错误、依赖缺失 检查脚本语法,确认依赖安装
API无法访问 404错误 路由未注册、脚本已停止 检查脚本运行状态和API注册
内存泄漏 内存使用持续增长 循环引用、资源未释放 使用内存分析工具,检查资源释放
性能下降 响应时间变长 阻塞操作、资源竞争 优化算法,使用异步编程
并发问题 数据不一致 共享状态竞争 使用锁机制或消息队列

十四、总结

本文档详细描述了VWED高扩展性Python脚本执行引擎的完整架构设计。该框架具有以下核心优势

14.1 核心优势

  1. 高度可扩展:模块化设计,支持任意功能模块扩展
  2. 多脚本并发独立的脚本ID隔离机制支持多脚本同时运行
  3. 统一管理:全局共享脚本,简化管理复杂度
  4. 企业级安全:多层安全防护,满足企业安全要求
  5. 云原生支持Docker化部署支持Kubernetes集群
  6. 完善监控:全方位的监控指标和告警机制

14.2 扩展能力

  • 内置模块扩展AI、视觉、算法等专业模块
  • 协议扩展gRPC、MQTT等多种通信协议
  • 安全策略扩展:企业级安全控制策略
  • 日志目标扩展:多种日志存储和分析方案
  • 监控扩展Prometheus、Grafana等监控集成

14.3 应用场景

  • 工业自动化控制系统
  • 智能仓储管理系统
  • 机器人调度控制系统
  • IoT设备管理平台
  • 数据处理与分析平台

该框架为企业提供了一个强大、灵活、安全的脚本执行环境,能够满足复杂业务场景的需求,并具备良好的扩展性和维护性。