OK. I think I messed up the process I originally had in mind, and that's why you are still needing to run those two other rules to make stuff happen. I should have first gotten the list of model state names from the Excel sheet, then the main loop in the rule will be looping through each of those names. Within each loop, update the main assembly's custom iProperty to this model state name, then loop through every component, updating its custom iProperty, then running its local rule to change its model state. Then right after the loop of components, but still inside the loop of names, update the main assembly, then export the main assembly to the two file formats. Then loop to the next model state name.
I also modified the 'ExportAssemblyToRFA' sub routine to what I believe is the way you are wanting it. It should now be naming the files similarly to the STEP files, but with the ".rfa" file extension. (assembly's original path, model state name, then ".rfa" file extension)
Sub ExportAssemblyToRFA(oAssembly As AssemblyDocument, oMSName As String)
oADef = oAssembly.ComponentDefinition
oPath = System.IO.Path.GetDirectoryName(oAssembly.FullFileName)
oDirChr = System.IO.Path.DirectorySeparatorChar
oFFN = oPath & oDirChr & oMSName & ".rfa"
'export
oADef.BIMComponent.ExportBuildingComponent(oFFN)
End Sub
So here is the finished final code. It doesn't run those two other external rules, but just runs the one local rule in each component.
AddReference "Microsoft.Office.Interop.Excel.dll"
Imports Microsoft.Office.Interop.Excel
Sub Main
'get the 'active' main assembly
If ThisApplication.ActiveDocumentType <> DocumentTypeEnum.kAssemblyDocumentObject Then
MsgBox("An Assembly Document must be active for this rule to work. Exiting.", vbCritical, "iLogic")
Exit Sub
End If
Dim oADoc As AssemblyDocument = ThisApplication.ActiveDocument
oADef = oADoc.ComponentDefinition
oAuto = iLogicVb.Automation
oLocalRuleName = "Update_Model_State"
oExcelFile = "C:\Users\frahaman\Documents\Model State List.xlsx"
oExcelSheet = "Sheet1"
Dim oExcelDataStartRow As Integer = 2 'starting with 2, because first data row in Excel is row 2
Dim oMSNames As List(Of String) = GetModelStateNames(oExcelFile, oExcelSheet, oExcelDataStartRow)
'<<<< MAKE SURE ALL CELLS IN COLUMN SERIES HAVE VALUE >>>>
'now loop through these ModelState names as the main loop in this rule
For Each oMSName In oMSNames
If String.IsNullOrEmpty(oMSName) Then Continue For
'push this ModelState name to the main assembly's custom iProperty
iProperties.Value("Custom", "Model_State:") = oMSName
'loop through each component, update its iProperty, and run its local rule
For Each oOcc As ComponentOccurrence In oADef.Occurrences
'push that ModelState name to custom iProperty of this component
iProperties.Value(oOcc.Name, "Custom", "Model_State:") = oMSName
'oOcc.ActiveModelState = oMSName
'get the Document that the component represents, so that we can run the local rule in it
Dim oOccDoc As Document = Nothing 'ensuring no carryover value
oOccDoc = oOcc.Definition.Document
'get the local rule in that document
Dim oRule As iLogicRule = Nothing 'ensuring no carryover value
oAuto.GetRule(oOccDoc, oLocalRuleName)
If Not IsNothing(oRule) Then
oAuto.RunRuleDirect(oRule)
End If
Next
'now main assembly and all components have updated iProperty
'and local rule has been ran in each component to change its ModelState
'now update the main assembly now to ensure changes are in place before export processes
oADoc.Update2(True) 'True = Accept Errors & Continue
'run export process sub routines here
ExportAssemblyToSTEP(oADoc, oModelStateName)
ExportAssemblyToRFA(oADoc, oModelStateName)
Next 'go to next ModelState name in the list we got from Excel
'just a final message to indicate that the process has finished
MsgBox("Finished All Export Processes.", vbInformation, "iLogic")
End Sub
Function GetModelStateNames(oXLFile As String, oXLSheet As String, oXLRow As Integer) As List(Of String)
Dim oExcel As Microsoft.Office.Interop.Excel.Application = GetExcel
If oExcel Is Nothing Then Return Nothing
Dim oWB As Workbook
Try
oWB = oExcel.Workbooks.Open(oXLFile)
Catch oEx As Exception
MsgBox("Failed to open specified Excel file." & vbCrLf & _
oEx.Message & vbCrLf & oEx.StackTrace, vbCritical, "iLogic")
Return Nothing
End Try
Dim oWS As Worksheet
Try
oWS = oWB.Worksheets.Item(oXLSheet)
Catch oEx As Exception
MsgBox("Couldn't find specified Excel Sheet." & vbCrLf & _
oEx.Message & vbCrLf & oEx.StackTrace, vbCritical, "iLogic")
Return Nothing
End Try
'determine last used row, so we know the limits of our loop through the rows
Dim oLastRowUsed As Integer = oWS.UsedRange.Rows.Count
'create variable to hold them all
Dim oNames As New List(Of String)
'loop through rows and add each name to the list
For oRow As Integer = oXLRow To oLastRowUsed
Dim oName As String = String.Empty
If IsNothing(oWS.Range("A" & oRow.ToString)) Then
'if an empty Cell is found in the series, it will stop,
'and return the list without any further names added
Return oNames
Else
oName = CStr(oWS.Range("A" & oRow.ToString).Value2)
End If
oNames.Add(oName)
Next
Return oNames
End Function
Sub ExportAssemblyToSTEP(oAssembly As AssemblyDocument, oMSName As String)
'your code for exporting the assembly to a STEP file here
'get the STEP translator add-in
Dim oSTEP As TranslatorAddIn
For Each oAddIn As ApplicationAddIn In ThisApplication.ApplicationAddIns
If oAddIn.DisplayName = "Translator: STEP" Then
oSTEP = oAddIn
End If
Next
If IsNothing(oSTEP) Then
MsgBox("STEP Translator Add-in not found. Exiting.", vbCritical, "iLogic")
Exit Sub
End If
'create needed variables for translator
oTO = ThisApplication.TransientObjects
oContext = oTO.CreateTranslationContext
oContext.Type = IOMechanismEnum.kFileBrowseIOMechanism
oOptions = oTO.CreateNameValueMap
oDataMedium = oTO.CreateDataMedium
'specify full file name of new STEP file it is to create
'get path of assembly, without file name or file extension
oPath = System.IO.Path.GetDirectoryName(oAssembly.FullFileName)
oDirChar = System.IO.Path.DirectorySeparatorChar
'integrate ModelState name in place of file name
oNewFile = oPath & oDirChar & oMSName & ".stp"
'Check to see if the STEP file already exists, if it does, ask if you want to overwrite it or not.
If System.IO.File.Exists(oNewFile) Then
oAns = MsgBox("A STEP file with this name already exists." & vbCrLf &
"Do you want to overwrite it with this new one?",vbYesNo + vbQuestion + vbDefaultButton2, "STEP FILE EXISTS")
If oAnswer = vbNo Then Exit Sub
End If
oDataMedium.FileName = oNewFile
If oSTEP.HasSaveCopyAsOptions(oAssembly, oContext, oOptions) Then
' Set application protocol.
' 2 = AP 203 - Configuration Controlled Design
' 3 = AP 214 - Automotive Design
oOptions.Value("ApplicationProtocolType") = 3
'oOptions.Value("IncludeSketches") = True
'oOptions.Value("export_fit_tolerance") = .000393701 'minimum
'oOptions.Value("Author") = ThisApplication.GeneralOptions.UserName
'oOptions.Value("Authorization") = ""
'oOptions.Value("Description") = iProperties.Value("Summary", "Title")
'oOptions.Value("Organization") = iProperties.Value("Summary", "Company")
Try
oSTEP.SaveCopyAs(oAssembly, oContext, oOptions, oDataMedium)
Catch
MsgBox("Your attempt to export this document as a STEP file FAILED!", vbOKOnly + vbExclamation, "Export to STEP Error")
End Try
End If
End Sub
Sub ExportAssemblyToRFA(oAssembly As AssemblyDocument, oMSName As String)
oADef = oAssembly.ComponentDefinition
oPath = System.IO.Path.GetDirectoryName(oAssembly.FullFileName)
oDirChr = System.IO.Path.DirectorySeparatorChar
oFFN = oPath & oDirChr & oMSName & ".rfa"
'export
oADef.BIMComponent.ExportBuildingComponent(oFFN)
End Sub
Function GetExcel() As Microsoft.Office.Interop.Excel.Application
Dim oXL As Microsoft.Office.Interop.Excel.Application
Try
'try to find an already running instance of the Excel Application
oXL = GetObject(, "Excel.Application")
Catch
'it wasn't found open, so create an instance of it (start the application)
oXL = CreateObject("Excel.Application")
'oXL = New Microsoft.Office.Interop.Excel.Application
Catch
MsgBox("Failed to Get/Create an instance of the Excel Application. Exiting.", , "")
Exit Function
End Try
Return oXL
End Function
It kind of seems to me like this whole job could be done much simpler though, without needing to use any iProperties, or any other rules. What if we simply got the list of names from Excel (like I'm doing in this last code), then while looping through those names, simply directly change the ActiveModelState of each component to the name in this loop.
Like with this line:
oOcc.ActiveModelState = oMSName
It seems to me like that would eliminate the need for the custom iProperties and running the local rule for each component. Besides, that local rule is running on the Document that the component represents, not just the one specific component occurrence in the assembly. So if there were multiple components representing the same part, and you may want different ones to be at different model states, this would mess that up. I did notice that the local rule seems to be digging down pretty deep before setting the ModelState. Does that main document not have its own set of model states that match the list? I have no idea, because like I said, I'm not familiar with the effects of derived stuff from multi-body parts, so I could be wrong about this last idea.
Wesley Crihfield

(Not an Autodesk Employee)