Page 1 MFG473705 Using iLogic with Design Automation for Inventor to create a Configurator Sajith Subramanian Autodesk Description Inventor Design Automation, in general, allows the use of API’s to be executed in the cloud, similar to how they would work on the desktop, but without the need of any installed software or its dependent licenses. An additional advantage of Inventor Design Automation, which may be easily overlooked, is its capability to run iLogic scripts in the cloud. These scripts being embedded in the CAD file itself can be programmed to auto execute under certain conditions. This class speaks about taking advantage of this capability to combine Forge Design Automation with iLogic, creating a configurator by modifying an input Inventor File (IPT) in the cloud, using a web browser, according to the inputs provided by the user on an easy-to-use UI. The results can be immediately viewed on the browser itself, using the Forge Viewer and can also be downloaded in multiple formats such as IPT, IDW, and PDF. This web application sample uses latest web technologies and is OS independent and mobile device friendly. Speaker Sajith Subramanian - A qualified software engineer with over a decade of development experience with various CAD tools, he is part of the Manufacturing team at Autodesk, supporting and evangelizing API’s for Inventor, Fusion and Vault. Now an evangelist and an enthusiast on the Forge platform, he has published various code samples showcasing the combined use of desktop product API’s with our Forge platform. He now looks forward to making this transition from desktop to cloud as seamless as possible for fellow developer enthusiasts. Learning Objectives • Create a web application that uses Forge Design Automation for Inventor that runs on a web browser without having the need of having locally installed software. • Extract the added advantages of being able to run iLogic in the cloud. • Immediately render and view the resulting output using the Forge Viewer. Drive dependent file formats like IPT, IDW and PDF.
30
Embed
MFG473705 Using iLogic with Design Automation for Inventor ...
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1
MFG473705
Using iLogic with Design Automation for Inventor to create a Configurator Sajith Subramanian Autodesk
Description
Inventor Design Automation, in general, allows the use of API’s to be executed in the cloud, similar to how they would work on the desktop, but without the need of any installed software or its dependent licenses. An additional advantage of Inventor Design Automation, which may be easily overlooked, is its capability to run iLogic scripts in the cloud. These scripts being embedded in the CAD file itself can be programmed to auto execute under certain conditions. This class speaks about taking advantage of this capability to combine Forge Design Automation with iLogic, creating a configurator by modifying an input Inventor File (IPT) in the cloud, using a web browser, according to the inputs provided by the user on an easy-to-use UI. The results can be immediately viewed on the browser itself, using the Forge Viewer and can also be downloaded in multiple formats such as IPT, IDW, and PDF. This web application sample uses latest web technologies and is OS independent and mobile device friendly.
Speaker
Sajith Subramanian - A qualified software engineer with over a decade of development experience with various CAD tools, he is part of the Manufacturing team at Autodesk, supporting and evangelizing API’s for Inventor, Fusion and Vault. Now an evangelist and an enthusiast on the Forge platform, he has published various code samples showcasing the combined use of desktop product API’s with our Forge platform. He now looks forward to making this transition from desktop to cloud as seamless as possible for fellow developer enthusiasts.
Learning Objectives
• Create a web application that uses Forge Design Automation for Inventor that runs on a web browser without having the need of having locally installed software.
• Extract the added advantages of being able to run iLogic in the cloud.
• Immediately render and view the resulting output using the Forge Viewer. Drive dependent file formats like IPT, IDW and PDF.
Page 2
Overview
To create a web application that will demonstrate how to create a configurator using Forge Design Automation for Inventor and iLogic, you start by creating an ASP.Net Core web application. We use .Net core so that it can run on other OS environments as well. This application provides a User Interface(UI) on a web browser, that allows the user to pick from various available configurations of an automotive rim. The selected configuration is then applied to the input template Ipt file using Forge Design Automation for Inventor, which in-turn triggers the iLogic rule embedded within this ipt file. The iLogic file then makes modifications to the design elements of the ipt file. The resulting ipt file and the drawing can be viewed immediately on the browser using the Viewer. The results are also stored in OSS (Object Storage Service), and is available for download in multiple formats, viz. ipt, idw and pdf.
FIGURE 1: HIGH-LEVEL WORKFLOW DIAGRAM
Page 3
Setting up your Web Application
Step 1: Prerequisite - Creating a Forge account To use Forge API’s, you need to first create your forge account by signing up on the Forge Developer Portal. Figure 2 shows how to create an app. A step-by-step help is listed here: How to create a Forge App
Step 2: Prerequisite – Setting up ngrok When Design Automation finishes modifying your model, it notifies back. As your machine is not exposed on the web, the ngrok tool create a temporary address to receive notifications. This tool is only required locally. You can download this tool here. After download, unzip it. Open the Windows Command Line Prompt (CMD) and navigate to the folder. Then run ngrok http 3000 -host-header="localhost:3000". Copy the forwarding URL value (in the form of http://1ab2c3d4.ngrok.com), as shown in Figure 3 below.
Step 3: Creating the solution in Visual Studio You will need two projects within your solution. One project would be a ASP.Net Core while the other project would be your Inventor plug-in. Let’s start with the ASP.Net Core application first as shown below in Figure 4.
FIGURE 4: CREATING YOUR ASP.Net PROJECT
Page 6
The second project is creating your Inventor plug-in. It is recommended to use the Design Automation template for creating the plug-in project. Let’s start by first downloading the Visual studio extension as shown in Figure 5.
FIGURE 5: DOWNLOADING THE EXTENSION
Page 7
You can then add a new project using the template as shown in Figure 6. For a complete step-by-step guide for creating an Inventor plug-in, please refer here: Create an Inventor plug-in
For adding the necessary references, right-click on the project in Solution Explorer, select ‘Manage NuGet Package’, then on Browse search for ‘Autodesk.Forge’ and install Autodesk.Forge. Similarly install ‘Autodesk.Forge.DesignAutomation’ and ‘Microsoft.AspNetCore.Mvc.NewtonsoftJson’ as shown below in Figure 7.
FIGURE 7: ADDING REFERENCES
For a complete step-by-step guide for creating a new project, please refer here: Create a .Net Core project
Step 5: Adding the environment variables Right-click on the project, go to Properties, then under Debug tab see the Environment Variables section. Finally, as this is running locally, uncheck Enable SSL option, as shown below in Figure 8.
FIGURE 8: ADDING ENVIRONMENT VARIABLES
Page 10
Preparing your inputs
Step 1: Preparing your Inventor plug-in The plug-in refers to Inventor API code (similar to the plug-in which you use for Inventor desktop application) that you would want to execute in the cloud using Design Automation. Design Automation uses an App Bundle, in which you need to create a zipped folder with the PackageContents.xml and along with the required DLLs and its related files. For this class, you will learn how to open the input ipt file that is part of your AppBundle, modify the required properties, and save back the file. To do so, add the following lines of code to the SampleAutomation.cs file in the Inventor plug-in project created earlier, as shown below: public void RunWithArguments(Document doc, NameValueMap map) { try { StringBuilder traceInfo = new StringBuilder("RunWithArguments called with"); Trace.TraceInformation(map.Count.ToString()); // values in map are keyed on _1, _2, etc for (int i = 0; i < map.Count; i++) { traceInfo.Append(" and "); traceInfo.Append(map.Value["_" + (i + 1)]); } Trace.TraceInformation(traceInfo.ToString()); #region change parameters Trace.TraceInformation("Changing User params"); // load processing parameters string paramsJson = GetParametersToChange(map); Trace.TraceInformation("Inventor Parameters JSON: \"" + paramsJson + "\""); Dictionary<string, string> parameters = JsonConvert.DeserializeObject<Dictionary<string, string>>(paramsJson); // Get path of add-in dll string assemblyPath = System.IO.Path.GetDirectoryName(Assembly.
GetExecutingAssembly().Location); Trace.TraceInformation("Assembly Path = " + assemblyPath); // Path of template relative to the dll's path string iptPath = parameters["InputIPT"]; string idwPath = parameters["InputIDW"];
Page 11
// Open template document string iptfullPath = System.IO.Path.Combine(assemblyPath, @"Rim\", iptPath); string idwfullPath = System.IO.Path.Combine(assemblyPath, @"Rim\", idwPath); Document iptDoc = m_inventorServer.Documents.Open(iptfullPath, false); Document idwDoc = m_inventorServer.Documents.Open(idwfullPath, false); var theParams = GetParameters(iptDoc); foreach (KeyValuePair<string, string> entry in parameters) { var parameterName = entry.Key; var value = entry.Value; Trace.TraceInformation("Parameter to change: {0}:{1}", parameterName, value); try { UserParameter param = theParams[parameterName]; try { param.Value = value; } catch { param.Expression = value; } } catch (Exception e) { Trace.TraceInformation("Cannot update '{0}' parameter. ({1})", parameterName, e.Message); } } iptDoc.Update(); Trace.TraceInformation("Part Doc updated."); var currDir = System.IO.Directory.GetCurrentDirectory(); var iptfileName = System.IO.Path.Combine(currDir, "Result.ipt"); // the name must be in sync with OutputIpt localName in Activity iptDoc.SaveAs(iptfileName, false); Trace.TraceInformation(" Part Doc saved."); idwDoc.Update(); Trace.TraceInformation("Drawing Doc updated."); var idwfileName = System.IO.Path.Combine(currDir, "Result.idw"); // the name must be in sync with OutputIdw localName in Activity idwDoc.SaveAs(idwfileName, false); var pdffilename = System.IO.Path.Combine(currDir, "Result.pdf"); // name must be in sync with OutputPDF localName in Activity idwDoc.SaveAs(pdffilename, true); Trace.TraceInformation(" Drawing Doc saved."); #endregion } catch (Exception ex) { Trace.TraceInformation(ex.Message); } }
Page 12
Step 2: Preparing your input file with iLogic In your input Ipt file, Navigate to the iLogic rules and add your code in the iLogic editor. Also, do remember to set the Event trigger, so that it auto executes whenever a parameter is changed, as shown in Figure 9 below.
FIGURE 9: ADDING YOUR ILOGIC CODE TO THE INPUT FILE
Page 13
Adding the code – Server!
Step 1: Creating the server Now that you have your inputs all set up with the necessary references, its time to add some code! In Program.cs and add the following namespaces: using Microsoft.AspNetCore; using Autodesk.Forge.DesignAutomation;
Modify the Main() method as shown below. This tells our application to load Forge Client ID & Secret from the environment variables defined above. public static void Main(string[] args) { CreateWebHostBuilder(args).ConfigureAppConfiguration(builder => { builder.AddForgeAlternativeEnvironmentVariables(); }).ConfigureServices((hostContext, services) => { services.AddDesignAutomation(hostContext.Configuration); }).Build().Run(); } In the Startup.cs and add the following namespace: using Microsoft.AspNetCore.Mvc;
Replace the content of the Startup class with the following code. public void ConfigureServices(IServiceCollection services) { services.AddMvc(options => options.EnableEndpointRouting = false).SetCompatibilityVersion(CompatibilityVersion.Version_3_0).AddNewtonsoftJson(); ; services.AddSignalR(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.UseRouting(); app.UseEndpoints(routes => { routes.MapHub<Controllers.DesignAutomationHub>("/api/signalr/designautomation"); }); app.UseFileServer(); app.UseMvc(); }
Page 14
Step 2: Adding the Web Api Controllers We now proceed to creating our Web Api Controllers. We start by creating a controllers folder. Under the controllers folder we create two files: 1. OAuthController.cs 2. DesignAutomationController.cs The following segments explain the various steps involved in modifying our input ipt file using Design Automation for Inventor
a. Authenticate: The OAuthController would be responsible for fetching the access token. Add the below content to the OAuthController.cs file.
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Autodesk.Forge; namespace InventorDA.Controllers { [ApiController] public class OAuthController : ControllerBase { private static dynamic InternalToken { get; set; } private static dynamic PublicToken { get; set; } /// <summary> /// Get access token with public (viewables:read) scope /// </summary> [HttpGet] [Route("api/forge/oauth/token")] public async Task<dynamic> GetPublicAsync() { if (PublicToken == null || PublicToken.ExpiresAt < DateTime.UtcNow) { PublicToken = await Get2LeggedTokenAsync(new Scope[] { Scope.ViewablesRead }) ; PublicToken.ExpiresAt =
You need to have the output ipt file placed in a location so that it can be further processed and translated to a Viewer friendly format. In this sample I use the Forge OSS (Object Storage Service), where files are stored as objects in buckets. Let’s go ahead and create the bucket using the following lines of code: private async Task<IActionResult> CreateBucket() {
dynamic oauth = await OAuthController.GetInternalAsync(); //Note that bucket keys must be of the form [-_.a-z0-9]{3,128} string bucketkey = "inventorilogicda" + nickName.ToLower(); BucketsApi bucketsApi = new BucketsApi(); bucketsApi.Configuration.AccessToken = oauth.access_token; dynamic buckets = await bucketsApi.GetBucketsAsync(); bool bucketExists = buckets.items.ToString().Contains(bucketkey); if (!bucketExists) { PostBucketsPayload postBucket = new PostBucketsPayload(bucketkey,
For further reading about buckets and OSS, please refer: OSS
c. Defining the activity: The activity is where we specify the action that needs to be executed. You can think of it as a ‘Function Definition’ where you define the specifics so that it can be called for execution at a later point in your code. The activity contains information such as which AppBundle to be used, input and output files etc. Let’s go ahead and define the activity using the below lines of code:
if (!System.IO.File.Exists(LocalAppPackageZip)) throw new Exception("Appbundle not found at " + LocalAppPackageZip); AppBundle appBundleSpec = new AppBundle() { Package = APPNAME, Engine = EngineName, Id = APPNAME, Description = string.Format("Description for {0}", APPNAME), }; AppBundle newApp = await _designAutomation.CreateAppBundleAsync(appBundleSpec); if (newApp == null) throw new Exception("Cannot create new app"); // create alias pointing to v1 Alias aliasSpec = new Alias() { Id = ALIAS, Version = 1 }; Alias newAlias = await _designAutomation.CreateAppBundleAliasAsync(APPNAME, aliasSpec); // upload the zip with .bundle RestClient uploadClient = new RestClient(newApp.UploadParameters.EndpointURL); RestRequest request = new RestRequest(string.Empty, Method.POST); request.AlwaysMultipartFormData = true; foreach (KeyValuePair<string, string> x in newApp.UploadParameters.FormData) request.AddParameter(x.Key, x.Value); request.AddFile("file", LocalAppPackageZip); request.AddHeader("Cache-Control", "no-cache"); await uploadClient.ExecuteTaskAsync(request); } Page<string> activities = await _designAutomation.GetActivitiesAsync(); string qualifiedActivityId = string.Format("{0}.{1}+{2}", nickName, ACTIVITY_NAME, ALIAS); if (!activities.Data.Contains(qualifiedActivityId)) { // define the activity string commandLine = string.Format("$(engine.path)\\inventorcoreconsole.exe /al \"$(appbundles[{0}].path)\" \"$(args[inputJson].path)\"", APPNAME); Activity activitySpec = new Activity() { Id = ACTIVITY_NAME, Appbundles = new List<string>() { string.Format("{0}.{1}+{2}", nickName, APPNAME, ALIAS) }, CommandLine = new List<string>() { commandLine }, Engine = EngineName, Parameters = new Dictionary<string, Parameter>() { { "inputJson", new Parameter() { Description = "input json", LocalName = "params.json", Ondemand = false, Required = false, Verb = Verb.Get, Zip = false } }, { "ResultIPT", new Parameter() { Description = "output IPT
Page 18
file", LocalName = outputIPTFile, Ondemand = false, Required = true, Verb = Verb.Put, Zip = false } }, { "ResultIDW", new Parameter() { Description = "output IDW file", LocalName = outputIDWFile, Ondemand = false, Required = true, Verb = Verb.Put, Zip = false } }, { "ResultPDF", new Parameter() { Description = "output PDF file", LocalName = outputPDFile, Ondemand = false, Required = true, Verb = Verb.Put, Zip = false } } } }; Activity newActivity = await _designAutomation.CreateActivityAsync(activitySpec); // specify the alias for this Activity Alias aliasSpec = new Alias() { Id = ALIAS, Version = 1 }; Alias newAlias = await _designAutomation.CreateActivityAliasAsync(ACTIVITY_NAME, aliasSpec); return Ok(new { Activity = qualifiedActivityId }); } return Ok(new { Activity = "Activity already defined" }); }
For further reading about activities, please refer: Defining an Activity
d. Creating the Workitem: The Workitem is where you execute the specifics listed in the activity in the previous step. So, this is can be imagined as our ‘Function call’ for executing the activity.
Let’s go ahead and create the workitem using the following lines of code:
For further reading about Workitem, please refer: Execute the Workitem
e. OnCallback: When the workitem is done, Design Automation will callback our app (using the ngrok forwarding URL). This function will handle it and push a notification to the client (using SignalR Hub).
We start by creating a js folder under the wwwroot folder. Under this js folder we create two files:
1. DesignAutomation.js 2. ForgeViewer.js
a. DesignAutomation.js: The DesignAutomation.js file is primarily used to execute Javascript functions on our webpage, and to post the user selected values to our WebApi Controller. Add the below code to this file. var rim, color, dia, width; var downloadiptURL, downloadidwURL, downloadpdfURL; var connection, connectionId; $(document).ready(function () { changeimage(); startConnection(); $('.form-group').change(changeimage); $('#preview').click(function () { deactivateUI(); $('#displaytext').show(); jQuery.post({
Page 26
url: 'api/forge/params/designautomation', contentType: 'application/json', data: JSON.stringify({ 'rim': rim, 'color': color, 'diameter': dia, 'width': width, 'browserconnectionId': connectionId }), }); }); $('#download').click(function () { var urls = [downloadidwURL, downloadiptURL, downloadpdfURL]; var interval = setInterval(download, 1000, urls); function download() { var url = urls.pop(); var a = document.createElement("a"); location.href = url; if (urls.length == 0) { clearInterval(interval); } } }); }); function changeimage() { $('#displaytext').hide(); $('#forgeViewer3d').empty(); $('#forgeViewer2d').empty(); rim = $("#rimstyle").val(); color = $("#color").val(); dia = $("#diameter").val(); width = $("#width").val(); var filepath = "images/img.png"; filepath = filepath.replace("img", color + rim.substr(5, 5) + dia + width); $('#img').attr('src', filepath); } function startConnection(onReady) { if (connection && connection.connectionState) { if (onReady) onReady(); return; } connection = new signalR.HubConnectionBuilder().withUrl("/api/signalr/designautomation").build(); connection.start() .then(function () { connection.invoke('getConnectionId') .then(function (id) { connectionId = id; // we'll need this... if (onReady) onReady(); }); }); connection.on("downloadResult", function (urlipt, urlidw, urlpdf) { downloadiptURL = urlipt; downloadidwURL = urlidw; downloadpdfURL = urlpdf;
Page 27
}); connection.on("onTranslate", function (urnipt, urnidw) { launchViewer(urnipt, urnidw); }); } function activateUI() { $('.panel :input').prop("disabled", false); } function deactivateUI() { $('.panel :input').prop("disabled", true);
}
b. ForgeViewer.js:
This file is responsible to handle the Viewer initialization. Add the below code to this file.
var viewer3d; var viewer2d; function launchViewer(urnipt, urnidw) { var options = { env: 'AutodeskProduction', getAccessToken: getForgeToken }; Autodesk.Viewing.Initializer(options, () => { viewer3d = new Autodesk.Viewing.GuiViewer3D(document.getElementById('forgeViewer3d')); viewer2d = new Autodesk.Viewing.GuiViewer3D(document.getElementById('forgeViewer2d')); viewer3d.start(); viewer2d.start(); viewer3d.addEventListener(Autodesk.Viewing.SELECTION_CHANGED_EVENT,function (event) { if (event.dbIdArray.length === 1) { viewer3d.getProperties(event.dbIdArray[0], function(data) { console.log(data.name) if (data.name.startsWith("Solid")) { var instanceTree = viewer3d.model.getData().instanceTree; var parentId = instanceTree.getNodeParentId(event.dbIdArray[0]) viewer3d.select([parentId]); } })
Page 28
} }); viewer2d.addEventListener(Autodesk.Viewing.SELECTION_CHANGED_EVENT,function(event) { if (event.dbIdArray.length === 1) { viewer2d.getProperties(event.dbIdArray[0], function(data) { console.log(data.name) if (data.name.startsWith("Solid")) { var instanceTree = viewer2d.model.getData().instanceTree; var parentId = instanceTree.getNodeParentId(event.dbIdArray[0]) viewer2d.select([parentId]); } }) } }); Autodesk.Viewing.endpoint.HTTP_REQUEST_HEADERS['If-Modified-Since'] = 'Sat, 29 Oct 1994 19:43:31 GMT'; var iptdocumentId = 'urn:' + urnipt; Autodesk.Viewing.Document.load(iptdocumentId, function (doc) { var viewables = doc.getRoot().getDefaultGeometry(); viewer3d.loadDocumentNode(doc, viewables).then(i => { activateUI(); }); }, onDocumentLoadFailure); var idwdocumentId = 'urn:' + urnidw; Autodesk.Viewing.Document.load(idwdocumentId,function (doc) { var viewables = doc.getRoot().getDefaultGeometry(); viewer2d.loadDocumentNode(doc, viewables).then(i => { // documented loaded, any action? }); }, onDocumentLoadFailure); }); }; function onDocumentLoadFailure(viewerErrorCode) { console.error('onDocumentLoadFailure() - errorCode:' + viewerErrorCode); } function getForgeToken(callback) { fetch('/api/forge/oauth/token').then(res => { res.json().then(data => { callback(data.access_token, data.expires_in); }); });
}
Page 29
That completes our code changes! When you hit play, the HTML interface should open up in the browser!
You can find the entire source of this application on GitHub at the following link: https://github.com/sajith-subramanian/forge-rimconfigurator-inventor The application is hosted on Heroku and can be accessed here: https://forge-rimconfigurator-inventor.herokuapp.com/
Review
We have learned how to create a web application that highlights the capabilities of Forge Design Automation for Inventor along with the added advantages of being able to run your iLogic code in the cloud. I hope this class was useful to you and encourages you to continue learning and experiment on various other workflows using the Forge APIs. Thank you for your time!