''' Example of an Epicor CPQ extension for AutoCAD. ''' For examples appropriate to other CAD platforms, please visit our online documentation. ''' A good reference for the object model for the AutoCad API. ''' https://help.autodesk.com/view/ACD/2017/ENU/?guid=GUID-A809CD71-4655-44E2-B674-1FE200B9FE30 ''' Any extension project requires a set of base libraries: Imports System.ComponentModel.Composition Imports System.Runtime.InteropServices Imports KBMax.Drivers.Core Imports KBMax.Extensions Imports KBMax.Models ''' The following two libraries should be added as references to any extension based on Autocad: ''' They should be located in C:\Program Files\Autodesk\AutoCAD 20XX Imports Autodesk.AutoCAD.Interop Imports Autodesk.AutoCAD.Interop.Common Public Class RuleExtension Implements KBMax.Extensions.IRulesExtension Private m_build As KBMax.Models.Build Private m_conf As KBMax.Models.Configurator Private m_quote As KBMax.Models.Quote Private WithEvents m_cadEvent As KBMax.Models.DriverEvents Private m_utilities As New ExtensionUtilities Private m_api As IApi ''' The m_driver in other CAD extensions is of type "CadDriver" ''' However, for an Autocad extension, the m_driver type is simply 'Driver' Private m_driver As Driver Private m_output As Output #Region "Output Builder Events" Public Property Enabled As Boolean Implements IExtension.Enabled Get Return True End Get Set(value As Boolean) End Set End Property Public Sub Close() Implements KBMax.Extensions.IExtension.Close End Sub Public Sub Load(api As KBMax.Extensions.IApi) Implements KBMax.Extensions.IRulesExtension.Load m_api = api End Sub Public Function OnInstallNewVersion(version As String) As Boolean Implements IExtension.OnInstallNewVersion Return True End Function Public Sub OnAfterExecOutputRules(conf As KBMax.Models.Configurator, output As KBMax.Models.Output, quote As KBMax.Models.Quote, build As KBMax.Models.Build, buildResults As List(Of OutputBuildResult)) Implements KBMax.Extensions.IRulesExtension.OnAfterExecOutputRules ' here the javascript rules have already been executed m_build = build m_conf = conf m_quote = quote m_utilities.SetConfiguratorObjects(m_conf, output, quote, build) End Sub Public Sub OnBeforeBuild(conf As KBMax.Models.Configurator, quoteProduct As KBMax.Models.QuoteProduct, build As KBMax.Models.Build) Implements KBMax.Extensions.IRulesExtension.OnBeforeBuild End Sub Public Sub OnAfterBuild(conf As KBMax.Models.Configurator, quoteProduct As KBMax.Models.QuoteProduct, build As KBMax.Models.Build) Implements KBMax.Extensions.IRulesExtension.OnAfterBuild End Sub Public Sub OnBeforeExecOutputRules(conf As KBMax.Models.Configurator, output As KBMax.Models.Output, quote As KBMax.Models.Quote, build As KBMax.Models.Build, buildResults As List(Of OutputBuildResult)) Implements KBMax.Extensions.IRulesExtension.OnBeforeExecOutputRules End Sub Public Sub OnBuild(conf As KBMax.Models.Configurator, pathToResults As String) Implements KBMax.Extensions.IRulesExtension.OnBuild End Sub Public Sub OnBeforeBuildOutput(config As Configurator, output As Output, pathToResults As String, isRoot As Boolean, configCode As String) Implements IExtension.OnBeforeBuildOutput End Sub Public Sub OnBuildOutput(output As Output, driver As Driver, pathToResults As String) Implements IRulesExtension.OnBuildOutput ''' The line below tells the Output builder when to run the CAD Driver code ''' In this example, only if the configurator ID is 25. ''' If you do not add this conditional, then it will run the CAD extension code on every build, regardless of the id of the config. If TypeOf driver.Events Is KBMax.Models.DriverEvents And m_conf.IdProduct = 25 Then m_cadEvent = driver.Events m_driver = driver m_output = output End If End Sub Public Sub OnClose(conf As KBMax.Models.Configurator) Implements KBMax.Extensions.IRulesExtension.OnClose End Sub Public Sub OnLoad(conf As KBMax.Models.Configurator) Implements KBMax.Extensions.IRulesExtension.OnLoad End Sub Public Function ResolvePathToResults(config As Configurator, pathToResults As String, isRoot As Boolean, configCode As String) As String Implements IExtension.ResolvePathToResults Return pathToResults End Function Public Sub OnLogIn(email As String, subdomain As String, environment As String) Implements IExtension.OnLogIn End Sub Public Sub OnLogOut() Implements IExtension.OnLogOut End Sub #End Region #Region "CAD Events" ''' There are different events that we need to handle under the ''' 'Driver' interface. These events are OnEnd() and OnStart() ''' In this example, we only use the OnEnd event. Sub m_cadEvent_OnEnd() Handles m_cadEvent.OnEnd On Error GoTo ErrorHandler ' Instantiate App and Doc Dim app As AcadApplication = Marshal.GetActiveObject("AutoCAD.Application") Dim doc As AcadDocument = app.Documents.Open(m_output.ResultFilePath) If app Is Nothing Then app = CreateObject("AutoCAD.Application") End If ' Adding Attributes Dim cadAttributeJson As String = m_utilities.GetInputFieldValueByName("fAcadAttributes") Dim params As CadParameterField() = Newtonsoft.Json.JsonConvert.DeserializeObject(Of CadParameterField())(cadAttributeJson) Dim paramsDict As New Scripting.Dictionary For Each param In params paramsDict.Add(param.name, param.value) Next Dim ent As AcadEntity For Each ent In doc.ModelSpace If TypeOf ent Is AcadBlockReference Then Dim aRef As AcadAttributeReference For Each aRef In DirectCast(ent, AcadBlockReference).GetAttributes() If paramsDict(aRef.TagString) IsNot Nothing Then aRef.TextString = paramsDict(aRef.TagString) End If Next End If Next 'Add Bom Items Dim bomJson As String = m_utilities.GetInputFieldValueByName("fAcadBomItems") Dim bomItems As CadBomItem() = Newtonsoft.Json.JsonConvert.DeserializeObject(Of CadBomItem())(bomJson) Dim bomPoint As New Point3d(0.5, 1.15, 0) For Each bomItem In bomItems Dim bomDict As New Scripting.Dictionary For Each cadParam In bomItem.CadParameters bomDict.Add(cadParam.name, cadParam.value) Next Dim point As Double() = bomPoint.ToArray() Dim newBlock As AcadBlockReference = doc.ModelSpace.InsertBlock(bomPoint.ToArray(), bomItem.FileName, 1, 1, 1, 0) Dim aRef As AcadAttributeReference For Each aRef In newBlock.GetAttributes() If bomDict(aRef.TagString) IsNot Nothing Then aRef.TextString = bomDict(aRef.TagString) End If Next bomPoint.Y += 0.35 Next ''' Always SaveAs and close the document. doc.SaveAs(m_output.ResultFilePath) doc.Close() Exit Sub ''' There are some error exceptions that we will need to handle: RPC errors (Remote Procedure Calls). ''' The extension makes a call to the AutoCAD API, but the AutoCad application may not respond. ''' To handle this, first confirm this is indeed the type of error we experienced. ''' Then give AutoCAD some time to become again available. ''' Finally 'resume' at the same line of code that threw the error if it is an RPC error. ErrorHandler: If Err.Description.Contains("RPC_E_SERVERCALL_RETRYLATER") Then ' TODO : Trap for Specifically RPC Errors. Threading.Thread.Sleep(100) m_driver.Result.Log(Err.Description, eLogId.OutputDriver_Info) m_driver.Result.Log("Retrying procedure..", eLogId.OutputDriver_Info) Resume Else Throw New System.Exception(Err.Description) End If End Sub #End Region End Class