├── .gitignore ├── LICENSE ├── README.md ├── server ├── npminstall.bat ├── package.json └── server.js ├── setup ├── init.bat ├── npminstall.bat ├── package.json ├── setup.bat └── setup.js ├── src ├── ClientApp │ ├── App.config │ ├── ClientApp.csproj │ ├── ODataDesignAuto.cs │ ├── ODataDesignAuto.tt │ ├── ODataDesignAuto.ttinclude │ ├── PackageContents.xml │ ├── Program.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ └── packages.config ├── RxApp │ ├── Commands.cs │ ├── DBObjectInfo.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── RxApp.csproj │ └── packages.config ├── SampleApp.sln ├── lambda │ ├── .serverless │ │ └── cloudformation-template-create-stack.json │ ├── app.js │ ├── config.js │ ├── deploy.bat │ ├── event.json │ ├── npminstall.bat │ ├── package.json │ └── serverless.yml ├── polysamp │ ├── asdkmgPoly.dll │ └── asdkpolyobj.dbx └── www │ ├── Autodesk.DASample.Viewing.Extension.DisplayXData.js │ ├── index.html │ └── viewer.html └── thumbnail.png /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | *.VC.VC.opendb 85 | 86 | # Visual Studio profiler 87 | *.psess 88 | *.vsp 89 | *.vspx 90 | *.sap 91 | 92 | # TFS 2012 Local Workspace 93 | $tf/ 94 | 95 | # Guidance Automation Toolkit 96 | *.gpState 97 | 98 | # ReSharper is a .NET coding add-in 99 | _ReSharper*/ 100 | *.[Rr]e[Ss]harper 101 | *.DotSettings.user 102 | 103 | # JustCode is a .NET coding add-in 104 | .JustCode 105 | 106 | # TeamCity is a build add-in 107 | _TeamCity* 108 | 109 | # DotCover is a Code Coverage Tool 110 | *.dotCover 111 | 112 | # NCrunch 113 | _NCrunch_* 114 | .*crunch*.local.xml 115 | nCrunchTemp_* 116 | 117 | # MightyMoose 118 | *.mm.* 119 | AutoTest.Net/ 120 | 121 | # Web workbench (sass) 122 | .sass-cache/ 123 | 124 | # Installshield output folder 125 | [Ee]xpress/ 126 | 127 | # DocProject is a documentation generator add-in 128 | DocProject/buildhelp/ 129 | DocProject/Help/*.HxT 130 | DocProject/Help/*.HxC 131 | DocProject/Help/*.hhc 132 | DocProject/Help/*.hhk 133 | DocProject/Help/*.hhp 134 | DocProject/Help/Html2 135 | DocProject/Help/html 136 | 137 | # Click-Once directory 138 | publish/ 139 | 140 | # Publish Web Output 141 | *.[Pp]ublish.xml 142 | *.azurePubxml 143 | # TODO: Comment the next line if you want to checkin your web deploy settings 144 | # but database connection strings (with potential passwords) will be unencrypted 145 | *.pubxml 146 | *.publishproj 147 | 148 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 149 | # checkin your Azure Web App publish settings, but sensitive information contained 150 | # in these scripts will be unencrypted 151 | PublishScripts/ 152 | 153 | # NuGet Packages 154 | *.nupkg 155 | # The packages folder can be ignored because of Package Restore 156 | **/packages/* 157 | # except build/, which is used as an MSBuild target. 158 | !**/packages/build/ 159 | # Uncomment if necessary however generally it will be regenerated when needed 160 | #!**/packages/repositories.config 161 | # NuGet v3's project.json files produces more ignoreable files 162 | *.nuget.props 163 | *.nuget.targets 164 | 165 | # Microsoft Azure Build Output 166 | csx/ 167 | *.build.csdef 168 | 169 | # Microsoft Azure Emulator 170 | ecf/ 171 | rcf/ 172 | 173 | # Windows Store app package directories and files 174 | AppPackages/ 175 | BundleArtifacts/ 176 | Package.StoreAssociation.xml 177 | _pkginfo.txt 178 | 179 | # Visual Studio cache files 180 | # files ending in .cache can be ignored 181 | *.[Cc]ache 182 | # but keep track of directories ending in .cache 183 | !*.[Cc]ache/ 184 | 185 | # Others 186 | ClientBin/ 187 | ~$* 188 | *~ 189 | *.dbmdl 190 | *.dbproj.schemaview 191 | *.pfx 192 | *.publishsettings 193 | node_modules/ 194 | orleans.codegen.cs 195 | 196 | # Since there are multiple workflows, uncomment next line to ignore bower_components 197 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 198 | #bower_components/ 199 | 200 | # RIA/Silverlight projects 201 | Generated_Code/ 202 | 203 | # Backup & report files from converting an old project file 204 | # to a newer Visual Studio version. Backup files are not needed, 205 | # because we have git ;-) 206 | _UpgradeReport_Files/ 207 | Backup*/ 208 | UpgradeLog*.XML 209 | UpgradeLog*.htm 210 | 211 | # SQL Server files 212 | *.mdf 213 | *.ldf 214 | 215 | # Business Intelligence projects 216 | *.rdl.data 217 | *.bim.layout 218 | *.bim_*.settings 219 | 220 | # Microsoft Fakes 221 | FakesAssemblies/ 222 | 223 | # GhostDoc plugin setting file 224 | *.GhostDoc.xml 225 | 226 | # Node.js Tools for Visual Studio 227 | .ntvs_analysis.dat 228 | 229 | # Visual Studio 6 build log 230 | *.plg 231 | 232 | # Visual Studio 6 workspace options file 233 | *.opt 234 | 235 | # Visual Studio LightSwitch build output 236 | **/*.HTMLClient/GeneratedArtifacts 237 | **/*.DesktopClient/GeneratedArtifacts 238 | **/*.DesktopClient/ModelManifest.xml 239 | **/*.Server/GeneratedArtifacts 240 | **/*.Server/ModelManifest.xml 241 | _Pvt_Extensions 242 | 243 | # Paket dependency manager 244 | .paket/paket.exe 245 | paket-files/ 246 | 247 | # FAKE - F# Make 248 | .fake/ 249 | 250 | # JetBrains Rider 251 | .idea/ 252 | *.sln.iml 253 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Autodesk, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DWG Custom Data Viewer 2 | 3 | [![Design Automation](https://img.shields.io/badge/Design%20Automation-v2-green.svg)](http://developer.autodesk.com/) 4 | [![Viewer](https://img.shields.io/badge/Viewer-v2.14-green.svg)](http://developer.autodesk.com/) 5 | 6 | The sample code shows the combined usage of [DesignAutomation API](https://developer.autodesk.com/en/docs/design-automation/v2) and [the Viewer API](https://developer.autodesk.com/en/docs/viewer/v2/overview/) in a sample [application](http://viewersample.autocad.io/). The sample displays the usage of custom objects, object enablers using DesignAutomation API. 7 | 8 | ## Pre-requisites 9 | 1. [Visual Studio 2015](https://msdn.microsoft.com/en-us/default.aspx) 10 | 2. [Nodejs](https://nodejs.org/en/download/) 11 | 3. [Amazon Web Service access key and secret](https://aws.amazon.com/). 12 | 4. [Autodesk Developer access key and secret](https://developer.autodesk.com/). 13 | 14 | 15 | ## Setup 16 | 1. Verify that you have all the pre-requisites. 17 | 2. Open .\src\SampleApp.sln in VisualStudio 2015. 18 | 3. Build solution. 19 | 4. Open .\setup\init.bat in your favorite text editor and enter the values. All the values are needed to run the sample. 20 | 5. Open "Node.js command prompt" from the Windows Start Menu, change directory to .\setup\ 21 | 6. Run setup.bat 22 | 23 | ## Setup Workflow 24 | 1. This will run ClientApp executable. This will create 25 | - Zip package .\src\bin\\package.zip. 26 | - AppPackage using the DesignAutomation API. 27 | - Custom activities using the DesignAutomation API. 28 | 2. The dependencies are downloaded for the various projects 29 | 3. The config.js file is updated with data needed to run the lambdas. 30 | 4. The services is deployed using serverless framework 31 | - The lambdas are uploaded and configured. 32 | - APIs are created, configured using AWS API gateway to expose the lambdas. 33 | - A storage bucket is created and configured for processing dwg file. 34 | 5. The html file is updated with the endpoints from the API gateway. 35 | 6. A node server is started and the page is loaded from localhost on port 8080. 36 | 37 | The sample app has essentially three parts 38 | 39 | ## 1. ARX Application 40 | The ARX application is written in .NET, it has a command called ‘TEST’. The TEST command extracts any XData associated with the entities to a JSON file. 41 | There is also another command called ‘TESTPOLY’ that creates the polysamp custom object. The ARX along with the object enablers and managed wrappers for polysamp custom object is uploaded as part of a custom package using the DesignAutomation API, and run as part of a custom activity. The object enablers and the respective managed wrappers for the polysamp custom object have been pre-built. Please refer to the ObjectARX sdk for the source code. 42 | 43 | 44 | ## 2. AWS Lambdas 45 | The backend processing is implemented using server less paradigm using AWS lambdas. The lambdas are exposed to the client webpage using the AWS API gateway. 46 | 47 | There are four lambda functions implemented. 48 | - UploadLocation 49 | The function returns a pre-signed url location that the client can upload the drawing. 50 | 51 | - SubmitWorkItem 52 | The function takes the activity name, and the location of the drawing file as input. It checks that the custom package and the activity are present, and creates workitem using the DesignAutomation API. 53 | 54 | - WorkItemStatus 55 | The function takes the workitem identifier as the input, and checks the status of the respective workitem. The function uses the DesignAutomation API to poll the status. It returns immediately on success or failure of the workitem. If the workitem status is pending or in-progress, it loops through, polling the status every two seconds for up to twenty seconds and then returns. The reason it returns after twenty seconds is due to a thirty second timeout limit set by the AWS API gateway. If the API does not respond within thirty seconds, then the request is terminated and an error status of 504 is returned by the AWS API gateway. 56 | 57 | - ProcessResult 58 | The function takes the location of the result posted by DesignAutomation API for the workitem that was submitted. The result which is a compressed file containing the output is uncompressed by the function and uploaded to a unique location on S3. The location of the SVF/F2D and the XData files are returned. 59 | 60 | ## 3. Client Webpage 61 | The webpage has JavaScript functionality to invoke the API exposed by the lambdas. The local drawing selected by the user is uploaded using the AWS API gateway, and the workitem referencing the uploaded drawing is submitted. The Viewer API is used to show the resultant SVF/F2D file that was processed. Selecting the object in the Viewer will display any XData associated with the object in a properties panel, implemented using the Viewer API. 62 | 63 | 64 | ### Workflow: 65 | ![thumbnail](/thumbnail.png) 66 | 67 | 89 | 90 | 91 | ## Workflow1: 92 | 1. The drawing is selected in the webpage. 93 | 2. A pre-signed url is obtained from the UploadLocation lambda using the AWS gateway API for uploading the drawing, and it is uploaded. 94 | 3. The SubmitWorkItem lambda is invoked passing the location of the uploaded drawing, and the activity name. 95 | 4. The workitem referencing the custom activity is submitted for the drawing. 96 | 5. The workitem status is queried by the client if the workitem has been successfully submitted. The status is queried using the WorkItemStatus lambda. The lambda uses the DesignAutomation API to get the status and return it to the client. 97 | 6. On success, the workitem output location is returned to the client. 98 | 7. The ProcessResult lambda is invoked with the location of the output, which is a zip file. 99 | 8. The output file is uncompressed by the lambda, uploads it to S3. It returns the location of the SVF/F2D and XData files, which are among the uncompressed files. 100 | 9. A new browser window is launched passing the location of the files when the Viewer button is clicked. 101 | 10. The SVF/F2D files are displayed using the Viewer API in the Window. The Viewer API is used to implement the extension to display the XData of the object. 102 | 11. When an object is selected, the XData information is searched if the object has any associated XData using the object handle. If it has, then the values are displayed in a properties panel created using the Viewer API. 103 | 104 | ### Workflow2: 105 | 1. Create custom object is selected in the Webpage. 106 | 2. The SubmitWorkItem lambda is invoked passing the location of the empty drawing. 107 | 3. The workitem referencing the custom activity to create the custom object is submitted. 108 | 4. The workitem status is queried by the client if the workitem has been successfully submitted. The status is queried using the WorkItemStatus lambda. The lambda uses the DesignAutomation API to get the status and return it to the client. 109 | 5. On success, the workitem output location is returned to the client. 110 | 6. The ProcessResult lambda is invoked with the location of the output, which is a zip file. 111 | 7. The output file is uncompressed by the lambda, uploads it to S3. It returns the location of the SVF/F2D and XData files, which are among the uncompressed files. 112 | 8. A new browser window is launched passing the location of the files when the Viewer button is clicked. 113 | 9. The F2D files are displayed using the Viewer API in the Window. The Viewer API is used to implement the extension to display the XData of the object. 114 | 10. When an object is selected, the XData information is searched if the object has any associated XData using the object handle. If it has, then the values are displayed in a properties panel created using the Viewer API. 115 | 116 | -------------------------------------------------------------------------------- /server/npminstall.bat: -------------------------------------------------------------------------------- 1 | rem call npm install to download all the dependencies 2 | npm install 3 | 4 | -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ViewerSampleServer", 3 | "version": "0.1.0", 4 | "description": "ViewerSampleServer", 5 | "main": "setup.js", 6 | "author": { 7 | "name": "George Varghese", 8 | "email": "george.varghese@autodesk.com" 9 | }, 10 | "dependencies": { 11 | "express": ">= 4.12.3" 12 | } 13 | } 14 | 15 | -------------------------------------------------------------------------------- /server/server.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | 3 | var app = express(); 4 | app.use(function(req, res, next) { 5 | res.header("Access-Control-Allow-Origin", "*"); 6 | res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); 7 | next(); 8 | }); 9 | 10 | 11 | app.use('/', express.static(__dirname + '/../src/www')); 12 | app.set('port', 8080); 13 | 14 | var server = app.listen(app.get('port'), function() { 15 | console.log('Server listening on port ' + server.address().port); 16 | }); 17 | 18 | -------------------------------------------------------------------------------- /setup/init.bat: -------------------------------------------------------------------------------- 1 | REM Please enter a unique name for your service somemthing along the lines below 2 | set DA_VIEWER_SERVIVCE_NAME=youcompanyname-service-37a1eae178c3 3 | 4 | REM Please enter your AWS access key 5 | set AWS_ACCESS_KEY_ID= 6 | 7 | REM Please enter your AWS access key secret 8 | set AWS_SECRET_ACCESS_KEY= 9 | 10 | REM The region can be configured 11 | set AWS_DEFAULT_REGION=us-west-2 12 | 13 | REM Please enter a unique name for the bucket, something along the lines below 14 | set DA_VIEWER_DWG_BUCKET=youcompanyname-service-37a1eae178c3 15 | 16 | REM Please enter the Autodesk developer key 17 | set ADSK_DEVELOPER_KEY= 18 | 19 | REM Please enter the Autodesk developer key secret 20 | set ADSK_DEVELOPER_SECRET= 21 | 22 | -------------------------------------------------------------------------------- /setup/npminstall.bat: -------------------------------------------------------------------------------- 1 | rem call npm install to download all the dependencies 2 | npm install 3 | 4 | -------------------------------------------------------------------------------- /setup/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ViewerSampleSetup", 3 | "version": "0.1.0", 4 | "description": "ViewerSampleSetup", 5 | "main": "setup.js", 6 | "author": { 7 | "name": "George Varghese", 8 | "email": "george.varghese@autodesk.com" 9 | }, 10 | "dependencies": { 11 | "aws-sdk": "^2.2.48", 12 | "uuid": "^2.0.2" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /setup/setup.bat: -------------------------------------------------------------------------------- 1 | rem call init.bat 2 | call init.bat 3 | rem call clientApp 4 | ..\src\bin\Output\ClientApp.exe 5 | 6 | if errorlevel 0 goto success 7 | goto failed 8 | :success 9 | rem install dependencies 10 | call npminstall.bat 11 | rem npm install 12 | cd ..\src\lambda 13 | call npminstall.bat 14 | cd ..\..\server 15 | call npminstall.bat 16 | cd ..\setup 17 | 18 | rem deploying... 19 | call node setup.js 20 | 21 | rem run server... 22 | cd ..\server 23 | start call node server.js 24 | 25 | rem load the test page... 26 | cd ..\setup 27 | start "" http://localhost:8080 28 | 29 | goto done 30 | 31 | :failed 32 | echo setup failed 33 | 34 | :done 35 | 36 | -------------------------------------------------------------------------------- /setup/setup.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var fs = require("fs"); 4 | const exec = require("child_process").exec; 5 | 6 | var configfile = "..\\src\\lambda\\config.js"; 7 | var deployfile = "..\\src\\lambda\\deploy.bat"; 8 | var indexfile = "..\\src\\www\\index.html"; 9 | var startmarker = "endpoints:"; 10 | var endmarker = "functions:"; 11 | 12 | var variables = ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_DEFAULT_REGION", 13 | "DA_VIEWER_DWG_BUCKET", "ADSK_DEVELOPER_KEY", "ADSK_DEVELOPER_SECRET"]; 14 | 15 | // Updates the config file with actual values 16 | // 17 | function updateConfigFile(callback) { 18 | 19 | var file = fs.createReadStream(configfile, "utf8"); 20 | var newdata = ""; 21 | file.on("data", function (data) { 22 | var updatedata = data; 23 | variables.forEach(function (val) { 24 | updatedata = updatedata.toString().replace(val, process.env[val]); 25 | }); 26 | 27 | newdata += updatedata; 28 | }); 29 | 30 | file.on("end", function () { 31 | fs.writeFile(configfile, newdata, function (err) { 32 | if (err) { 33 | console.log(err); 34 | callback(false); 35 | return; 36 | } 37 | 38 | callback(true); 39 | }); 40 | }); 41 | } 42 | 43 | // Helper function to execute the batch file 44 | // 45 | function execbatchfile(filename, callback) { 46 | 47 | exec(filename, function (err, stdout) { 48 | if (err) { 49 | console.error(err); 50 | callback(false, null); 51 | return; 52 | } 53 | 54 | callback(true, stdout); 55 | }); 56 | } 57 | 58 | // Updates the webpage with the AWS API Gateway endpoints 59 | // 60 | function updateUrl(value, callback) { 61 | 62 | var outputtext = value.toString(); 63 | var start = outputtext.indexOf(startmarker) + startmarker.length; 64 | var end = outputtext.indexOf(endmarker); 65 | 66 | outputtext = outputtext.slice(start, end); 67 | outputtext = outputtext.trim(); 68 | 69 | var endpoints = outputtext.split("\n"); 70 | var uploadurl = endpoints[0].trim(); 71 | var workitemurl = endpoints[1].trim(); 72 | var workitemstatusurl = endpoints[2].trim(); 73 | var processurl = endpoints[3].trim(); 74 | 75 | uploadurl = uploadurl.slice(uploadurl.indexOf("-") + 1); 76 | uploadurl = uploadurl.trim(); 77 | workitemurl = workitemurl.slice(workitemurl.indexOf("-") + 1); 78 | workitemurl = workitemurl.trim(); 79 | workitemstatusurl = workitemstatusurl.slice(workitemstatusurl.indexOf("-") + 1); 80 | workitemstatusurl = workitemstatusurl.trim(); 81 | processurl = processurl.slice(processurl.indexOf("-") + 1); 82 | processurl = processurl.trim(); 83 | 84 | if (!uploadurl || !workitemurl || !workitemstatusurl || !processurl) { 85 | console.log("The endpoints could not be extracted"); 86 | callback(false); 87 | return; 88 | } 89 | 90 | var file = fs.createReadStream(indexfile, "utf8"); 91 | var newdata = ""; 92 | file.on("data", function (data) { 93 | var updatedata = data; 94 | updatedata = updatedata.toString().replace("UPLOAD_URL", uploadurl); 95 | updatedata = updatedata.toString().replace("WORKITEM_URL", workitemurl); 96 | updatedata = updatedata.toString().replace("WORKITEMSTATUS_URL", workitemstatusurl); 97 | updatedata = updatedata.toString().replace("PROCESS_URL", processurl); 98 | 99 | newdata += updatedata; 100 | }); 101 | 102 | file.on("end", function () { 103 | fs.writeFile(indexfile, newdata, function (err) { 104 | if (err) { 105 | console.log("Error updating index.html"); 106 | console.log(err); 107 | callback(false); 108 | return; 109 | } 110 | 111 | callback(true); 112 | }); 113 | }); 114 | } 115 | 116 | var initfunction = function () { 117 | console.log("Staring installation of the service..."); 118 | var initialized = true; 119 | variables.forEach(function (val) { 120 | if (!process.env[val]) { 121 | console.log(val + " environment variable not set"); 122 | initialized = false; 123 | } 124 | }); 125 | 126 | if (!initialized) { 127 | console.log("\n"); 128 | console.log("Please set the value of environment variables in init.bat, and run the batch file"); 129 | return; 130 | } 131 | 132 | console.log("Update the config file..."); 133 | // Update the config file 134 | // 135 | updateConfigFile(function (status) { 136 | if (!status) { 137 | return; 138 | } 139 | 140 | console.log("Deploying the service, this will take sometime"); 141 | // Start the deployment 142 | // 143 | execbatchfile(deployfile, function (status, text) { 144 | if (!status) { 145 | console.log("Error deploying to AWS"); 146 | return; 147 | } 148 | 149 | // Update the html file 150 | // 151 | var output = text.toString(); 152 | console.log(output); 153 | console.log("Update the html file..."); 154 | updateUrl(output, function (status) { 155 | if (status) { 156 | console.log("Successfully updated index.html"); 157 | } 158 | }); 159 | }); 160 | }); 161 | 162 | }; 163 | 164 | initfunction(); 165 | -------------------------------------------------------------------------------- /src/ClientApp/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/ClientApp/ClientApp.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {48A6117B-2E44-424C-828A-C60A955F6D12} 8 | Exe 9 | Properties 10 | ClientApp 11 | ClientApp 12 | v4.5.2 13 | 512 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | ..\bin\Output\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | ..\bin\Output\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | true 37 | ..\bin\Output\ 38 | DEBUG;TRACE 39 | full 40 | x64 41 | prompt 42 | MinimumRecommendedRules.ruleset 43 | true 44 | 45 | 46 | ..\bin\Output\ 47 | TRACE 48 | true 49 | pdbonly 50 | x64 51 | prompt 52 | MinimumRecommendedRules.ruleset 53 | true 54 | 55 | 56 | 57 | ..\packages\Microsoft.OData.Client.6.13.0\lib\net40\Microsoft.OData.Client.dll 58 | True 59 | 60 | 61 | ..\packages\Microsoft.OData.Core.6.13.0\lib\portable-net40+sl5+wp8+win8+wpa\Microsoft.OData.Core.dll 62 | True 63 | 64 | 65 | ..\packages\Microsoft.OData.Edm.6.13.0\lib\portable-net40+sl5+wp8+win8+wpa\Microsoft.OData.Edm.dll 66 | True 67 | 68 | 69 | ..\packages\Microsoft.Spatial.6.13.0\lib\portable-net40+sl5+wp8+win8+wpa\Microsoft.Spatial.dll 70 | True 71 | 72 | 73 | ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll 74 | True 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | True 88 | True 89 | ODataDesignAuto.tt 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | TextTemplatingFileGenerator 102 | ODataDesignAuto.cs 103 | 104 | 105 | Always 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | xcopy $(ProjectDir)..\polysamp\asdkpolyobj.dbx ..\Output\ /R /Y 114 | 115 | 122 | -------------------------------------------------------------------------------- /src/ClientApp/ODataDesignAuto.tt: -------------------------------------------------------------------------------- 1 | <#@ include file="ODataDesignAuto.ttinclude" #> 2 | <#+ 3 | public static class Configuration 4 | { 5 | // The URI of the metadata document. The value must be set to a valid service document URI or a local file path 6 | // eg : "http://services.odata.org/V4/OData/OData.svc/", "File:///C:/Odata.edmx", or @"C:\Odata.edmx" 7 | // ### Notice ### If the OData service requires authentication for accessing the metadata document, the value of 8 | // MetadataDocumentUri has to be set to a local file path, or the client code generation process will fail. 9 | public const string MetadataDocumentUri = "https://developer.api.autodesk.com/autocad.io/us-east/v2/"; 10 | 11 | // The use of DataServiceCollection enables entity and property tracking. The value must be set to true or false. 12 | public const bool UseDataServiceCollection = true; 13 | 14 | // The namespace of the client code generated. It replaces the original namespace in the metadata document, 15 | // unless the model has several namespaces. 16 | public const string NamespacePrefix = "ClientApp"; 17 | 18 | // The target language of the generated client code. The value must be set to "CSharp" or "VB". 19 | public const string TargetLanguage = "CSharp"; 20 | 21 | // This flag indicates whether to enable naming alias. The value must be set to true or false. 22 | public const bool EnableNamingAlias = true; 23 | 24 | // This flag indicates whether to ignore unexpected elements and attributes in the metadata document and generate 25 | // the client code if any. The value must be set to true or false. 26 | public const bool IgnoreUnexpectedElementsAndAttributes = true; 27 | } 28 | 29 | public static class Customization 30 | { 31 | /// 32 | /// Changes the text to use upper camel case, which upper case for the first character. 33 | /// 34 | /// Text to convert. 35 | /// The converted text in upper camel case 36 | internal static string CustomizeNaming(string text) 37 | { 38 | if (string.IsNullOrEmpty(text)) 39 | { 40 | return text; 41 | } 42 | 43 | if (text.Length == 1) 44 | { 45 | return Char.ToUpperInvariant(text[0]).ToString(CultureInfo.InvariantCulture); 46 | } 47 | 48 | return Char.ToUpperInvariant(text[0]) + text.Substring(1); 49 | } 50 | 51 | /// 52 | /// Changes the namespace to use upper camel case, which upper case for the first character of all segments. 53 | /// 54 | /// Namespace to convert. 55 | /// The converted namespace in upper camel case 56 | internal static string CustomizeNamespace(string fullNamespace) 57 | { 58 | if (string.IsNullOrEmpty(fullNamespace)) 59 | { 60 | return fullNamespace; 61 | } 62 | 63 | string[] segs = fullNamespace.Split('.'); 64 | string upperNamespace = string.Empty; 65 | int n = segs.Length; 66 | for (int i = 0; i < n; ++i) 67 | { 68 | upperNamespace += Customization.CustomizeNaming(segs[i]); 69 | upperNamespace += (i == n - 1 ? string.Empty : "."); 70 | } 71 | 72 | return upperNamespace; 73 | } 74 | } 75 | #> 76 | -------------------------------------------------------------------------------- /src/ClientApp/PackageContents.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 16 | 17 | 18 | 21 | 26 | 32 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/ClientApp/Program.cs: -------------------------------------------------------------------------------- 1 | using ClientApp.ACES.Models; 2 | using ClientApp.Operations; 3 | using Newtonsoft.Json; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.IO.Compression; 8 | using System.Linq; 9 | using System.Net.Http; 10 | using System.Text; 11 | using System.Text.RegularExpressions; 12 | using System.Threading.Tasks; 13 | 14 | namespace ClientApp 15 | { 16 | class Credentials 17 | { 18 | // Get your ConsumerKey/ConsumerSecret from https://developer.autodesk.com 19 | // 20 | public static string baseUrl = "https://developer.api.autodesk.com"; 21 | } 22 | 23 | class Program 24 | { 25 | static readonly string PackageName = "MyCustomPackage"; 26 | static readonly string zip = "package.zip"; 27 | static readonly string ActivityName2d = "MyPublishActivity2d"; 28 | static readonly string ActivityName3d = "MyPublishActivity3d"; 29 | static readonly string ActivityNameCustom = "MyPublishActivityCustom"; 30 | static readonly string Script2d = "_test ./result/xdata.json\r\n_prepareforpropertyextraction index.json\r\n_indexextractor index.json\r\n_publishtof2d ./output\r\n_createbubblepackage ./output ./result \r\n\n"; 31 | static readonly string Script3d = "_test ./result/xdata.json\r\n_prepareforpropertyextraction index.json\r\n_indexextractor index.json\r\n_publishtosvf ./output/result.svf\r\n_createbubblepackage ./output ./result \r\n\n"; 32 | static readonly string ScriptCustom = "_testpoly\r\n_save \r\n_test ./result/xdata.json\r\n_prepareforpropertyextraction index.json\r\n_indexextractor index.json\r\n_publishtof2d ./output\r\n_createbubblepackage ./output ./result \r\n\n"; 33 | 34 | static readonly string RequiredEngineVersion = "22.0"; 35 | 36 | static int Main(string[] args) 37 | { 38 | var consumerKey = Environment.GetEnvironmentVariable("ADSK_DEVELOPER_KEY"); 39 | var consumerSecret = Environment.GetEnvironmentVariable("ADSK_DEVELOPER_SECRET"); 40 | 41 | if (string.IsNullOrEmpty(consumerKey) || string.IsNullOrEmpty(consumerSecret)) 42 | { 43 | Console.WriteLine("Please enter valid Autodesk consumer key secret"); 44 | return 1; 45 | } 46 | 47 | var token = GetToken(consumerKey, consumerSecret); 48 | if (token == null) 49 | { 50 | Console.WriteLine("Error obtaining access token"); 51 | return 1; 52 | } 53 | 54 | string url = Credentials.baseUrl + "/autocad.io/us-east/v2/"; 55 | Container container = new Container(new Uri(url)); 56 | container.SendingRequest2 += (sender, e) => e.RequestMessage.SetHeader("Authorization", token); 57 | 58 | // Create the app package if it does not exist 59 | AppPackage package = CreatePackage(container); 60 | if (package == null) 61 | { 62 | Console.WriteLine("Error creating app package"); 63 | return 1; 64 | } 65 | 66 | // Create the custom activity 67 | // 68 | Activity activity = CreateActivity(ActivityName2d, Script2d, container); 69 | if (activity == null) 70 | { 71 | Console.WriteLine("Error creating custom activity: " + ActivityName2d); 72 | return 1; 73 | } 74 | 75 | activity = CreateActivity(ActivityName3d, Script3d, container); 76 | if (activity == null) 77 | { 78 | Console.WriteLine("Error creating custom activity: " + ActivityName3d); 79 | return 1; 80 | } 81 | 82 | activity = CreateActivity(ActivityNameCustom, ScriptCustom, container); 83 | if (activity == null) 84 | { 85 | Console.WriteLine("Error creating custom activity: " + ActivityNameCustom); 86 | return 1; 87 | } 88 | 89 | // Save changes if any 90 | container.SaveChanges(); 91 | 92 | return 0; 93 | } 94 | 95 | public static string GetToken(string consumerKey, string consumerSecret) 96 | { 97 | using (var client = new HttpClient()) 98 | { 99 | var values = new List>(); 100 | values.Add(new KeyValuePair("client_id", consumerKey)); 101 | values.Add(new KeyValuePair("client_secret", consumerSecret)); 102 | values.Add(new KeyValuePair("grant_type", "client_credentials")); 103 | values.Add(new KeyValuePair("scope", "code:all")); 104 | 105 | var requestContent = new FormUrlEncodedContent(values); 106 | string url = Credentials.baseUrl + "/authentication/v1/authenticate"; 107 | var response = client.PostAsync(url, requestContent).Result; 108 | var responseContent = response.Content.ReadAsStringAsync().Result; 109 | var resValues = JsonConvert.DeserializeObject>(responseContent); 110 | return resValues["token_type"] + " " + resValues["access_token"]; 111 | } 112 | } 113 | 114 | static AppPackage CreatePackage(Container container) 115 | { 116 | AppPackage package = null; 117 | try 118 | { 119 | package = container.AppPackages.ByKey(PackageName).GetValue(); 120 | } 121 | catch 122 | { 123 | } 124 | if (package != null) 125 | { 126 | string res = null; 127 | res = Prompts.PromptForKeyword(string.Format("AppPackage '{0}' already exists. What do you want to do? [Recreate/Update/Leave]", PackageName)); 128 | if (res == "Recreate") 129 | { 130 | container.DeleteObject(package); 131 | container.SaveChanges(); 132 | package = null; 133 | } 134 | if (res == "Leave") 135 | return package; 136 | } 137 | 138 | var dir = Path.GetDirectoryName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName); 139 | if (!File.Exists(Path.Combine(dir, "RxApp.dll"))) 140 | { 141 | Console.WriteLine("Error building RxApp.dll"); 142 | return null; 143 | } 144 | if (!File.Exists(Path.Combine(dir, "asdkmgPoly.dll"))) 145 | { 146 | Console.WriteLine("Error building asdkmgPoly.dll"); 147 | return null; 148 | } 149 | if (!File.Exists(Path.Combine(dir, "asdkpolyobj.dbx"))) 150 | { 151 | Console.WriteLine("Error building asdkpolyobj.dbx"); 152 | return null; 153 | } 154 | 155 | string zipfile = CreateZip(); 156 | if (string.IsNullOrEmpty(zipfile)) 157 | { 158 | Console.WriteLine("Error creating package.zip"); 159 | return null; 160 | } 161 | Console.WriteLine("The package.zip file was successfully created"); 162 | 163 | Console.WriteLine("Creating/Updating AppPackage..."); 164 | // Query for the url to upload the AppPackage file 165 | var url = container.AppPackages.GetUploadUrl().GetValue(); 166 | var client = new HttpClient(); 167 | // Upload AppPackage file 168 | client.PutAsync(url, new StreamContent(File.OpenRead(zip))).Result.EnsureSuccessStatusCode(); 169 | 170 | // third step -- after upload, create the AppPackage entity 171 | if (package == null) 172 | { 173 | package = new AppPackage() 174 | { 175 | Id = PackageName, 176 | RequiredEngineVersion = RequiredEngineVersion, 177 | Resource = url 178 | }; 179 | container.AddToAppPackages(package); 180 | } 181 | else 182 | { 183 | package.Resource = url; 184 | container.UpdateObject(package); 185 | } 186 | 187 | container.SaveChanges(); 188 | return package; 189 | } 190 | 191 | 192 | static Activity CreateActivity(string activityname, string script, Container container) 193 | { 194 | Activity activity = null; 195 | try { activity = container.Activities.ByKey(activityname).GetValue(); } 196 | catch { } 197 | if (activity != null) 198 | { 199 | if (Prompts.PromptForKeyword(string.Format("Activity '{0}' already exists. Do you want to recreate it? [Yes/No]", activityname)) == "Yes") 200 | { 201 | container.DeleteObject(activity); 202 | container.SaveChanges(); 203 | activity = null; 204 | } 205 | else 206 | { 207 | return activity; 208 | } 209 | } 210 | 211 | Console.WriteLine("Creating/Updating Activity..."); 212 | activity = new Activity() 213 | { 214 | Id = activityname, 215 | Instruction = new Instruction() 216 | { 217 | Script = script 218 | }, 219 | Parameters = new Parameters() 220 | { 221 | InputParameters = { 222 | new Parameter() { Name = "HostDwg", LocalFileName = "$(HostDwg)" } 223 | }, 224 | OutputParameters = { new Parameter() { Name = "Results", LocalFileName = "result" } } 225 | }, 226 | RequiredEngineVersion = RequiredEngineVersion 227 | }; 228 | activity.AppPackages.Add(PackageName); 229 | activity.AppPackages.Add("Publish2View22"); // reference the custom AppPackage 230 | container.AddToActivities(activity); 231 | container.SaveChanges(); 232 | return activity; 233 | } 234 | 235 | 236 | static string CreateZip() 237 | { 238 | Console.WriteLine("Generating autoloader zip..."); 239 | if (File.Exists(zip)) 240 | File.Delete(zip); 241 | var dir = Path.GetDirectoryName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName); 242 | using (var archive = ZipFile.Open(zip, ZipArchiveMode.Create)) 243 | { 244 | string bundle = PackageName + ".bundle"; 245 | string name = "PackageContents.xml"; 246 | archive.CreateEntryFromFile(Path.Combine(dir,name), Path.Combine(bundle, name)); 247 | name = "RxApp.dll"; 248 | archive.CreateEntryFromFile(Path.Combine(dir, name), Path.Combine(bundle, "Contents", name)); 249 | name = "asdkmgPoly.dll"; 250 | archive.CreateEntryFromFile(Path.Combine(dir, name), Path.Combine(bundle, "Contents", name)); 251 | name = "asdkpolyobj.dbx"; 252 | archive.CreateEntryFromFile(Path.Combine(dir, name), Path.Combine(bundle, "Contents", name)); 253 | } 254 | return zip; 255 | } 256 | } 257 | 258 | class Prompts 259 | { 260 | class KeywordPrompt 261 | { 262 | string[] m_keywords; 263 | string m_default; 264 | static Regex s_reg = new Regex(".*\\[(?.*)\\]\\<(?.*)\\>$"); 265 | public KeywordPrompt(string prompt) 266 | { 267 | var match = s_reg.Match(prompt); 268 | if (!match.Success) 269 | throw new ArgumentException(); 270 | m_keywords = match.Groups["keywords"].Value.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries); 271 | m_default = match.Groups["default"].Value; 272 | string dummy; 273 | if (string.IsNullOrEmpty(m_default) || !TryMatch(m_default, out dummy)) 274 | throw new ArgumentException(); 275 | } 276 | public bool TryMatch(string str, out string match) 277 | { 278 | if (string.IsNullOrEmpty(str)) 279 | { 280 | match = m_default; 281 | return true; 282 | } 283 | match = null; 284 | var matches = m_keywords.Where(s => s.StartsWith(str, StringComparison.CurrentCultureIgnoreCase)); 285 | if (matches.Count() != 1) 286 | return false; 287 | match = matches.First(); 288 | return true; 289 | } 290 | } 291 | public static string PromptForKeyword(string promptString) 292 | { 293 | var prompt = new KeywordPrompt(promptString); 294 | while (true) 295 | { 296 | Console.Write("{0}:", promptString); 297 | var resp = Console.ReadLine(); 298 | string ret; 299 | if (prompt.TryMatch(resp, out ret)) 300 | return ret; 301 | } 302 | 303 | } 304 | } 305 | 306 | } 307 | -------------------------------------------------------------------------------- /src/ClientApp/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("ClientApp")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ClientApp")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("48a6117b-2e44-424c-828a-c60a955f6d12")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /src/ClientApp/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/RxApp/Commands.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Autodesk.AutoCAD.Runtime; 7 | using Autodesk.AutoCAD.ApplicationServices.Core; 8 | using Autodesk.AutoCAD.DatabaseServices; 9 | using System.Collections.ObjectModel; 10 | using Autodesk.AutoCAD.EditorInput; 11 | using System.IO; 12 | using System.Runtime.Serialization.Json; 13 | using Autodesk.AutoCAD.Geometry; 14 | using Autodesk.AutoCAD.ApplicationServices; 15 | 16 | [assembly: CommandClass(typeof(RxApp.Commands))] 17 | [assembly: ExtensionApplication(null)] 18 | 19 | namespace RxApp 20 | { 21 | public class Commands 22 | { 23 | [CommandMethod("MyTestCommand", "test", CommandFlags.Modal)] 24 | static public void Test() 25 | { 26 | var doc = Application.DocumentManager.MdiActiveDocument; 27 | var ed = doc.Editor; 28 | 29 | PromptResult pr = ed.GetString("Specify output filename:"); 30 | if (pr.Status == Autodesk.AutoCAD.EditorInput.PromptStatus.Cancel) 31 | { 32 | ed.WriteMessage("Command cancelled"); 33 | return; 34 | } 35 | string outputFile = pr.StringResult; 36 | if (string.IsNullOrEmpty(outputFile)) 37 | { 38 | ed.WriteMessage("Error: Invalid output file"); 39 | return; 40 | } 41 | 42 | try 43 | { 44 | Collection xdata = new Collection(); 45 | 46 | Database db = doc.Database; 47 | ObjectId modId = SymbolUtilityServices.GetBlockModelSpaceId(db); 48 | using (Transaction trans = doc.TransactionManager.StartTransaction()) 49 | { 50 | BlockTableRecord btr = trans.GetObject(modId, OpenMode.ForRead) as BlockTableRecord; 51 | foreach (ObjectId entId in btr) 52 | { 53 | DBObject obj = trans.GetObject(entId, OpenMode.ForRead); 54 | ResultBuffer rb = obj.XData; 55 | if (rb == null) 56 | continue; 57 | 58 | DBObjectInfo objInfo = new DBObjectInfo(); 59 | objInfo.Id = obj.Handle.ToString(); 60 | string strValue; 61 | foreach (TypedValue tvalue in rb) 62 | { 63 | strValue = ""; 64 | if (tvalue.TypeCode == 1004) 65 | { 66 | string hexvalue = BitConverter.ToString(tvalue.Value as byte[]); 67 | hexvalue = hexvalue.Replace("-", ""); 68 | strValue = hexvalue; 69 | } 70 | else 71 | { 72 | strValue = string.Format("{0}", tvalue.Value); 73 | } 74 | 75 | DataValue dataVal = new DataValue(); 76 | dataVal.Type = tvalue.TypeCode; 77 | dataVal.Value = strValue; 78 | 79 | objInfo.XData.Add(dataVal); 80 | } 81 | rb.Dispose(); 82 | 83 | xdata.Add(objInfo); 84 | } 85 | } 86 | 87 | if (xdata.Count <= 0) 88 | { 89 | ed.WriteMessage("There are no XData associated with entities in this drawing."); 90 | return; 91 | } 92 | 93 | Directory.CreateDirectory(Path.GetDirectoryName(outputFile)); 94 | using (var stream = File.Create(outputFile)) 95 | { 96 | DataContractJsonSerializer ser = new DataContractJsonSerializer(xdata.GetType()); 97 | ser.WriteObject(stream, xdata); 98 | } 99 | 100 | } 101 | catch (System.Exception e) 102 | { 103 | ed.WriteMessage("Error: {0}", e); 104 | } 105 | } 106 | 107 | [CommandMethod("MyTestCommand", "testpoly", CommandFlags.Modal)] 108 | static public void Poly() 109 | { 110 | Document doc = Application.DocumentManager.MdiActiveDocument; 111 | using (DocumentLock l = doc.LockDocument()) 112 | { 113 | using (Transaction t = doc.Database.TransactionManager.StartTransaction()) 114 | { 115 | BlockTable bt = (BlockTable)t.GetObject(doc.Database.BlockTableId, OpenMode.ForRead); 116 | BlockTableRecord btr = (BlockTableRecord)t.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite); 117 | using (Autodesk.ObjectDbxSample.Poly poly = new Autodesk.ObjectDbxSample.Poly()) 118 | { 119 | poly.Center = new Point2d(100, 100); 120 | poly.StartPoint2d = new Point2d(300, 100); 121 | poly.NumberOfSides = 5; 122 | poly.Name = "Managed Poly"; 123 | 124 | btr.AppendEntity(poly); 125 | t.AddNewlyCreatedDBObject(poly, true); 126 | 127 | AddRegAppTableRecord("IOVIEWERSAMPLE"); 128 | ResultBuffer rb = new ResultBuffer(new TypedValue(1001, "IOVIEWERSAMPLE"), 129 | new TypedValue(1000, "This is a sample string")); 130 | 131 | poly.XData = rb; 132 | rb.Dispose(); 133 | } 134 | t.Commit(); 135 | } 136 | } 137 | } 138 | 139 | static void AddRegAppTableRecord(string regAppName) 140 | { 141 | Document doc = Application.DocumentManager.MdiActiveDocument; 142 | Editor ed = doc.Editor; 143 | Database db = doc.Database; 144 | using (Transaction tr = doc.TransactionManager.StartTransaction()) 145 | { 146 | RegAppTable regAppTable = (RegAppTable)tr.GetObject(db.RegAppTableId, OpenMode.ForRead, false); 147 | if (!regAppTable.Has(regAppName)) 148 | { 149 | regAppTable.UpgradeOpen(); 150 | RegAppTableRecord regAppTableRecord = new RegAppTableRecord(); 151 | regAppTableRecord.Name = regAppName; 152 | regAppTable.Add(regAppTableRecord); 153 | tr.AddNewlyCreatedDBObject(regAppTableRecord, true); 154 | } 155 | tr.Commit(); 156 | } 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/RxApp/DBObjectInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.Linq; 5 | using System.Runtime.Serialization; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace RxApp 10 | { 11 | [DataContract] 12 | class DBObjectInfo 13 | { 14 | Collection coll = new Collection(); 15 | public DBObjectInfo() 16 | { 17 | 18 | } 19 | 20 | [DataMember] 21 | public string Id { get; set; } 22 | 23 | [DataMember] 24 | public Collection XData 25 | { 26 | get 27 | { 28 | return coll; 29 | } 30 | } 31 | } 32 | 33 | [DataContract] 34 | class DataValue 35 | { 36 | public DataValue() 37 | { 38 | 39 | } 40 | 41 | [DataMember] 42 | public short Type { get; set; } 43 | 44 | [DataMember] 45 | public string Value { get; set; } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/RxApp/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("RxApp")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("RxApp")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("24016c00-90d3-44dc-95e4-7f93f5cf2b09")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /src/RxApp/RxApp.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {24016C00-90D3-44DC-95E4-7F93F5CF2B09} 8 | Library 9 | Properties 10 | RxApp 11 | RxApp 12 | v4.5.2 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | ..\bin\Output\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | ..\bin\Output\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | true 34 | ..\bin\Output\ 35 | DEBUG;TRACE 36 | full 37 | x64 38 | prompt 39 | MinimumRecommendedRules.ruleset 40 | 41 | 42 | ..\bin\Output\ 43 | TRACE 44 | true 45 | pdbonly 46 | x64 47 | prompt 48 | MinimumRecommendedRules.ruleset 49 | 50 | 51 | true 52 | ..\bin\Output\ 53 | DEBUG;TRACE 54 | full 55 | x86 56 | prompt 57 | MinimumRecommendedRules.ruleset 58 | 59 | 60 | ..\bin\Output\ 61 | TRACE 62 | true 63 | pdbonly 64 | x86 65 | prompt 66 | MinimumRecommendedRules.ruleset 67 | 68 | 69 | 70 | ..\packages\AutoCAD.NET.Core.21.0.1\lib\45\AcCoreMgd.dll 71 | True 72 | 73 | 74 | ..\packages\AutoCAD.NET.Model.21.0.0\lib\45\AcDbMgd.dll 75 | True 76 | 77 | 78 | ..\packages\AutoCAD.NET.Model.21.0.0\lib\45\AcDbMgdBrep.dll 79 | True 80 | 81 | 82 | False 83 | ..\polysamp\asdkmgPoly.dll 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 111 | -------------------------------------------------------------------------------- /src/RxApp/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/SampleApp.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClientApp", "ClientApp\ClientApp.csproj", "{48A6117B-2E44-424C-828A-C60A955F6D12}" 7 | ProjectSection(ProjectDependencies) = postProject 8 | {24016C00-90D3-44DC-95E4-7F93F5CF2B09} = {24016C00-90D3-44DC-95E4-7F93F5CF2B09} 9 | EndProjectSection 10 | EndProject 11 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RxApp", "RxApp\RxApp.csproj", "{24016C00-90D3-44DC-95E4-7F93F5CF2B09}" 12 | EndProject 13 | Global 14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 15 | Debug|Any CPU = Debug|Any CPU 16 | Release|Any CPU = Release|Any CPU 17 | EndGlobalSection 18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 19 | {48A6117B-2E44-424C-828A-C60A955F6D12}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 20 | {48A6117B-2E44-424C-828A-C60A955F6D12}.Debug|Any CPU.Build.0 = Debug|Any CPU 21 | {48A6117B-2E44-424C-828A-C60A955F6D12}.Release|Any CPU.ActiveCfg = Release|Any CPU 22 | {48A6117B-2E44-424C-828A-C60A955F6D12}.Release|Any CPU.Build.0 = Release|Any CPU 23 | {24016C00-90D3-44DC-95E4-7F93F5CF2B09}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {24016C00-90D3-44DC-95E4-7F93F5CF2B09}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {24016C00-90D3-44DC-95E4-7F93F5CF2B09}.Release|Any CPU.ActiveCfg = Release|Any CPU 26 | {24016C00-90D3-44DC-95E4-7F93F5CF2B09}.Release|Any CPU.Build.0 = Release|Any CPU 27 | EndGlobalSection 28 | GlobalSection(SolutionProperties) = preSolution 29 | HideSolutionNode = FALSE 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /src/lambda/.serverless/cloudformation-template-create-stack.json: -------------------------------------------------------------------------------- 1 | { 2 | "AWSTemplateFormatVersion": "2010-09-09", 3 | "Description": "The AWS CloudFormation template for this Serverless application", 4 | "Resources": { 5 | "ServerlessDeploymentBucket": { 6 | "Type": "AWS::S3::Bucket" 7 | } 8 | }, 9 | "Outputs": { 10 | "ServerlessDeploymentBucketName": { 11 | "Value": { 12 | "Ref": "ServerlessDeploymentBucket" 13 | } 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/lambda/app.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var AWS = require("aws-sdk"); 4 | var request = require("request"); 5 | var uuid = require("uuid"); 6 | var unzip = require("unzip"); 7 | var async = require("async"); 8 | var stream = require("stream"); 9 | var path = require("path"); 10 | 11 | // local files 12 | var config = require("./config.js"); 13 | 14 | 15 | 16 | // Helper functions 17 | // 18 | 19 | // Sends a request with the authorization token 20 | // 21 | function sendAuthData(uri, httpmethod, token, data, callback) { 22 | 23 | var requestbody = ""; 24 | if (data) { 25 | requestbody = JSON.stringify(data); 26 | } 27 | 28 | request({ 29 | url: uri, 30 | method: httpmethod, 31 | headers: { 32 | "Content-Type": "application/json", 33 | "Authorization": token 34 | }, 35 | body: requestbody 36 | }, function (error, response, body) { 37 | if (callback) { 38 | callback(error || response.statusCode, body); 39 | } else { 40 | if (error) { 41 | console.log(error); 42 | } else { 43 | console.log(response.statusCode, body); 44 | } 45 | } 46 | }); 47 | } 48 | 49 | // Helper function to check if the given app package is available 50 | // 51 | function isPackageAvailable(url, token, packagename, callback) { 52 | var uri = url + "(\'" + packagename + "\')"; 53 | sendAuthData(uri, "GET", token, null, function (status) { 54 | if (status === 200) { 55 | callback(true); 56 | } 57 | else { 58 | callback(false); 59 | } 60 | }); 61 | } 62 | 63 | // The function checks if the activity with the given name is available 64 | // 65 | function isActivityAvailable(url, token, activityname, callback) { 66 | var uri = url + "(\'" + activityname + "\')"; 67 | sendAuthData(uri, "GET", token, null, function (status) { 68 | if (status === 200) { 69 | callback(true); 70 | } 71 | else { 72 | callback(false); 73 | } 74 | }); 75 | } 76 | 77 | // The functions submits a workitem using the Design Automation API. 78 | // 79 | function submitWorkItem(url, token, activityname, resource, callback) { 80 | var workitembody = { 81 | "ActivityId": activityname, 82 | "Arguments": { 83 | "InputArguments": [ 84 | { 85 | "Name": "HostDwg", 86 | "Resource": resource, 87 | "StorageProvider": "Generic" 88 | } 89 | ], 90 | "OutputArguments": [ 91 | { 92 | "HttpVerb": "POST", 93 | "Name": "Results", 94 | "Resource": null, 95 | "ResourceKind": "ZipPackage", 96 | "StorageProvider": "Generic" 97 | } 98 | ] 99 | } 100 | }; 101 | 102 | sendAuthData(url, "POST", token, workitembody, function (status, param) { 103 | if (status === 200 || status === 201) { 104 | console.log("Submitted work item"); 105 | var paramObj = JSON.parse(param); 106 | callback(true, paramObj.Id); 107 | } else { 108 | console.log("Error: submitting workitem"); 109 | callback(false); 110 | } 111 | }); 112 | } 113 | 114 | // The function creates the app package and the custom activity if not available and then submits the 115 | // workitem for the given drawing. 116 | // 117 | function submitItem(token, dwgLocation, activityname, callback) { 118 | 119 | isPackageAvailable(config.package_endpoint, token, config.package_name, function (status) { 120 | if (status) { 121 | isActivityAvailable(config.activity_endpoint, token, activityname, function (status) { 122 | if (status) { 123 | submitWorkItem(config.workitem_endpoint, token, activityname, dwgLocation, function (ret, workitemId) { 124 | if (ret) { 125 | console.log("Sucessfully submitted the workitem"); 126 | callback(true, workitemId); 127 | } else { 128 | console.log("Error: submitting the workitem"); 129 | callback(false); 130 | } 131 | }); 132 | } else { 133 | console.log("Error: Activity " + activityname + " not available "); 134 | callback(false); 135 | return; 136 | } 137 | }); 138 | } else { 139 | console.log("Error: AppPackage " + config.package_name + " not available "); 140 | callback(false); 141 | return; 142 | } 143 | }); 144 | } 145 | 146 | // Helper function to download binary data 147 | // 148 | function getBinaryData(uri, callback) { 149 | request({ 150 | url: uri, 151 | method: "GET", 152 | encoding: null 153 | }, function (error, response, body) { 154 | if (callback) { 155 | callback(error || response.statusCode, body); 156 | } else { 157 | if (error) { 158 | console.log(error); 159 | } else { 160 | console.log(response.statusCode, body); 161 | } 162 | } 163 | }); 164 | } 165 | 166 | // The function uncompresses the data, and upload the result to S3. 167 | // In the process it also stores the location of SVF/F2D and xdata, 168 | // and returns it to the caller. 169 | // 170 | function uploadResultToS3(body, folderName, callback) { 171 | 172 | var s3 = new AWS.S3({ accessKeyId: config.aws_key, secretAccessKey: config.aws_secret }); 173 | 174 | var resultLocation; 175 | var location; 176 | var customdatalocation; 177 | var count = 0; 178 | var bufferStream = new stream.PassThrough(); 179 | bufferStream.end(body); 180 | bufferStream.pipe(unzip.Parse()).on("entry", function (entry) { 181 | ++count; 182 | var fileName = entry.path; 183 | 184 | var index = fileName.indexOf("\\"); 185 | if (index !== -1) { 186 | fileName = fileName.replace("\\", "/"); 187 | } 188 | 189 | var key = folderName + fileName; 190 | 191 | var params = { Bucket: config.aws_s3_bucket, Key: key, ACL: "public-read", Body: entry }; 192 | s3.upload(params, function (err, data) { 193 | if (err) { 194 | console.log("Error: uploading to S3" + ": " + err); 195 | entry.autodrain(); 196 | callback(false); 197 | } else { 198 | resultLocation = path.parse(data.Location); 199 | if (resultLocation) { 200 | if (resultLocation.ext.toUpperCase() === ".SVF") { 201 | location = data.Location; 202 | } else if (resultLocation.ext.toUpperCase() === ".F2D") { 203 | location = data.Location; 204 | } else { 205 | if (resultLocation.base.toUpperCase() === "XDATA.JSON") { 206 | customdatalocation = data.Location; 207 | } 208 | } 209 | } 210 | count--; 211 | console.log("Successfully uploaded " + count); 212 | if (count === 0) { 213 | callback(true, location, customdatalocation); 214 | } 215 | } 216 | }); 217 | }); 218 | } 219 | 220 | // Get the result and process it. 221 | // 222 | function uploadWorkItemResult(url, folderName, callback) { 223 | if (!url) { 224 | callback(false); 225 | } 226 | 227 | getBinaryData(url, function (status, body) { 228 | if (status === 200) { 229 | uploadResultToS3(body, folderName, function (status, resultlocation, xdatalocation) { 230 | if (status) { 231 | callback(true, resultlocation, xdatalocation); 232 | } else { 233 | console.log("Error: failed to upload the results to S3"); 234 | callback(false); 235 | } 236 | }); 237 | } 238 | else { 239 | callback(false); 240 | } 241 | }); 242 | } 243 | 244 | // The function gets the status of the workitem using the DesignAutomation API, 245 | // loops through to a count of ten if the status is pending with a time interval 246 | // of two seconds. The AWS API gateway timesout at 30s, and so it keeps the loop 247 | // below thirty seconds after which it returns the pending status to the client. 248 | // It returns the status immediately on success or failure. 249 | // 250 | function getWorkItemStatus(url, token, callback) { 251 | var count = 0; 252 | async.forever(function (next) { 253 | setTimeout(sendAuthData(url, "GET", token, null, function (status, body) { 254 | if (status === 200) { 255 | var result = JSON.parse(body); 256 | if (result.Status === "Pending" || result.Status === "InProgress") { 257 | if (count > 10) { 258 | // Exit at count 10, we do not want to run over 20s 259 | next({ Status: true, StatusText: "Pending" }); 260 | return; 261 | } 262 | 263 | next(); 264 | ++count; 265 | } else if (result.Status === "Succeeded") { 266 | next({ Status: true, StatusText: "Succeeded", Result: { Output: result.Arguments.OutputArguments[0].Resource, Report: result.StatusDetails.Report } }); 267 | } 268 | else { 269 | next({ Status: true, StatusText: "Failed", Result: { Report: result.StatusDetails.Report } }); 270 | } 271 | } 272 | else { 273 | next({ Status: false, StatusText: "Failed", Result: body }); 274 | } 275 | }), 2000); 276 | }, function (obj) { 277 | if (obj.Status) { 278 | callback(true, obj.StatusText, obj.Result); 279 | } else { 280 | callback(false, obj.StatusText, obj.Result); 281 | } 282 | }); 283 | } 284 | 285 | 286 | // Helper function to get the access token 287 | // 288 | function requestToken(authurl, key, secret, callback) { 289 | request({ 290 | url: authurl, 291 | method: "POST", 292 | headers: { 293 | "Content-Type": "application/x-www-form-urlencoded" 294 | }, 295 | body: "client_id=" + key + "&client_secret=" + secret + "&grant_type=client_credentials" + "&scope=code:all" 296 | }, function (error, response, body) { 297 | if (callback) { 298 | var token = ""; 299 | var bodyObj = {}; 300 | if (body) { 301 | bodyObj = JSON.parse(body); 302 | } 303 | if (bodyObj.token_type && bodyObj.access_token) { 304 | token = bodyObj.token_type + " " + bodyObj.access_token; 305 | } 306 | callback(error, token); 307 | } 308 | else { 309 | if (error) { 310 | console.log(error); 311 | } else { 312 | console.log(response.statusCode, body); 313 | } 314 | } 315 | }); 316 | } 317 | 318 | function getToken(url, key, secret, callback) { 319 | requestToken(url, key, secret, function (err, token) { 320 | if (err) { 321 | console.log("Error: " + err.toString()); 322 | } 323 | callback(token); 324 | }); 325 | } 326 | 327 | // The function returns a pre-signed url location that the client can upload the drawing 328 | // 329 | module.exports.uploadlocation = (event, context, callback) => { 330 | 331 | // The drawing name is passed as part of the input request 332 | var drawingName = event.dwgname; 333 | if (drawingName) { 334 | drawingName = decodeURIComponent(drawingName); 335 | } else { 336 | drawingName = "input.dwg"; 337 | } 338 | 339 | // Create a unique key 340 | var key = "drawings/" + uuid.v4() + "/" + drawingName; 341 | 342 | var s3 = new AWS.S3({ accessKeyId: config.aws_key, secretAccessKey: config.aws_secret }); 343 | var params = { Bucket: config.aws_s3_bucket, Key: key }; 344 | var url = s3.getSignedUrl("putObject", params); 345 | 346 | // Return the presigned url 347 | if (url) { 348 | context.callbackWaitsForEmptyEventLoop = false; 349 | var param = { Result: url }; 350 | callback(null, param); 351 | } else { 352 | context.callbackWaitsForEmptyEventLoop = false; 353 | callback("Could not process the request", "Error message"); 354 | } 355 | }; 356 | 357 | // The function takes the activity name, and the location of the drawing file as input. 358 | // It checks if the custom package and the activity are present. 359 | // And then the workitem is then submitted using the DesignAutomation API, configured to run the 360 | // activity that was passed. 361 | // 362 | module.exports.submitworkitem = (event, context, callback) => { 363 | 364 | // Get the activity name from the request 365 | // 366 | var activityname = event.activityname; 367 | 368 | if (!activityname) { 369 | callback("Error: Invalid activity name", "Error message"); 370 | return; 371 | } 372 | 373 | if (!event.dwglocation) { 374 | callback("Error: Invalid drawing location"); 375 | return; 376 | } 377 | 378 | var url = event.dwglocation; 379 | if (url.indexOf("?") !== -1) { 380 | var key = event.dwglocation; 381 | var strtoken = "amazonaws.com/"; 382 | key = key.substring(key.indexOf(strtoken) + strtoken.length); 383 | key = key.substring(0, key.indexOf("?")); 384 | if (!key) { 385 | callback("Invalid pre-signed url"); 386 | return; 387 | } 388 | 389 | key = decodeURIComponent(key); 390 | 391 | // Create a presigned url for GET that needs to be passed to DesignAutomation API 392 | // 393 | var s3 = new AWS.S3({ accessKeyId: config.aws_key, secretAccessKey: config.aws_secret }); 394 | var params = { Bucket: config.aws_s3_bucket, Key: key }; 395 | url = s3.getSignedUrl("getObject", params); 396 | } 397 | 398 | if (!url) { 399 | context.callbackWaitsForEmptyEventLoop = false; 400 | callback("Could not process the request", "Error message"); 401 | return; 402 | } 403 | 404 | getToken(config.auth_endpoint, config.developer_key, config.developer_secret, function (token) { 405 | if (!token) { 406 | callback("Error: Failed to obtain the access token"); 407 | return; 408 | } 409 | 410 | submitItem(token, url, activityname, function (ret, workitemId) { 411 | if (ret) { 412 | context.callbackWaitsForEmptyEventLoop = false; 413 | var param = { Result: workitemId }; 414 | callback(null, param); 415 | } else { 416 | context.callbackWaitsForEmptyEventLoop = false; 417 | callback("Could not process the request", "Error message"); 418 | } 419 | }); 420 | }); 421 | } 422 | 423 | // The function takes the workitem identifier as the input, and checks the 424 | // status of the respective workitem. The function uses the DesignAutomation 425 | // API to poll the status. It returns immediately on success or failure of the 426 | // workitem. If the workitem status is pending or in-progress, it loops through, 427 | // polling the status every two seconds for up to twenty seconds and then returns. 428 | // The reason it returns after twenty seconds is due to a thirty second timeout 429 | // limit set by the AWS API gateway. If the API does not respond within thirty 430 | // seconds, then the request is terminated and an error status of 504 is returned 431 | // by the AWS API gateway. 432 | // 433 | module.exports.workitemstatus = (event, context, callback) => { 434 | 435 | // Get the workitem id from the request 436 | // 437 | var workitemId = event.workitemid; 438 | if (!workitemId) { 439 | callback("Error: Invalid workitem id"); 440 | return; 441 | } 442 | workitemId = decodeURIComponent(workitemId); 443 | var url = config.workitem_endpoint + "(\'" + workitemId + "\')"; 444 | 445 | getToken(config.auth_endpoint, config.developer_key, config.developer_secret, function (token) { 446 | 447 | getWorkItemStatus(url, token, function (status, statustext, body) { 448 | if (status) { 449 | context.callbackWaitsForEmptyEventLoop = false; 450 | var param = { StatusText: statustext, Result: body }; 451 | callback(null, param); 452 | } else { 453 | context.callbackWaitsForEmptyEventLoop = false; 454 | var errormsg = "Error getting workitem status"; 455 | if (body) { 456 | errormsg = body; 457 | } 458 | callback(errormsg, "Error message"); 459 | } 460 | }); 461 | }); 462 | } 463 | 464 | // The function takes the location of the result posted by DesignAutomation API 465 | // for the workitem that was submitted. The result which is a compressed file 466 | // containing the output is uncompressed by the function and uploaded to a unique 467 | // location on S3. The location of the SVF/F2D and the XData files are returned. 468 | // 469 | module.exports.processresult = (event, context, callback) => { 470 | 471 | var url = event.output; 472 | if (!url) { 473 | console.log("Error: Invalid url"); 474 | callback("Error: Invalid url"); 475 | return; 476 | } 477 | 478 | var folderName = "files/" + uuid.v4() + "/"; 479 | 480 | // The function gets the result output by the Design Automation API, 481 | // which is a compressed form. It uncompresses the result, uploads 482 | // the files to a unique location on S3, and returns the location 483 | // of SVF/F2D files, along with the location of the xdata. 484 | // 485 | uploadWorkItemResult(url, folderName, function (status, resultlocation, xdatalocation) { 486 | if (status) { 487 | context.callbackWaitsForEmptyEventLoop = false; 488 | var param = { Result: resultlocation, CustomData: xdatalocation }; 489 | callback(null, param); 490 | } 491 | else { 492 | context.callbackWaitsForEmptyEventLoop = false; 493 | var errormsg = "Could not process the request"; 494 | if (resultlocation) { 495 | errormsg = resultlocation; 496 | } 497 | callback(errormsg, "Error message"); 498 | } 499 | }); 500 | } 501 | -------------------------------------------------------------------------------- /src/lambda/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | auth_endpoint: "https://developer.api.autodesk.com/authentication/v1/authenticate", 3 | 4 | package_endpoint: "https://developer.api.autodesk.com/autocad.io/us-east/v2/AppPackages", 5 | package_upload_endpoint: "https://developer.api.autodesk.com/autocad.io/us-east/v2/AppPackages/Operations.GetUploadUrl()", 6 | activity_endpoint: "https://developer.api.autodesk.com/autocad.io/us-east/v2/Activities", 7 | workitem_endpoint: "https://developer.api.autodesk.com/autocad.io/us-east/v2/WorkItems", 8 | package_name: "MyCustomPackage", 9 | 10 | developer_key: "ADSK_DEVELOPER_KEY", 11 | developer_secret: "ADSK_DEVELOPER_SECRET", 12 | 13 | aws_key: "AWS_ACCESS_KEY_ID", 14 | aws_secret: "AWS_SECRET_ACCESS_KEY", 15 | aws_region: "", 16 | aws_s3_bucket: "DA_VIEWER_DWG_BUCKET" 17 | } 18 | -------------------------------------------------------------------------------- /src/lambda/deploy.bat: -------------------------------------------------------------------------------- 1 | rem call npm install to download all the dependencies 2 | cd 3 | cd ..\src\lambda 4 | node_modules\.bin\serverless deploy 5 | -------------------------------------------------------------------------------- /src/lambda/event.json: -------------------------------------------------------------------------------- 1 | { 2 | "key3": "value3", 3 | "key2": "value2", 4 | "key1": "value1" 5 | } 6 | -------------------------------------------------------------------------------- /src/lambda/npminstall.bat: -------------------------------------------------------------------------------- 1 | rem call npm install to download all the dependencies 2 | npm install 3 | 4 | -------------------------------------------------------------------------------- /src/lambda/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ViewerSample", 3 | "version": "0.1.0", 4 | "description": "ViewerSample", 5 | "main": "app.js", 6 | "author": { 7 | "name": "George Varghese", 8 | "email": "george.varghese@autodesk.com" 9 | }, 10 | "dependencies": { 11 | "async": "^2.0.1", 12 | "aws-sdk": "^2.2.48", 13 | "request": "^2.75.0", 14 | "unzip": "^0.1.11", 15 | "uuid": "^2.0.2", 16 | "serverless": "1.0.0-rc.2" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/lambda/serverless.yml: -------------------------------------------------------------------------------- 1 | # Welcome to Serverless! 2 | # 3 | # This file is the main config file for your service. 4 | # It's very minimal at this point and uses default values. 5 | # You can always add more config options for more control. 6 | # We've included some commented out config examples here. 7 | # Just uncomment any of them to get that config option. 8 | # 9 | # For full config options, check the docs: 10 | # docs.serverless.com 11 | # 12 | # Happy Coding! 13 | 14 | service: ${env:DA_VIEWER_SERVIVCE_NAME} 15 | 16 | provider: 17 | name: aws 18 | runtime: nodejs4.3 19 | 20 | # you can overwrite defaults here 21 | stage: stage 22 | region: us-west-2 23 | 24 | # you can add statements to the Lambda function's IAM Role here 25 | iamRoleStatements: 26 | - Effect: "Allow" 27 | Action: 28 | - "lambda:InvokeFunction" 29 | Resource: "*" 30 | 31 | # you can add packaging information here 32 | #package: 33 | # include: 34 | # - include-me.js 35 | # exclude: 36 | # - exclude-me.js 37 | # artifact: my-service-code.zip 38 | 39 | functions: 40 | myuploadlocation: 41 | handler: app.uploadlocation 42 | timeout: 30 43 | events: 44 | - http: 45 | path: uploadlocation 46 | method: get 47 | cors: true 48 | request: 49 | template: 50 | application/json: '{ "dwgname" : "$input.params(''dwgname'')" }' 51 | 52 | mysubmitworkitem: 53 | handler: app.submitworkitem 54 | timeout: 30 55 | events: 56 | - http: 57 | path: submitworkitem 58 | method: post 59 | cors: true 60 | request: 61 | template: 62 | application/json: '{ "dwglocation" : $input.json(''$.dwglocation''), "activityname" : $input.json(''$.activityname'') }' 63 | 64 | myworkitemstatus: 65 | handler: app.workitemstatus 66 | timeout: 30 67 | events: 68 | - http: 69 | path: workitemstatus 70 | method: get 71 | cors: true 72 | request: 73 | template: 74 | application/json: '{ "workitemid" : "$input.params(''workitemid'')" }' 75 | 76 | myprocessresult: 77 | handler: app.processresult 78 | timeout: 30 79 | events: 80 | - http: 81 | path: processresult 82 | method: post 83 | cors: true 84 | request: 85 | template: 86 | application/json: '{ "output" : $input.json(''$.output'') }' 87 | 88 | # - s3: ${env:BUCKET} 89 | # - schedule: rate(10 minutes) 90 | # - sns: greeter-topic 91 | 92 | # you can add CloudFormation resource templates here 93 | resources: 94 | Resources: 95 | NewResource1: 96 | Type: AWS::S3::Bucket 97 | Properties: 98 | BucketName: ${env:DA_VIEWER_DWG_BUCKET} 99 | CorsConfiguration: 100 | CorsRules: 101 | - 102 | AllowedHeaders: 103 | - '*' 104 | AllowedMethods: 105 | - 'GET' 106 | - 'PUT' 107 | - 'POST' 108 | AllowedOrigins: 109 | - '*' 110 | MaxAge: 3000 111 | # Outputs: 112 | # NewOutput: 113 | # Description: "Description for the output" 114 | # Value: "Some output value" 115 | -------------------------------------------------------------------------------- /src/polysamp/asdkmgPoly.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Autodesk-Forge/design.automation-custom-data-viewer/be201c29fc90cfc03509551670388855174e3a14/src/polysamp/asdkmgPoly.dll -------------------------------------------------------------------------------- /src/polysamp/asdkpolyobj.dbx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Autodesk-Forge/design.automation-custom-data-viewer/be201c29fc90cfc03509551670388855174e3a14/src/polysamp/asdkpolyobj.dbx -------------------------------------------------------------------------------- /src/www/Autodesk.DASample.Viewing.Extension.DisplayXData.js: -------------------------------------------------------------------------------- 1 | // The viewer extension to view Xdata associated with the object 2 | // 3 | AutodeskNamespace("Autodesk.DASample.Viewing.Extension"); 4 | 5 | // The property panel to view the xdata values 6 | XDataPanel = function (parentContainer, id, x, y) { 7 | this.content = document.createElement('div'); 8 | Autodesk.Viewing.UI.PropertyPanel.call(this, parentContainer, id, 'XData'); 9 | 10 | this.container.style.height = "auto"; 11 | this.container.style.width = "auto"; 12 | this.container.style.resize = "none"; 13 | this.container.style.left = x + "px"; 14 | this.container.style.top = y + "px"; 15 | }; 16 | 17 | XDataPanel.prototype = Object.create(Autodesk.Viewing.UI.PropertyPanel.prototype); 18 | XDataPanel.prototype.constructor = XDataPanel; 19 | XDataPanel.prototype.initialize = function () { 20 | 21 | this.title = this.createTitleBar(this.titleLabel || this.container.id); 22 | this.container.appendChild(this.title); 23 | this.container.appendChild(this.content); 24 | this.initializeMoveHandlers(this.container); 25 | }; 26 | 27 | 28 | Autodesk.DASample.Viewing.Extension.DisplayXData = function (viewer, options) { 29 | 30 | Autodesk.Viewing.Extension.call(this, viewer, options); 31 | 32 | var _self = this; 33 | var _panel = null; 34 | 35 | _self.load = function () { 36 | viewer.addEventListener(Autodesk.Viewing.SELECTION_CHANGED_EVENT, onSelectionChanged); 37 | _panel = new XDataPanel(viewer.container, guid(), 5, 5); 38 | _panel.setVisible(true); 39 | 40 | return true; 41 | }; 42 | 43 | _self.unload = function () { 44 | 45 | viewer.removeEventListener(Autodesk.Viewing.SELECTION_CHANGED_EVENT, onSelectionChanged); 46 | return true; 47 | }; 48 | 49 | function guid() { 50 | 51 | var d = new Date().getTime(); 52 | 53 | var guid = 'xxxx-xxxx-xxxx-xxxx'.replace( 54 | /[xy]/g, 55 | function (c) { 56 | var r = (d + Math.random() * 16) % 16 | 0; 57 | d = Math.floor(d / 16); 58 | return (c == 'x' ? r : (r & 0x7 | 0x8)).toString(16); 59 | }); 60 | 61 | return guid; 62 | } 63 | 64 | function onSelectionChanged(event) { 65 | _panel.removeAllProperties(); 66 | 67 | var viewer = event.target; 68 | event.dbIdArray.forEach(function (dbId) { 69 | displayXData(dbId); 70 | }); 71 | } 72 | 73 | // The function checks if the object that is passed has any xdata that has 74 | // been cached. The object handle is used to check the availability 75 | // in the cache. 76 | // 77 | function displayXData(dbId) { 78 | if (!xdatainfo) 79 | return; 80 | 81 | viewer.getProperties(dbId, function _cb(result) { 82 | if (result.properties) { 83 | var displayValue; 84 | var length = result.properties.length; 85 | for (var i = 0; i < length; i++) { 86 | var prop = result.properties[i]; 87 | if (prop.displayName === "elementId") { 88 | displayValue = prop.displayValue; 89 | break; 90 | } 91 | } 92 | 93 | if (displayValue) { 94 | if (xdatainfo[displayValue]) { 95 | var xdata = xdatainfo[displayValue].XData; 96 | var length = xdata.length; 97 | for (var i = 0; i < length; i++) { 98 | _panel.addProperty(xdata[i].Type, xdata[i].Value, dbId); 99 | } 100 | } 101 | } 102 | } 103 | }); 104 | } 105 | }; 106 | 107 | Autodesk.DASample.Viewing.Extension.DisplayXData.prototype = 108 | Object.create(Autodesk.Viewing.Extension.prototype); 109 | 110 | Autodesk.DASample.Viewing.Extension.DisplayXData.prototype.constructor = 111 | Autodesk.DASample.Viewing.Extension.DisplayXData; 112 | 113 | Autodesk.Viewing.theExtensionManager.registerExtension( 114 | 'Autodesk.DASample.Viewing.Extension.DisplayXData', 115 | Autodesk.DASample.Viewing.Extension.DisplayXData); 116 | -------------------------------------------------------------------------------- /src/www/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Acquiring File Information 5 | 11 | 281 | 282 | 283 | 284 |
285 |
286 |
287 | Upload 3D drawings 288 |
289 |
290 | 291 |
292 |
293 |
294 |
295 |
296 |
297 | Upload 2D drawings 298 |
299 |
300 | 301 |
302 |
303 |
304 |
305 |
306 |
307 | Create/View CustomObject 308 |
309 |
310 | 311 |
312 |
313 |
314 |
315 |
316 |
317 | 318 |
319 |
320 |
321 | 322 | 323 | -------------------------------------------------------------------------------- /src/www/viewer.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | My Viewer 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Autodesk-Forge/design.automation-custom-data-viewer/be201c29fc90cfc03509551670388855174e3a14/thumbnail.png --------------------------------------------------------------------------------