业务系统有时会把 DWG 文件编码成 base64 字符串,通过 HTTP 请求传到服务端,然后希望在 Rhino.Compute 中完成导入、识别、参数化生成或二次处理。这个场景不要简单理解成“把 base64 字符串直接接到 Grasshopper 电池上”,因为 DWG 通常较大,而且涉及文件格式解析、插件授权、临时文件、单位、图层、块参照和安全校验。
推荐做成三层:
客户端 / 业务后端 -> 接收 base64,做认证、大小限制、审计和任务编号 -> Rhino.Compute 自定义插件端点 -> 解码 DWG,导入到无界面 RhinoDoc 或转换为中间文件/结构化结果 -> Grasshopper Hops 定义 / 自定义 GH 电池 -> 输出几何、识别结果、3dm 文件、JSON 摘要或后续业务结果
核心原则:base64 只适合作为传输格式,不适合作为 Grasshopper 画布长期流转的数据格式。Grasshopper/Hops 更适合接收 job_id、dwg_path、3dm_path、layer_filter、tolerance 这类短参数。
推荐把 DWG 处理能力封装在一个 Rhino 插件中,插件内部拆成三个部分:
DwgImportService:核心服务,负责解码、校验、落盘、导入 DWG、提取几何和元数据。Compute Endpoint:注册到 Rhino.Compute,对外提供稳定 HTTP API。Grasshopper Component:自定义 GH 电池,给 Hops 定义或普通 GH 画布使用。这样可以让同一套 DWG 处理逻辑同时服务两种入口:
| 入口 | 用途 |
|---|---|
| 自定义 Compute 端点 | 外部系统直接提交 DWG base64,返回结构化结果或中间文件引用。 |
| 自定义 GH 电池 | 设计师在 Grasshopper 中继续编排识别、生成、过滤、标注等参数化逻辑。 |
| Hops 定义 | 把含有自定义 GH 电池的 Grasshopper 定义封装成远程函数。 |
推荐数据流如下:
1. 客户端上传:
POST /api/cad/analyze
Body: { fileName, dwgBase64, options, correlationId }
2. 业务后端校验:
- 用户权限
- base64 长度
- 业务配额
- 文件名和扩展名
- correlation id
3. 后端调用 Rhino.Compute 自定义端点:
POST /Rhino/CustomDwg/Prepare
Body: { fileName, dwgBase64, options, correlationId }
4. Compute 插件执行:
- Convert.FromBase64String
- 写入服务端临时目录
- 创建无界面 RhinoDoc
- 导入 DWG
- 提取对象、图层、块、包围盒、单位等信息
- 可选:保存为 .3dm 或生成结构化 JSON
5. Grasshopper/Hops 继续处理:
- 输入 job_id 或 3dm_path
- 自定义 GH 电池读取准备好的几何或元数据
- 下游 GH 逻辑做识别、生成、优化或导出
6. 返回结果:
- result_id
- objects_count
- layers
- bounding_box
- errors / warnings
- 可下载的 3dm、json、图片或业务结果
插件端点仍然按 Compute 扩展规则实现:在 Rhino 插件中定义静态类和 public static 方法,并在插件 OnLoad 中调用 Rhino.Runtime.HostUtils.RegisterComputeEndPoint。
请求 DTO 建议保持清晰:
public sealed class DwgPrepareRequest { public string FileName { get; set; } public string DwgBase64 { get; set; } public string CorrelationId { get; set; } public DwgPrepareOptions Options { get; set; } } public sealed class DwgPrepareOptions { public string UnitSystem { get; set; } public double AbsoluteTolerance { get; set; } public string[] LayerAllowList { get; set; } public bool SaveAs3dm { get; set; } }
端点方法示意:
static class CustomDwgComputeFunctions { public static DwgPrepareResult Prepare(DwgPrepareRequest request) { // 1. 校验 fileName、base64 长度、扩展名、correlationId // 2. 解码 base64,写入受控临时目录 // 3. 使用 DwgImportService 导入和提取结果 // 4. 返回结构化摘要或 3dm/job 引用 return DwgImportService.Prepare(request); } }
注册示意:
protected override LoadReturnCode OnLoad(ref string errorMessage) { Rhino.Runtime.HostUtils.RegisterComputeEndPoint( "Rhino.CustomDwg", typeof(CustomDwgComputeFunctions) ); return base.OnLoad(ref errorMessage); }
在 Rhino 8 中,优先使用 code-driven file IO 和无界面 RhinoDoc。它可以避免脚本命令、弹窗和活动文档依赖,更适合 Compute 服务端。
实现思路:
using var doc = Rhino.RhinoDoc.CreateHeadless(null); var options = new Rhino.FileIO.FileDwgReadOptions { ImportUnreferencedBlocks = true, ImportUnreferencedLayers = true, ImportUnreferencedLinetypes = true }; bool ok = doc.Import(dwgPath, options.ToDictionary()); if (!ok) throw new InvalidOperationException("DWG import failed."); foreach (var obj in doc.Objects) { var geometry = obj.Geometry; var layerIndex = obj.Attributes.LayerIndex; // 提取曲线、块、文字、标注、包围盒或转成业务 DTO }
如果使用 Rhino 7,需要单独评估 DWG 导入路径。常见选择是:
自定义 GH 电池不要直接接收超长 base64 字符串作为常规输入。推荐输入:
| 输入名 | 类型 | 说明 |
|---|---|---|
job_id | Text | 由后端或插件端点生成的任务编号。 |
dwg_path | Text | 服务端受控目录中的 DWG 文件路径。只用于可信内网/本机调试。 |
model_3dm_path | Text | 已由插件端点转换好的 3dm 文件路径。 |
layer_filter | Text/List | 需要处理的图层名或规则。 |
tolerance | Number | 几何识别容差。 |
run | Boolean | 控制是否执行,避免画布反复求解。 |
推荐输出:
| 输出名 | 类型 | 说明 |
|---|---|---|
curves | Curve/List | 可直接进入后续 GH 逻辑的曲线。 |
breps | Brep/List | 可选的面或实体。 |
metadata | Text/JSON | 图层、块、单位、对象数量、警告信息。 |
result_id | Text | 后续下载或查询结果的编号。 |
GH 电池内部调用同一个 DwgImportService,或者读取端点已经生成的 3dm/JSON。不要让 GH 电池自己管理用户认证、HTTP 上传、base64 解码和全局临时目录清理。
在 Hops 中使用时,建议把含有自定义 GH 电池的定义保存为独立 .gh 文件,例如:
dwg_prepare_and_analyze.gh
定义结构:
Context Get: job_id / model_3dm_path / layer_filter / tolerance / run -> 自定义 GH 电池:ReadPreparedDwg -> 后续识别或参数化逻辑 Context Print: metadata / result_id / warnings Context Bake 或普通输出:curves / breps
调用侧 Hops 组件只需要把 Path 指向这个 .gh 文件或远程 REST URL。Hops 会根据 Context Get 和 Context Print/Bake 自动生成输入输出。自定义 GH 电池必须安装在所有 Compute 子进程能加载到的位置。
只有在内网调试、小文件、一次性验证时,才可以临时把 base64 作为 Text 输入传给 Hops;生产方案应改成预上传或插件端点接收。
.dwg,不要信任客户端传来的文件名,服务端应重新生成安全文件名。C:\ProgramData\YourCompany\ComputeJobs,禁止客户端传任意绝对路径。correlation_id 或服务端生成的 GUID。/sdk 和最小请求中验证。/sdk,确认 Rhino.CustomDwg 端点出现。Prepare,确认能返回对象数量和图层摘要。job_id 或 3dm_path 读取准备好的结果。推荐按以下顺序落地:
job_id 或 3dm_path,输出曲线、图层和元数据。这样每一步都能单独测试,出错时也能判断是上传、插件导入、Compute 加载、Grasshopper 求解还是 Hops 调用的问题。