1

上一篇文章中,我们讲解了Forge Design Automation的基本原理和样例代码的插件部分。本文继续前篇,当插件包准备就绪后,转向forgesample,基本框架流程是:

1 . app的Configure首先检测本地可用的bundle压缩包,即上篇文章编译好的结果。选择压缩包,并选择云端Forge执行的引擎。显然,Revit插件的要Revit引擎。注意选择合适的版本

图片描述

2 . 点击【Create/Update】,app将从本地上传bundle压缩包到服务端,再通过Forge的云端自动化服务端口创建自动化App,提交插件包,创建Activity。所有这些准备好后,您的Forge账户中就含有了对应的Activity,而Activity连接到自动化App,知道使用哪个插件执行接下来的任务。

         /// <summary>
        /// 创建云端自动化app,上载插件bundle
        /// </summary>
        [HttpPost]
        [Route("api/forge/designautomation/appbundles")]
        public async Task<IActionResult> CreateAppBundle([FromBody]JObject appBundleSpecs)
        {
        //......
        /// <summary>
        /// 创建 activity
        /// </summary>
        [HttpPost]
        [Route("api/forge/designautomation/activities")]
        public async Task<IActionResult> CreateActivity([FromBody]JObject activitySpecs)
        {
         //......

3 . 退出Configure窗口,程序会通过云端自动化服务获取Activity列表。在【Existing activities】选择到需要的Activity

         /// <summary>
        /// 获取该Forge账户下所有可用Activity列表
        /// </summary>
        [HttpGet]
        [Route("api/forge/designautomation/activities")]
        public async Task<List<string>> GetDefinedActivities()
        {
             Page<string> activities = await _designAutomation.GetActivitiesAsync();
            //......
             

4 . 【Input File】选择到本地的Revit文件,可使用本例samples文件夹下提供的样例文件。

5 . 输入需要改变的长和宽,点击[Start Workitem]. 将执行StartWorkitem

         [HttpPost]
        [Route("api/forge/designautomation/workitems")]
        public async Task<IActionResult> StartWorkitem([FromForm]StartWorkitemInput input)
        {
**StartWorkitem**首先把本地Revit上传到后端,并通过Forge的Data Management API将文件传到某个bucket,和我们通常为了测试Forge Viewer上传文件到bucket一样。因为Forge的云端自动化服务的输入文件只能从云存储而来,所以此案例利用Forge的bucket存储。您也可以改造为从其它数据存储。

``

        //....
        // 确保bucket存在,否则创建之
        try
        {
            PostBucketsPayload bucketPayload = new PostBucketsPayload(bucketKey, null, PostBucketsPayload.PolicyKeyEnum.Transient);
            await buckets.CreateBucketAsync(bucketPayload, "US");
        }
        catch { }; 
         
         //.......
        //上载文件到bucket
        using (StreamReader streamReader = new StreamReader(fileSavePath))
            await objects.UploadObjectAsync(bucketKey, inputFileNameOSS, (int)streamReader.BaseStream.Length, streamReader.BaseStream, "application/octet-stream");
        System.IO.File.Delete(fileSavePath);

    当这些准备好以后,配置云端自动化WorkItem的参数:

   1) 输入参数:一个是原始Revit文件,通过配置access token来拿到
  
    XrefTreeArgument inputFileArgument = new XrefTreeArgument()
                {
                    Url = string.Format("https://developer.api.autodesk.com/oss/v2/buckets/{0}/objects/{1}", bucketKey, inputFileNameOSS),
                    Headers = new Dictionary<string, string>()
                     {
                         { "Authorization", "Bearer " + oauth.access_token }
                     }
                };
    ```

2) 需要更改的几何数据(长宽),通过配置一个json文件。长宽从客户端提交的输入而来。

            dynamic inputJson = new JObject();
            inputJson.Width = widthParam;
            inputJson.Height = heigthParam;
            XrefTreeArgument inputJsonArgument = new XrefTreeArgument()
            {
                Url = "data:application/json, " + ((JObject)inputJson).ToString(Formatting.None).Replace("\"", "'")
            };
3) 再配置更新后的Revit文件放置的位置,同样,本例将把结果文件放回到bucket中。同样,需要access token。


```
        string outputFileNameOSS = string.Format("{0}_output_{1}", 
                                        DateTime.Now.ToString("yyyyMMddhhmmss"), 
                                        Path.GetFileName(input.inputFile.FileName));  
                    XrefTreeArgument outputFileArgument = new XrefTreeArgument()
                    {
                        Url = string.Format("https://developer.api.autodesk.com/oss/v2/buckets/{0}/objects/{1}",     
                                            bucketKey, outputFileNameOSS),
                        Verb = Verb.Put,
                        Headers = new Dictionary<string, string>()
                           {
                               {"Authorization", "Bearer " + oauth.access_token }
                           }
                    };
```

4) 最后就是提交任务WorkItem,传入前面配置好的参数,开启云端自动化的过程。
            string callbackUrl = string.Format("{0}/api/forge/callback/designautomation?id={1}&outputFileName={2}", 
                             OAuthController.GetAppSetting("FORGE_WEBHOOK_URL"), browerConnectionId, 
                             outputFileNameOSS);
            WorkItem workItemSpec = new WorkItem()
            {
                ActivityId = activityName,
                Arguments = new Dictionary<string, IArgument>()
                {
                    { "inputFile", inputFileArgument },
                    { "inputJson",  inputJsonArgument },
                    { "outputFile", outputFileArgument },
                    //回调函数
                    { "onComplete", new XrefTreeArgument { Verb = Verb.Post, Url = callbackUrl } }
                }
            };
            WorkItemStatus workItemStatus = await _designAutomation.CreateWorkItemsAsync(workItemSpec);

云端自动化开启后,需要一个过程才结束,可以通过主动查询来获取状态,而本例采取回调函数的方式,即onComplete。当WorkItem结束后,无论什么情况,都自动通知callbackUrl, 带上WorkItem执行的日志(log)文件内容。而callbackUrl定义如下:

        [HttpPost]
        [Route("/api/forge/callback/designautomation")]
        public async Task<IActionResult> OnCallback(string id, string outputFileName, [FromBody]dynamic body)
        {

由于默认是本地测试(localhost),回调函数要得到响应,采取了ngrok做重定向。

但这只是通知了网络应用的后端,但怎么自动通知前端浏览器呢?本例采用了SignalR. SignalR 是一个ASP .NET 下的类库,可以在ASP .NET 的Web项目中实现实时通信。此部分内容略过,请查阅本例的前后端相关代码。

当WorkItem成功执行,结果文件导出到Forge的bucket,还会提供一个签名下载url,出现在客户端面板,用户就能把更改后的文件下载来查看了。

图片描述

运行forgesample,按照Readme要求填写Forge的client id,client secret,本网络应用的host,以及用于获取Design Automation回调结果状态的ngrok 重定向链接。本文就不再赘述了。

本例可以作为研究其它方案的模板框架,只需要对前端用户界面做一定修改,提供核心插件模块,易于调试和诊断Forge云端自动化全过程。


梁晓冬
527 声望301 粉丝

现就职于Autodesk软件中国有限公司,主要负责Autodesk产品API和APS云服务的推广和传播工作。


引用和评论

0 条评论