Загрузка данных


Понял систему координат так: `X Axis` — ось гиба. Центр первого фланца лежит в плоскости `XZ` (зеркало) на расстоянии `Радиус` от `X`. Дуга лежит в плоскости `YZ`, а `Y Axis` параллельна оси первого фланца. В построении я использую только это: рёбра фланцев, плоскость `YZ` (дуга горизонтальна) и направление `Y Axis`. От `XY` ничего не зависит.

Ниже — построение второго патрубка и кольца. Функции `AddNozzle / AddConnectionPipe / AddDryPipe` меняются почти целиком (появляется номер патрубка), поэтому их привожу полностью. Остальное — точечные правки.

---

### Патч 1 — константы имён

Заменить блок `' --- имена компонентов …` (от `NOZZLE` до `TEE`) на:

vb

```vb
' --- общие компоненты (один комплект на РВС) ---
Private Const VERT As String = "Сухотруб верт ГПСС:1"
Private Const TEE As String = "Сухотруб тройник:1"

' --- файлы ---
Private Const DRY_FILE As String = "сухотруб подключения ГПСС.iam"
Private Const DRY_FILE_MIRROR As String = "сухотруб подключения ГПСС-01.iam"   ' ← проверь имя
Private Const RING_FILE As String = "Труба вальцованная ГПСС.iam"

' --- вальцованные трубы: калибруются на модели ---
Private Const RING_TURN_1 As Double = 0       ' угол YZ трубы к XZ РВС, сторона патрубка 1
Private Const RING_TURN_2 As Double = 180     ' то же, сторона патрубка 2
Private Const RING_INSERT_GAP As Double = -2  ' зазор под прокладку между зеркалами
```

Имена компонентов патрубка теперь даёт класс `GpssNames` (патч 6). У патрубка №1 имена прежние, поэтому управляемая группа его не пересоздаёт.

### Патч 2 — `Main`, блок построения

Заменить блок от `ThisAssembly.BeginManage` до `ThisAssembly.EndManage` включительно:

vb

```vb
    ThisAssembly.BeginManage(Group)
    If enabled Then
        Dim g1 As New GpssNames(1)
        AddGpss(g1, gpssAngle)

        Dim g2 As GpssNames = Nothing
        If count >= 2 Then
            g2 = New GpssNames(2)
            AddGpss(g2, gpssAngle + 180)              ' второй — напротив
        End If

        AddVerticalDryPipe()                          ' один на РВС (при 2 ГПСС — на α + 90°)

        If count >= 2 Then
            Try
                Dim ring As RingPlan = CalcRing()
                AddRing(g1, ring, RING_TURN_1)
                AddRing(g2, ring, RING_TURN_2)
            Catch ex As Exception
                errors.Add("Кольцо: " & ex.Message)
            End Try
        End If
    End If
    ThisAssembly.EndManage(Group)       ' всё, что не добавлено в этом запуске, удаляется
```

### Патч 3 — заменить `AddNozzle`, `AddConnectionPipe`, `AddDryPipe` целиком

vb

```vb
' Один патрубок со всей своей цепочкой
Sub AddGpss(g As GpssNames, angle As Double)
    AddNozzle(g, angle)
    AddConnectionPipe(g)
    AddDryPipe(g)
End Sub


' ============================================================================
'  1. ПАТРУБОК, ПЕНОГЕНЕРАТОР, УКРЕПЛИСТ
' ============================================================================
Sub AddNozzle(g As GpssNames, angle As Double)
    Components.Add(g.Nozzle, "Патрубок для ГПСС.iam")
    Components.Add(g.Foam, "Пеногенератор ГПСС.ipt")
    Components.Add(g.Seal, "Прокладка под ГПСС.ipt")
    Components.Add(g.Plate, "Укреп лист на ГПСС.ipt")

    Parameter(g.Nozzle, "Материал") = material
    Parameter(g.Plate, "Толщина") = shellS
    Parameter(g.Plate, "Диаметр") = tankD + 2 * shellS
    Parameter(g.Plate, "Полный") = (gpssH < tankH - 450)     ' 450 = −20 + 100 + 370 (как в исходном коде)

    Dim c As String = g.Tag & " "

    ' --- укреплист на стенке РВС: ось, высота, угол ---
    Constraints.AddMate(c & "Укреплист и РВС", g.Plate, "Y Axis", "", "Y Axis",
                        solutionType := MateConstraintSolutionTypeEnum.kAlignedSolutionType)
    Constraints.AddFlush(c & "Укреплист и РВС 2", g.Plate, "XZ Plane", "", "XZ Plane", -gpssH)
    Constraints.AddAngle(c & "Укреплист и РВС 3", g.Plate, "YZ Plane", "", "YZ Plane", 180 град + angle,
                         AngleConstraintSolutionTypeEnum.kReferenceVectorSolution, "", "Y Axis")

    ' --- патрубок на укреплисте ---
    Constraints.AddMate(c & "Патрубок и укреплист", g.Plate, "XZ Plane", g.Nozzle, "XY Plane")
    Constraints.AddMate(c & "Патрубок и укреплист 2", g.Plate, "YZ Plane", g.Nozzle, "YZ Plane")
    Constraints.AddMate(c & "Патрубок и укреплист 3", g.Nozzle, "XZ Plane", g.Plate, "XY Plane",
                        tankD / 2 + 330 мм)

    ' --- пеногенератор на патрубке ---
    Constraints.AddMate(c & "ось патр и пеногена", g.Nozzle, "Y Axis", g.Foam, "Z Axis",
                        solutionType := MateConstraintSolutionTypeEnum.kAlignedSolutionType)
    Constraints.AddFlush(c & "Патрубок и пеногена", g.Foam, "XY Plane", g.Nozzle, "XZ Plane", 12 мм)
    Constraints.AddMate(c & "Патрубок и пеногена 2", g.Foam, "XZ Plane", g.Nozzle, "XY Plane")

    ' --- прокладка пеногенератора ---
    Constraints.AddMate(c & "Пеногена и прокладка", g.Seal, "ЦентрОсь", g.Foam, "Z Axis",
                        solutionType := MateConstraintSolutionTypeEnum.kAlignedSolutionType)
    Constraints.AddFlush(c & "Пеногена и прокладка 2", g.Seal, "XZ Plane", g.Foam, "XY Plane", -10 мм)
    Constraints.AddAngle(c & "Пеногена и прокладка 3", g.Seal, "XY Plane", g.Foam, "XZ Plane", 0 град,
                         AngleConstraintSolutionTypeEnum.kReferenceVectorSolution, g.Foam, "Y Axis")

    ' --- крепёж пеногенератора: две стороны, сетка 3 × 2 (шаг 170, между рядами 510) ---
    Dim off As Double = NutOffset(22)
    Fasten(g.Tag & "_ПГ1", g.Foam, "Ребро0", 55, 80, off,
           Sub(kit) RectPattern("Метизы " & g.Tag & "_ПГ1:1", kit, 3, 170, g.Foam, "Ребро3", False,
                                2, 510, g.Foam, "Ребро2", True))
    Fasten(g.Tag & "_ПГ2", g.Foam, "Ребро1", 55, 80, off,
           Sub(kit) RectPattern("Метизы " & g.Tag & "_ПГ2:1", kit, 3, 170, g.Foam, "Ребро2", True,
                                2, 510, g.Foam, "Ребро3", False))
End Sub


' ============================================================================
'  2. ТРУБОПРОВОД ПОДКЛЮЧЕНИЯ
' ============================================================================
Sub AddConnectionPipe(g As GpssNames)
    Components.Add(g.Pipe, "Трубопровод подключения ГПСС.iam")
    Parameter(g.Pipe, "Коррозия") = corrosion
    Parameter(g.Pipe, "Материал") = material
    Parameter(g.Pipe, "Тип") = flangeType

    Dim c As String = g.Tag & " "
    Constraints.AddMate(c & "Трубопровод подключения:1", g.Pipe, "Y Axis", g.Foam, "Z Axis",
                        solutionType := MateConstraintSolutionTypeEnum.kAlignedSolutionType)
    Constraints.AddFlush(c & "Трубопровод подключения:2", g.Pipe, "YZ Plane", g.Foam, "YZ Plane")
    Constraints.AddFlush(c & "Трубопровод подключения:3", g.Pipe, "XZ Plane", g.Foam, "XY Plane", 592 мм)

    AddGasket("Прокладка " & g.Tag & "_Подкл:1", {g.Pipe, "Фланец"})

    ' Здесь всегда болты (как в исходном коде). Смещение гайки 55,3 — фиксированное.
    ' ПРОВЕРИТЬ: возможно, должно быть NutOffset(flangeS).
    Fasten(g.Tag & "_Подкл", {g.Pipe, "Фланец"}, "Proxy:Ребро1", 65, 0, -55.3,
           Sub(kit) CircPattern("Метизы " & g.Tag & "_Подкл:1", kit, 4, 90, g.Pipe, "Y Axis"),
           boltOnly := True, washerEdge := "Ребро3")
End Sub


' ============================================================================
'  3. СУХОТРУБ (у 2-го патрубка — зеркальная сборка -01)
' ============================================================================
Sub AddDryPipe(g As GpssNames)
    Components.Add(g.Dry, If(g.N = 1, DRY_FILE, DRY_FILE_MIRROR))
    Parameter(g.Dry, "Материал") = material
    Parameter(g.Dry, "Коррозия") = corrosion
    Parameter(g.Dry, "Направление2") = dryDirection
    Parameter(g.Dry, "Тип") = flangeType

    Dim c As String = g.Tag & " "
    Constraints.AddMate(c & "Сухотруб:1", g.Dry, "XZ Plane", g.Pipe, "XY Plane", 602 мм)
    Constraints.AddFlush(c & "Сухотруб:2", g.Dry, "YZ Plane", g.Pipe, "YZ Plane")
    Constraints.AddFlush(c & "Сухотруб:3", g.Dry, "XY Plane", g.Pipe, "XZ Plane", 350 мм)

    AddGasket("Прокладка " & g.Tag & "_Сух1:1", {g.Dry, "Фланец"})
    AddGasket("Прокладка " & g.Tag & "_Сух2:1", {g.Dry, "Фланец2"})

    ' трубопровод подключения ↔ сухотруб
    Fasten(g.Tag & "_Сух1", {g.Pipe, "Фланец2"}, "РеброДляВставки", 65, 90, NutOffset(flangeS),
           Sub(kit) CircPattern("Метизы " & g.Tag & "_Сух1:1", kit, 4, 90, g.Dry, "Y Axis"))
    ' сухотруб ↔ вертикальный сухотруб (1 ГПСС) или первая вальцованная труба (2 ГПСС)
    Fasten(g.Tag & "_Сух2", {g.Dry, "Фланец2"}, "РеброДляВставки", 65, 90, NutOffset(flangeS),
           Sub(kit) CircPattern("Метизы " & g.Tag & "_Сух2:1", kit, 4, 90, {g.Dry, "Фланец2"}, "X Axis"))
End Sub
```

Имена всех зависимостей теперь начинаются с `g.Tag`, иначе у двух патрубков они бы совпали. Старые зависимости патрубка №1 группа один раз удалит и создаст под новыми именами — это безопасно.

### Патч 4 — `AddRing`

Вставить после `AddTee`:

vb

```vb
' ============================================================================
'  КОЛЬЦО ОДНОЙ СТОРОНЫ: N вальцованных труб цепочкой от сухотруба к тройнику
'  Каждая труба: «Фланец» — в зеркало предыдущего фланца (зазор под прокладку)
'  + угол её YZ (плоскость дуги) к XZ РВС — фиксирует поворот вокруг оси.
'  К тройнику последнюю не привязываем: замкнутого контура нет, стык сходится
'  по расчёту CalcRing. Прокладка и крепёж — на «Фланец2» каждой трубы.
' ============================================================================
Sub AddRing(g As GpssNames, r As RingPlan, turn As Double)
    Dim prev As String() = {g.Dry, "Фланец2"}
    For k As Integer = 1 To r.N
        Dim seg As String = g.Ring(k)
        Components.Add(seg, RING_FILE)
        Parameter(seg, "Радиус") = r.Rho
        Parameter(seg, "Угол") = r.ThetaDeg
        Parameter(seg, "Тип") = flangeType
        Parameter(seg, "Материал") = material
        Parameter(seg, "Коррозия") = corrosion

        Constraints.AddInsert("Вставка " & seg, {seg, "Фланец"}, "Proxy:Ребро2", prev, "Proxy:Ребро2",
                              axesOpposed := True, distance := RING_INSERT_GAP)
        Constraints.AddAngle("Угол " & seg, seg, "YZ Plane", "", "XZ Plane", turn,
                             AngleConstraintSolutionTypeEnum.kReferenceVectorSolution, seg, "Y Axis")

        ' дальний фланец: стык со следующей трубой или с тройником
        Dim far As String() = {seg, "Фланец2"}
        Dim joint As String = g.Tag & "_Кольцо" & k
        AddGasket("Прокладка " & joint & ":1", far)
        Fasten(joint, far, "РеброДляВставки", 65, 90, NutOffset(flangeS),
               Sub(kit) CircPattern("Метизы " & joint & ":1", kit, 4, 90, far, "X Axis"))

        prev = far
    Next
End Sub
```

### Патч 5 — класс `GpssNames`

Добавить в конец правила, рядом с `RingPlan`:

vb

```vb
' Имена компонентов одного патрубка. №1 — прежние (их отслеживает группа),
' №2 — с номером. Tag — префикс соединений, массивов и зависимостей.
Class GpssNames
    Public N As Integer
    Public Tag As String
    Public Nozzle As String, Foam As String, Seal As String, Plate As String
    Public Pipe As String, Dry As String

    Public Sub New(index As Integer)
        N = index
        Dim sfx As String = If(index = 1, "", " " & index)
        Tag = If(index = 1, "ГПСС", "ГПСС" & index)
        Nozzle = "Патрубок ГПСС" & sfx
        Foam = "Пеногенератор ГПСС" & sfx
        Seal = "Прокладка ГПСС" & sfx
        Plate = "Укреплист ГПСС" & sfx
        Pipe = "Трубопровод подключения ГПСС" & sfx
        Dry = "Сухотруб ГПСС:" & index
    End Sub

    Public Function Ring(k As Integer) As String
        Return "Труба вальцованная " & Tag & ":" & k
    End Function
End Class
```

`AddVerticalDryPipe` и `AddTee` не меняются: они уже ставят вертикальный сухотруб на `gpssAngle + dryAngle` (+90° при 2 ГПСС).

---

### Калибровка на первом прогоне (как с отводами змеевика)

1. **Труба гнётся наружу, от стенки** — поменять `RING_TURN_1` (или `_2` для второй стороны) на 0 ↔ 180.
2. **Фланцы «влезли» друг в друга, а не стоят с зазором 2 мм** — поменять знак `RING_INSERT_GAP`.
3. **Последняя труба не попадает во фланец тройника.** Если невязка в проверочном окне ≈ 0, значит модели не совпадают с константами. Проверь `RING_STRAIGHT` (300 у сухотруба и у тройника) и `RING_AXIS_GAP` (ось сухотруба = ось вертикального).

### Нужно подтвердить

- Имя файла зеркального сухотруба — у меня `сухотруб подключения ГПСС-01.iam`. Фланцы в нём тоже называются `Фланец` и `Фланец2`?
- В сборке вальцованной трубы фланец на плоскости `XZ` называется именно `Фланец`, а дальний — `Фланец2`? Параметры называются `Радиус`, `Угол`, `Тип`, `Материал`, `Коррозия`?
- Модели сухотруба и тройника уже переделаны под 300, или пока стоят старые 600 / 350? Если старые, кольцо не сойдётся с тройником — тогда временно поставь `RING_STRAIGHT = 600` и проверяй только сторону сухотруба.