Загрузка данных
Option Explicit
' ============================================================================
' Макрос SolidWorks 2023 VBA
' Назначение:
' 1. В главной сборке читаются два выбранных размера:
' - 1-й выбранный размер = ширина;
' - 2-й выбранный размер = высота.
' 2. Выбирается одна подсборка верхнего уровня.
' 3. В файле подсборки создаётся/используется конфигурация:
' ширина x высота
' В новой конфигурации подсборки:
' D2@LENGHT = ширина;
' D1@LENGHT = высота.
' 4. Для каждой непосредственной детали подсборки создаётся/используется
' конфигурация, имя которой строится по фактическим значениям после
' перестроения:
' D7@LENGHT x D8@LENGHT
' Временная конфигурация создаётся от Proba, в ней:
' D2@LENGHT = ширина;
' D1@LENGHT = высота.
' 5. Конфигурации назначаются только выбранному экземпляру подсборки и
' деталям внутри её новой конфигурации. Другие экземпляры подсборок
' в главной сборке не переключаются.
'
' Важно:
' - Встроенный редактор SolidWorks VBA обычно уже содержит ссылки на:
' SolidWorks <версия> Type Library;
' SolidWorks Constant type library (swconst).
' - Внешние ссылки и Microsoft Scripting Runtime не используются.
' - Компоненты должны быть внешними сохранёнными файлами и разрешёнными
' (resolved). Виртуальные компоненты и подавленные компоненты выдаются
' как ошибка.
' ============================================================================
Private Const DETAIL_BASE_CONFIGURATION As String = "Proba"
Private Const SKETCH_NAME As String = "LENGHT"
Private Const SUBASSEMBLY_HEIGHT_DIM As String = "D1@LENGHT"
Private Const SUBASSEMBLY_WIDTH_DIM As String = "D2@LENGHT"
Private Const DETAIL_HEIGHT_DIM As String = "D1@LENGHT"
Private Const DETAIL_WIDTH_DIM As String = "D2@LENGHT"
Private Const DETAIL_NAME_WIDTH_DIM As String = "D7@LENGHT"
Private Const DETAIL_NAME_HEIGHT_DIM As String = "D8@LENGHT"
' Разделитель имени конфигурации. При необходимости можно заменить на " х ".
Private Const CONFIG_NAME_SEPARATOR As String = " x "
' -----------------------------------------------------------------------------
' Основная процедура. Имя main используется встроенным редактором макросов.
' -----------------------------------------------------------------------------
Public Sub main()
Dim swApp As SldWorks.SldWorks
Dim swAssemblyModel As SldWorks.ModelDoc2
Dim swSelectionMgr As SldWorks.SelectionMgr
Dim swSubassemblyComponent As SldWorks.Component2
Dim widthSystemValue As Double
Dim heightSystemValue As Double
Dim subassemblyConfigurationName As String
Dim report As String
Dim selectionMessage As String
Dim ok As Boolean
Dim saveErrors As Long
Dim saveWarnings As Long
On Error GoTo FatalError
Set swApp = Application.SldWorks
If swApp Is Nothing Then
MsgBox "Не удалось получить объект SolidWorks.", vbCritical, "Макрос сетки"
Exit Sub
End If
Set swAssemblyModel = swApp.ActiveDoc
If swAssemblyModel Is Nothing Then
MsgBox "Нет активного документа. Откройте главную сборку.", vbExclamation, "Макрос сетки"
Exit Sub
End If
If swAssemblyModel.GetType <> swDocASSEMBLY Then
MsgBox "Активный документ не является сборкой." & vbCrLf & _
"Откройте главную сборку и повторите запуск.", _
vbExclamation, "Макрос сетки"
Exit Sub
End If
Set swSelectionMgr = swAssemblyModel.SelectionManager
' Требуется ровно два размера и одна подсборка.
ok = ReadMainAssemblySelection(swAssemblyModel, swSelectionMgr, _
widthSystemValue, heightSystemValue, _
swSubassemblyComponent, selectionMessage)
If Not ok Then
MsgBox selectionMessage, vbExclamation, "Макрос сетки"
Exit Sub
End If
' Формирование имени конфигурации подсборки по единицам главной сборки.
subassemblyConfigurationName = BuildConfigurationName(swAssemblyModel, _
widthSystemValue, _
heightSystemValue)
If Len(subassemblyConfigurationName) = 0 Then
MsgBox "Не удалось сформировать имя конфигурации подсборки.", _
vbCritical, "Макрос сетки"
Exit Sub
End If
report = "Выбрано:" & vbCrLf & _
" Ширина: " & FormatSystemValueForMessage(swAssemblyModel, widthSystemValue) & vbCrLf & _
" Высота: " & FormatSystemValueForMessage(swAssemblyModel, heightSystemValue) & vbCrLf & _
" Подсборка: " & swSubassemblyComponent.Name2 & vbCrLf & _
" Конфигурация подсборки: " & subassemblyConfigurationName & vbCrLf & vbCrLf
' Создание конфигураций в подсборке и деталях.
ok = ProcessSelectedSubassembly(swApp, swAssemblyModel, _
swSubassemblyComponent, _
widthSystemValue, heightSystemValue, _
subassemblyConfigurationName, report)
If Not ok Then
MsgBox "Операция остановлена." & vbCrLf & vbCrLf & report, _
vbCritical, "Макрос сетки"
Exit Sub
End If
' Назначение конфигурации только выбранному экземпляру подсборки.
' Это не переключает другие одинаковые подсборки в главной сборке.
swSubassemblyComponent.ReferencedConfiguration = subassemblyConfigurationName
' Обновляем главную сборку и сохраняем её.
swAssemblyModel.EditRebuild3
swAssemblyModel.ForceRebuild3 True
ok = swAssemblyModel.Save3(swSaveAsOptions_Silent, saveErrors, saveWarnings)
If Not ok Then
AddReport report, "ПРЕДУПРЕЖДЕНИЕ: главная сборка не сохранена. Код ошибки Save3: " & CStr(saveErrors)
Else
AddReport report, "Главная сборка сохранена."
End If
swAssemblyModel.ClearSelection2 True
MsgBox "Готово." & vbCrLf & vbCrLf & report, vbInformation, "Макрос сетки"
Exit Sub
FatalError:
AddReport report, "Критическая ошибка VBA/API: " & Err.Number & " — " & Err.Description
MsgBox report, vbCritical, "Макрос сетки"
End Sub
' -----------------------------------------------------------------------------
' Чтение выбора в главной сборке.
' Ожидаемый порядок выбора:
' 1) размер ширины;
' 2) размер высоты;
' 3) подсборка.
' Для совместного выбора удерживайте Ctrl.
' -----------------------------------------------------------------------------
Private Function ReadMainAssemblySelection( _
ByVal swAssemblyModel As SldWorks.ModelDoc2, _
ByVal swSelectionMgr As SldWorks.SelectionMgr, _
ByRef widthSystemValue As Double, _
ByRef heightSystemValue As Double, _
ByRef selectedSubassembly As SldWorks.Component2, _
ByRef errorMessage As String) As Boolean
Dim selectedCount As Long
Dim i As Long
Dim selectionType As Long
Dim dimensionCount As Long
Dim componentCount As Long
Dim displayDimension As SldWorks.DisplayDimension
Dim dimension As SldWorks.Dimension
Dim selectedComponent As SldWorks.Component2
Dim componentModel As SldWorks.ModelDoc2
Dim componentPath As String
On Error GoTo SelectionError
selectedCount = swSelectionMgr.GetSelectedObjectCount2(-1)
If selectedCount <> 3 Then
errorMessage = "Нужно выбрать ровно 3 объекта в главной сборке:" & vbCrLf & _
"1) размер ширины;" & vbCrLf & _
"2) размер высоты;" & vbCrLf & _
"3) одну подсборку." & vbCrLf & vbCrLf & _
"Для множественного выбора удерживайте Ctrl."
Exit Function
End If
For i = 1 To selectedCount
selectionType = swSelectionMgr.GetSelectedObjectType3(i, -1)
If selectionType = swSelDIMENSIONS Then
dimensionCount = dimensionCount + 1
Set displayDimension = swSelectionMgr.GetSelectedObject6(i, -1)
If displayDimension Is Nothing Then
errorMessage = "Не удалось получить выбранный размер №" & CStr(dimensionCount) & "."
Exit Function
End If
Set dimension = displayDimension.GetDimension2(0)
If dimension Is Nothing Then
errorMessage = "Не удалось получить модельный размер №" & CStr(dimensionCount) & "."
Exit Function
End If
If dimensionCount = 1 Then
' По условию первый выбранный размер — ширина.
widthSystemValue = ReadDimensionSystemValue(dimension)
ElseIf dimensionCount = 2 Then
' По условию второй выбранный размер — высота.
heightSystemValue = ReadDimensionSystemValue(dimension)
End If
ElseIf selectionType = swSelCOMPONENTS Then
componentCount = componentCount + 1
Set selectedComponent = swSelectionMgr.GetSelectedObjectsComponent4(i, -1)
If selectedComponent Is Nothing Then
errorMessage = "Не удалось получить выбранный компонент подсборки."
Exit Function
End If
If componentCount > 1 Then
errorMessage = "Выбрано более одной подсборки. Оставьте только одну подсборку."
Exit Function
End If
Set selectedSubassembly = selectedComponent
Else
errorMessage = "Среди выбранных объектов есть объект неподдерживаемого типа." & vbCrLf & _
"Нужны только два размера и один компонент-подсборка."
Exit Function
End If
Next i
If dimensionCount <> 2 Or componentCount <> 1 Then
errorMessage = "Не найдено требуемое сочетание выбора: два размера и одна подсборка."
Exit Function
End If
If widthSystemValue <= 0# Or heightSystemValue <= 0# Then
errorMessage = "Размеры ширины и высоты должны быть больше нуля."
Exit Function
End If
componentPath = selectedSubassembly.GetPathName
If Len(componentPath) = 0 Then
errorMessage = "Выбранная подсборка не имеет пути к файлу." & vbCrLf & _
"Виртуальные подсборки этим макросом не поддерживаются."
Exit Function
End If
If LCase$(Right$(componentPath, 6)) <> ".sldasm" Then
errorMessage = "Выбранный компонент не является файлом подсборки (*.SLDASM)."
Exit Function
End If
' Проверка доступности модельного документа. Для облегчённых компонентов
' GetModelDoc2 может вернуть Nothing — в таком случае просим разрешить его.
Set componentModel = selectedSubassembly.GetModelDoc2
If componentModel Is Nothing Then
errorMessage = "Не удалось получить модель выбранной подсборки." & vbCrLf & _
"Перед запуском переведите подсборку в состояние Resolved " & _
"(не Lightweight и не Suppressed)."
Exit Function
End If
If componentModel.GetType <> swDocASSEMBLY Then
errorMessage = "Выбранный компонент по пути имеет расширение SLDASM, " & _
"но SolidWorks не распознал его как сборку."
Exit Function
End If
ReadMainAssemblySelection = True
Exit Function
SelectionError:
errorMessage = "Ошибка при чтении выбора: " & Err.Number & " — " & Err.Description
End Function
' -----------------------------------------------------------------------------
' Обработка выбранной подсборки.
' -----------------------------------------------------------------------------
Private Function ProcessSelectedSubassembly( _
ByVal swApp As SldWorks.SldWorks, _
ByVal swAssemblyModel As SldWorks.ModelDoc2, _
ByVal selectedSubassembly As SldWorks.Component2, _
ByVal widthSystemValue As Double, _
ByVal heightSystemValue As Double, _
ByVal targetConfigurationName As String, _
ByRef report As String) As Boolean
Dim subassemblyPath As String
Dim subassemblyModel As SldWorks.ModelDoc2
Dim wasAlreadyOpen As Boolean
Dim openedByMacro As Boolean
Dim oldConfigurationName As String
Dim baseConfigurationName As String
Dim configuration As SldWorks.Configuration
Dim vChildren As Variant
Dim child As SldWorks.Component2
Dim childCount As Long
Dim partCount As Long
Dim i As Long
Dim childName As String
Dim childPath As String
Dim childNames() As String
Dim childPaths() As String
Dim detailConfigurationName As String
Dim detailReport As String
Dim saveErrors As Long
Dim saveWarnings As Long
Dim ok As Boolean
On Error GoTo ProcessError
subassemblyPath = selectedSubassembly.GetPathName
If Len(subassemblyPath) = 0 Then
Err.Raise vbObjectError + 1000, , "У выбранной подсборки отсутствует путь к файлу."
End If
Set subassemblyModel = GetOrOpenModel(swApp, subassemblyPath, swDocASSEMBLY, _
wasAlreadyOpen, openedByMacro)
If subassemblyModel Is Nothing Then
Err.Raise vbObjectError + 1001, , "Не удалось открыть файл подсборки:" & vbCrLf & subassemblyPath
End If
If subassemblyModel.GetType <> swDocASSEMBLY Then
Err.Raise vbObjectError + 1002, , "Файл выбранной подсборки не является сборкой."
End If
oldConfigurationName = GetActiveConfigurationName(subassemblyModel)
baseConfigurationName = selectedSubassembly.ReferencedConfiguration
If Len(baseConfigurationName) = 0 Then baseConfigurationName = oldConfigurationName
Set configuration = subassemblyModel.GetConfigurationByName(targetConfigurationName)
If Not configuration Is Nothing Then
' Если конфигурация уже существует, согласно требованию пользователя
' не меняем её размеры и дочерние компоненты — только используем её.
If Not subassemblyModel.ShowConfiguration2(targetConfigurationName) Then
Err.Raise vbObjectError + 1003, , _
"Не удалось активировать уже существующую конфигурацию подсборки: " & _
targetConfigurationName
End If
AddReport report, "Конфигурация подсборки уже существовала и не изменялась: " & _
targetConfigurationName
ProcessSelectedSubassembly = True
GoTo CleanExit
End If
If Len(baseConfigurationName) = 0 Then baseConfigurationName = "Default"
Set configuration = subassemblyModel.GetConfigurationByName(baseConfigurationName)
If configuration Is Nothing Then
Err.Raise vbObjectError + 1004, , _
"Базовая конфигурация подсборки не найдена: " & baseConfigurationName
End If
If Not subassemblyModel.ShowConfiguration2(baseConfigurationName) Then
Err.Raise vbObjectError + 1005, , _
"Не удалось активировать базовую конфигурацию подсборки: " & _
baseConfigurationName
End If
' AddConfiguration3 без флага Derived создаёт самостоятельную конфигурацию,
' наследующую текущее состояние активной конфигурации.
Set configuration = subassemblyModel.AddConfiguration3(targetConfigurationName, "", "", 0)
If configuration Is Nothing Then
Err.Raise vbObjectError + 1006, , _
"Не удалось создать конфигурацию подсборки: " & targetConfigurationName
End If
If Not subassemblyModel.ShowConfiguration2(targetConfigurationName) Then
Err.Raise vbObjectError + 1007, , _
"Не удалось активировать новую конфигурацию подсборки: " & _
targetConfigurationName
End If
' В новой конфигурации подсборки меняются только два параметра сетки.
SetDimensionSystemValue subassemblyModel, SUBASSEMBLY_WIDTH_DIM, widthSystemValue, _
"подсборке, ширина"
SetDimensionSystemValue subassemblyModel, SUBASSEMBLY_HEIGHT_DIM, heightSystemValue, _
"подсборке, высота"
subassemblyModel.EditRebuild3
subassemblyModel.ForceRebuild3 True
' Сохраняем имена и пути непосредственных дочерних компонентов заранее.
' После переключения ReferencedConfiguration массивы GetChildren могут
' стать недействительными, поэтому ниже используются имена компонентов.
Set configuration = subassemblyModel.GetActiveConfiguration
If configuration Is Nothing Then
Err.Raise vbObjectError + 1008, , "Не удалось получить активную конфигурацию подсборки."
End If
Set child = configuration.GetRootComponent
If child Is Nothing Then
Err.Raise vbObjectError + 1009, , "Не удалось получить корневой компонент подсборки."
End If
vChildren = child.GetChildren
If IsEmpty(vChildren) Then
Err.Raise vbObjectError + 1010, , "В выбранной подсборке нет дочерних компонентов."
End If
childCount = UBound(vChildren) - LBound(vChildren) + 1
ReDim childNames(1 To childCount)
ReDim childPaths(1 To childCount)
For i = LBound(vChildren) To UBound(vChildren)
Set child = vChildren(i)
childName = child.Name2
childPath = child.GetPathName
If Len(childName) = 0 Then
Err.Raise vbObjectError + 1011, , "Один из дочерних компонентов не имеет имени."
End If
childNames(i - LBound(vChildren) + 1) = childName
childPaths(i - LBound(vChildren) + 1) = childPath
If Len(childPath) = 0 Then
AddReport report, "ПРОПУСК: виртуальный или недоступный компонент " & childName
ElseIf LCase$(Right$(childPath, 6)) = ".sldprt" Then
partCount = partCount + 1
ElseIf LCase$(Right$(childPath, 6)) = ".sldasm" Then
AddReport report, "ПРОПУСК: вложенная подсборка " & childName & _
" (обрабатываются только детали первого уровня)."
Else
AddReport report, "ПРОПУСК: неподдерживаемый тип компонента " & childName & _
" — " & childPath
End If
Next i
If partCount = 0 Then
Err.Raise vbObjectError + 1012, , _
"В выбранной подсборке не найдено ни одной непосредственной детали (*.SLDPRT)."
End If
' Обрабатываем каждую непосредственную деталь и назначаем конфигурацию
' конкретному экземпляру в текущей конфигурации подсборки.
For i = 1 To childCount
childName = childNames(i)
childPath = childPaths(i)
If LCase$(Right$(childPath, 6)) = ".sldprt" Then
detailReport = ""
detailConfigurationName = ""
ok = ProcessDetailPart(swApp, childPath, widthSystemValue, _
heightSystemValue, detailConfigurationName, _
detailReport)
If Not ok Then
If Len(detailReport) > 0 Then AddReport report, detailReport
Err.Raise vbObjectError + 1013, , _
"Не удалось обработать деталь " & childName & "."
End If
If Len(detailReport) > 0 Then AddReport report, detailReport
ok = SetDirectChildReferencedConfiguration(subassemblyModel, childName, _
detailConfigurationName, detailReport)
If Not ok Then
If Len(detailReport) > 0 Then AddReport report, detailReport
Err.Raise vbObjectError + 1014, , _
"Не удалось назначить конфигурацию детали " & childName & "."
End If
AddReport report, " " & childName & " -> " & detailConfigurationName
End If
Next i
subassemblyModel.EditRebuild3
subassemblyModel.ForceRebuild3 True
ok = subassemblyModel.Save3(swSaveAsOptions_Silent, saveErrors, saveWarnings)
If Not ok Then
Err.Raise vbObjectError + 1015, , _
"Не удалось сохранить подсборку. Код Save3: " & CStr(saveErrors) & _
vbCrLf & subassemblyPath
End If
AddReport report, "Подсборка сохранена: " & subassemblyPath
ProcessSelectedSubassembly = True
CleanExit:
On Error Resume Next
' Возвращаем открытый ранее документ к его исходной активной конфигурации.
If wasAlreadyOpen Then
If Len(oldConfigurationName) > 0 Then
subassemblyModel.ShowConfiguration2 oldConfigurationName
End If
ElseIf openedByMacro Then
swApp.CloseDoc subassemblyModel.GetTitle
End If
On Error GoTo 0
Exit Function
ProcessError:
AddReport report, "Ошибка подсборки: " & Err.Number & " — " & Err.Description
ProcessSelectedSubassembly = False
On Error Resume Next
If Not subassemblyModel Is Nothing Then
If wasAlreadyOpen Then
If Len(oldConfigurationName) > 0 Then subassemblyModel.ShowConfiguration2 oldConfigurationName
ElseIf openedByMacro Then
swApp.CloseDoc subassemblyModel.GetTitle
End If
End If
On Error GoTo 0
End Function
' -----------------------------------------------------------------------------
' Обработка одной детали.
' Если целевая конфигурация уже существует, она не изменяется.
' Чтобы получить её имя по D7/D8 после перестроения, используется временная
' конфигурация. Она удаляется, если целевая конфигурация уже была в файле.
' -----------------------------------------------------------------------------
Private Function ProcessDetailPart( _
ByVal swApp As SldWorks.SldWorks, _
ByVal partPath As String, _
ByVal widthSystemValue As Double, _
ByVal heightSystemValue As Double, _
ByRef targetConfigurationName As String, _
ByRef report As String) As Boolean
Dim partModel As SldWorks.ModelDoc2
Dim wasAlreadyOpen As Boolean
Dim openedByMacro As Boolean
Dim oldConfigurationName As String
Dim tempConfigurationName As String
Dim tempConfiguration As SldWorks.Configuration
Dim finalConfiguration As SldWorks.Configuration
Dim d7 As SldWorks.Dimension
Dim d8 As SldWorks.Dimension
Dim d7SystemValue As Double
Dim d8SystemValue As Double
Dim saveErrors As Long
Dim saveWarnings As Long
Dim ok As Boolean
Dim cleanupConfiguration As SldWorks.Configuration
On Error GoTo DetailError
If Len(partPath) = 0 Then
Err.Raise vbObjectError + 2000, , "Путь к детали пустой."
End If
Set partModel = GetOrOpenModel(swApp, partPath, swDocPART, _
wasAlreadyOpen, openedByMacro)
If partModel Is Nothing Then
Err.Raise vbObjectError + 2001, , "Не удалось открыть деталь:" & vbCrLf & partPath
End If
If partModel.GetType <> swDocPART Then
Err.Raise vbObjectError + 2002, , "Файл не является деталью: " & partPath
End If
oldConfigurationName = GetActiveConfigurationName(partModel)
Set finalConfiguration = partModel.GetConfigurationByName(DETAIL_BASE_CONFIGURATION)
If finalConfiguration Is Nothing Then
Err.Raise vbObjectError + 2003, , _
"В детали отсутствует базовая конфигурация " & DETAIL_BASE_CONFIGURATION & "."
End If
' Временная конфигурация создаётся именно от Proba.
If Not partModel.ShowConfiguration2(DETAIL_BASE_CONFIGURATION) Then
Err.Raise vbObjectError + 2004, , _
"Не удалось активировать конфигурацию " & DETAIL_BASE_CONFIGURATION & "."
End If
tempConfigurationName = MakeUniqueTemporaryConfigurationName(partModel)
Set tempConfiguration = partModel.AddConfiguration3(tempConfigurationName, "", "", 0)
If tempConfiguration Is Nothing Then
Err.Raise vbObjectError + 2005, , "Не удалось создать временную конфигурацию детали."
End If
If Not partModel.ShowConfiguration2(tempConfigurationName) Then
Err.Raise vbObjectError + 2006, , "Не удалось активировать временную конфигурацию детали."
End If
' Размеры D1/D2 меняются только в новой временной конфигурации.
SetDimensionSystemValue partModel, DETAIL_WIDTH_DIM, widthSystemValue, _
"детали, ширина"
SetDimensionSystemValue partModel, DETAIL_HEIGHT_DIM, heightSystemValue, _
"детали, высота"
partModel.EditRebuild3
partModel.ForceRebuild3 True
Set d7 = partModel.Parameter(DETAIL_NAME_WIDTH_DIM)
If d7 Is Nothing Then
Err.Raise vbObjectError + 2007, , _
"В детали не найден размер " & DETAIL_NAME_WIDTH_DIM & "."
End If
Set d8 = partModel.Parameter(DETAIL_NAME_HEIGHT_DIM)
If d8 Is Nothing Then
Err.Raise vbObjectError + 2008, , _
"В детали не найден размер " & DETAIL_NAME_HEIGHT_DIM & "."
End If
d7SystemValue = ReadDimensionSystemValue(d7)
d8SystemValue = ReadDimensionSystemValue(d8)
' Имя: фактическая ширина D7 после перестроения x фактическая высота D8.
targetConfigurationName = BuildConfigurationName(partModel, d7SystemValue, d8SystemValue)
If Len(targetConfigurationName) = 0 Then
Err.Raise vbObjectError + 2009, , "Не удалось сформировать имя конфигурации детали."
End If
Set finalConfiguration = partModel.GetConfigurationByName(targetConfigurationName)
If finalConfiguration Is Nothing Then
' Целевого имени ещё нет: переименовываем временную конфигурацию.
Set tempConfiguration = partModel.GetConfigurationByName(tempConfigurationName)
If tempConfiguration Is Nothing Then
Err.Raise vbObjectError + 2010, , "Временная конфигурация детали потеряна."
End If
tempConfiguration.Name = targetConfigurationName
AddReport report, "Деталь " & partModel.GetTitle & ": создана конфигурация " & _
targetConfigurationName
Else
' Целевая конфигурация уже существовала. Не изменяем её, а удаляем только
' временную конфигурацию после выхода из неё.
If Not partModel.ShowConfiguration2(DETAIL_BASE_CONFIGURATION) Then
Err.Raise vbObjectError + 2011, , _
"Не удалось выйти из временной конфигурации детали."
End If
ok = partModel.DeleteConfiguration2(tempConfigurationName)
If Not ok Then
Err.Raise vbObjectError + 2012, , _
"Не удалось удалить временную конфигурацию детали: " & _
tempConfigurationName
End If
If Not partModel.ShowConfiguration2(targetConfigurationName) Then
Err.Raise vbObjectError + 2013, , _
"Не удалось активировать существующую конфигурацию детали: " & _
targetConfigurationName
End If
AddReport report, "Деталь " & partModel.GetTitle & ": конфигурация уже существовала " & _
"и не изменялась — " & targetConfigurationName
End If
partModel.EditRebuild3
partModel.ForceRebuild3 True
ok = partModel.Save3(swSaveAsOptions_Silent, saveErrors, saveWarnings)
If Not ok Then
Err.Raise vbObjectError + 2014, , _
"Не удалось сохранить деталь. Код Save3: " & CStr(saveErrors) & _
vbCrLf & partPath
End If
ProcessDetailPart = True
DetailCleanExit:
On Error Resume Next
If wasAlreadyOpen Then
If Len(oldConfigurationName) > 0 Then
partModel.ShowConfiguration2 oldConfigurationName
End If
ElseIf openedByMacro Then
swApp.CloseDoc partModel.GetTitle
End If
On Error GoTo 0
Exit Function
DetailError:
AddReport report, "Ошибка детали " & partPath & ": " & Err.Number & " — " & Err.Description
ProcessDetailPart = False
' Пытаемся удалить временную конфигурацию при ошибке, чтобы не оставлять
' технические конфигурации в файле.
On Error Resume Next
If Not partModel Is Nothing Then
If Len(tempConfigurationName) > 0 Then
Set cleanupConfiguration = partModel.GetConfigurationByName(tempConfigurationName)
If Not cleanupConfiguration Is Nothing Then
partModel.ShowConfiguration2 DETAIL_BASE_CONFIGURATION
partModel.DeleteConfiguration2 tempConfigurationName
End If
End If
If wasAlreadyOpen Then
If Len(oldConfigurationName) > 0 Then partModel.ShowConfiguration2 oldConfigurationName
ElseIf openedByMacro Then
swApp.CloseDoc partModel.GetTitle
End If
End If
On Error GoTo 0
End Function
' -----------------------------------------------------------------------------
' Назначение конфигурации конкретному непосредственному компоненту подсборки.
' Поиск выполняется заново после возможного Resolve, потому что SolidWorks
' может сделать ранее полученный массив компонентов недействительным.
' -----------------------------------------------------------------------------
Private Function SetDirectChildReferencedConfiguration( _
ByVal subassemblyModel As SldWorks.ModelDoc2, _
ByVal childName As String, _
ByVal targetConfigurationName As String, _
ByRef errorMessage As String) As Boolean
Dim subassemblyConfiguration As SldWorks.Configuration
Dim rootComponent As SldWorks.Component2
Dim vChildren As Variant
Dim child As SldWorks.Component2
Dim i As Long
Dim suppressionState As Long
Dim suppressionResult As Long
Dim found As Boolean
On Error GoTo AssignError
For i = 1 To 2
Set subassemblyConfiguration = subassemblyModel.GetActiveConfiguration
If subassemblyConfiguration Is Nothing Then
errorMessage = "Не удалось получить активную конфигурацию подсборки."
Exit Function
End If
Set rootComponent = subassemblyConfiguration.GetRootComponent
If rootComponent Is Nothing Then
errorMessage = "Не удалось получить корневой компонент подсборки."
Exit Function
End If
vChildren = rootComponent.GetChildren
If IsEmpty(vChildren) Then
errorMessage = "В активной конфигурации подсборки отсутствуют дочерние компоненты."
Exit Function
End If
found = False
For Each child In vChildren
If StrComp(child.Name2, childName, vbBinaryCompare) = 0 Then
found = True
suppressionState = child.GetSuppression
If suppressionState = swComponentSuppressed Then
errorMessage = "Компонент " & childName & " подавлен."
Exit Function
End If
If suppressionState = swComponentLightweight Then
' Переводим только этот компонент в Resolved.
suppressionResult = child.SetSuppression2(swComponentFullyResolved)
If suppressionResult = 0 Then
' SolidWorks мог вернуть код ошибки; всё равно ниже
' повторно перечитаем объект и проверим его состояние.
End If
Exit For
End If
child.ReferencedConfiguration = targetConfigurationName
SetDirectChildReferencedConfiguration = True
Exit Function
End If
Next child
If Not found Then
errorMessage = "Компонент не найден в активной конфигурации подсборки: " & childName
Exit Function
End If
' После Resolve начинаем поиск заново.
Next i
errorMessage = "Не удалось перевести компонент в Resolved и назначить конфигурацию: " & childName
Exit Function
AssignError:
errorMessage = "Ошибка назначения конфигурации компоненту " & childName & ": " & _
Err.Number & " — " & Err.Description
End Function
' -----------------------------------------------------------------------------
' Открыть документ, если он ещё не открыт.
' -----------------------------------------------------------------------------
Private Function GetOrOpenModel( _
ByVal swApp As SldWorks.SldWorks, _
ByVal filePath As String, _
ByVal documentType As Long, _
ByRef wasAlreadyOpen As Boolean, _
ByRef openedByMacro As Boolean) As SldWorks.ModelDoc2
Dim model As SldWorks.ModelDoc2
Dim openErrors As Long
Dim openWarnings As Long
On Error GoTo OpenError
Set model = swApp.GetOpenDocumentByName(filePath)
If Not model Is Nothing Then
wasAlreadyOpen = True
openedByMacro = False
Set GetOrOpenModel = model
Exit Function
End If
Set model = swApp.OpenDoc6(filePath, documentType, swOpenDocOptions_Silent, "", _
openErrors, openWarnings)
If model Is Nothing Then
wasAlreadyOpen = False
openedByMacro = False
Set GetOrOpenModel = Nothing
Exit Function
End If
wasAlreadyOpen = False
openedByMacro = True
Set GetOrOpenModel = model
Exit Function
OpenError:
wasAlreadyOpen = False
openedByMacro = False
Set GetOrOpenModel = Nothing
End Function
' -----------------------------------------------------------------------------
' Установить размер в текущей конфигурации документа.
' Значение передаётся в системных единицах SolidWorks — метрах.
' -----------------------------------------------------------------------------
Private Sub SetDimensionSystemValue( _
ByVal model As SldWorks.ModelDoc2, _
ByVal dimensionName As String, _
ByVal newSystemValue As Double, _
ByVal contextName As String)
Dim dimension As SldWorks.Dimension
Dim setResult As Long
Set dimension = model.Parameter(dimensionName)
If dimension Is Nothing Then
Err.Raise vbObjectError + 3000, , _
"Не найден размер " & dimensionName & " в " & contextName & "."
End If
If dimension.ReadOnly Then
Err.Raise vbObjectError + 3001, , _
"Размер " & dimensionName & " доступен только для чтения в " & contextName & "."
End If
setResult = dimension.SetSystemValue3(newSystemValue, _
swSetValue_InThisConfiguration, Empty)
If setResult <> swSetValue_Successful Then
Err.Raise vbObjectError + 3002, , _
"SolidWorks не принял новое значение размера " & dimensionName & _
" в " & contextName & ". Код SetSystemValue3: " & CStr(setResult)
End If
End Sub
' -----------------------------------------------------------------------------
' Системное значение Dimension в метрах.
' GetSystemValue3 возвращает Variant; в некоторых режимах это массив.
' -----------------------------------------------------------------------------
Private Function ReadDimensionSystemValue(ByVal dimension As SldWorks.Dimension) As Double
Dim value As Variant
value = dimension.GetSystemValue3(swThisConfiguration, Empty)
If IsArray(value) Then
ReadDimensionSystemValue = CDbl(value(LBound(value)))
Else
ReadDimensionSystemValue = CDbl(value)
End If
End Function
' -----------------------------------------------------------------------------
' Сформировать имя конфигурации из значений размеров.
' SolidWorks API хранит длину в метрах, а GetUserUnit переводит её в единицы
' документа (например, mm, cm, inch). Единица измерения в имя не добавляется.
' Десятичный разделитель берётся из региональных настроек Windows через Format$.
' -----------------------------------------------------------------------------
Private Function BuildConfigurationName( _
ByVal model As SldWorks.ModelDoc2, _
ByVal firstSystemValue As Double, _
ByVal secondSystemValue As Double) As String
Dim firstText As String
Dim secondText As String
firstText = FormatSystemValueForConfigName(model, firstSystemValue)
secondText = FormatSystemValueForConfigName(model, secondSystemValue)
If Len(firstText) = 0 Or Len(secondText) = 0 Then
BuildConfigurationName = ""
Else
BuildConfigurationName = SanitizeConfigurationName(firstText & _
CONFIG_NAME_SEPARATOR & _
secondText)
End If
End Function
Private Function FormatSystemValueForConfigName( _
ByVal model As SldWorks.ModelDoc2, _
ByVal systemValue As Double) As String
Dim userUnit As SldWorks.UserUnit
Dim userValue As Double
Dim text As String
On Error GoTo FormatError
Set userUnit = model.GetUserUnit(swLengthUnit)
If userUnit Is Nothing Then GoTo FormatError
userValue = systemValue * userUnit.GetConversionFactor
' До 8 знаков после запятой, без незначащих нулей.
text = Format$(userValue, "0.########")
text = Replace$(text, ChrW$(160), "")
text = Trim$(text)
FormatSystemValueForConfigName = text
Exit Function
FormatError:
' Запасной вариант для крайне редких случаев недоступности UserUnit.
text = Format$(systemValue * 1000#, "0.########")
FormatSystemValueForConfigName = Trim$(text)
End Function
Private Function FormatSystemValueForMessage( _
ByVal model As SldWorks.ModelDoc2, _
ByVal systemValue As Double) As String
FormatSystemValueForMessage = FormatSystemValueForConfigName(model, systemValue)
End Function
' -----------------------------------------------------------------------------
' Удаление символов, которые SolidWorks не принимает в имени конфигурации.
' -----------------------------------------------------------------------------
Private Function SanitizeConfigurationName(ByVal nameText As String) As String
Dim result As String
result = Trim$(nameText)
result = Replace$(result, "\", "_")
result = Replace$(result, "/", "_")
result = Replace$(result, ":", "_")
result = Replace$(result, "*", "_")
result = Replace$(result, "?", "_")
result = Replace$(result, Chr$(34), "_")
result = Replace$(result, "<", "_")
result = Replace$(result, ">", "_")
result = Replace$(result, "|", "_")
SanitizeConfigurationName = Trim$(result)
End Function
' -----------------------------------------------------------------------------
' Уникальное имя временной конфигурации детали.
' -----------------------------------------------------------------------------
Private Function MakeUniqueTemporaryConfigurationName( _
ByVal model As SldWorks.ModelDoc2) As String
Dim baseName As String
Dim candidate As String
Dim index As Long
Dim existingConfiguration As SldWorks.Configuration
baseName = "__GRID_TMP_" & Format$(Now, "yyyymmdd_hhnnss")
candidate = baseName
index = 0
Set existingConfiguration = model.GetConfigurationByName(candidate)
Do While Not existingConfiguration Is Nothing
index = index + 1
candidate = baseName & "_" & CStr(index)
Set existingConfiguration = model.GetConfigurationByName(candidate)
Loop
MakeUniqueTemporaryConfigurationName = candidate
End Function
Private Function GetActiveConfigurationName(ByVal model As SldWorks.ModelDoc2) As String
Dim configuration As SldWorks.Configuration
On Error GoTo ConfigurationError
Set configuration = model.GetActiveConfiguration
If Not configuration Is Nothing Then
GetActiveConfigurationName = configuration.Name
Else
GetActiveConfigurationName = ""
End If
Exit Function
ConfigurationError:
GetActiveConfigurationName = ""
End Function
Private Sub AddReport(ByRef report As String, ByVal lineText As String)
If Len(report) > 0 Then report = report & vbCrLf
report = report & lineText
End Sub