# ONEBOT 直线气缸目录【源数据生成器】(统一路线, 2026-08-22): # 系列定义 meta JSON + 参数表 CSV + 简化几何 STEP → 目录源目录 onebot-data\catalog\ # .opc 打包改由 C# 统一内核完成: 维护窗口【重建全量目录】或 CLI: # OnebotCatalog\bin\OnebotCatalog.exe --buildfull onebot-data\catalog sample\OnebotCatalog_<版本>.opc # 数据来源: 《ONEBOT - 直线气缸 - 中文.pdf》订购码与规格参数表 (2026-08 提取) # 说明: # - STEP 为简化圆柱组合占位模型 (尺寸随缸径/行程缩放), 待 NX 母模批量导出后替换同名文件即可 # - 相同几何的型号组合共用同一 STEP (磁石/固定形式/杆材/后盖不改变简化几何) # - 行程取标准档位 10/20/30/50/75/100; 手册标注"可调行程 25~1000mm"属参数化二期 # - 本脚本只负责【生成/刷新源数据】(几何+CSV+系列定义); 目录打包/发布走 C# 统一路线 # 用法: powershell -NoProfile -ExecutionPolicy Bypass -File onebot-data\tools\gen-onebot-source.ps1 param( [string]$OutDir = (Join-Path $PSScriptRoot '..\catalog') ) $ProgressPreference = 'SilentlyContinue' $inv = [System.Globalization.CultureInfo]::InvariantCulture $stepDir = Join-Path $OutDir 'step' $csvDir = Join-Path $OutDir 'csv' $seriesDir = Join-Path $OutDir 'series' $manDir = Join-Path $OutDir 'manual' New-Item -ItemType Directory -Force -Path $stepDir | Out-Null New-Item -ItemType Directory -Force -Path $csvDir | Out-Null New-Item -ItemType Directory -Force -Path $seriesDir | Out-Null # ==================== 系列与分类定义 ==================== $SERIES = New-Object System.Collections.Generic.List[object] $SERIES.Add(@{ code='KC'; cat='AUT'; zh='KC 系列标准气缸 (ISO15552)'; en='KC Series Standard Cylinder (ISO15552)'; kw='kc iso15552 biaozhun qigang' }) $SERIES.Add(@{ code='KCB'; cat='AUT'; zh='KCB 系列倍力气缸'; en='KCB Series Tandem Cylinder'; kw='kcb beili qigang tandem' }) $SERIES.Add(@{ code='KSB'; cat='AUT'; zh='KSB 系列多位置气缸'; en='KSB Series Multi-position Cylinder'; kw='ksb duoweizhi multi-position' }) $SERIES.Add(@{ code='KCS'; cat='AUT'; zh='KCS 系列超经济型标准气缸'; en='KCS Series Economy Standard Cylinder'; kw='kcs chaojingji economy' }) $SERIES.Add(@{ code='KS'; cat='AUT'; zh='KS 系列拉杆式标准气缸'; en='KS Series Tie-rod Standard Cylinder'; kw='ks lagan tie-rod' }) $SERIES.Add(@{ code='KSU'; cat='AUT'; zh='KSU 系列拉杆内藏式标准气缸'; en='KSU Series Concealed Tie-rod Cylinder'; kw='ksu lagan neicang concealed' }) $SERIES.Add(@{ code='M'; cat='MIN'; zh='M 系列不锈钢迷你缸 (ISO6432)'; en='M Series Stainless Mini Cylinder (ISO6432)'; kw='m iso6432 minigang stainless mini' }) $SERIES.Add(@{ code='MS'; cat='MIN'; zh='MS 系列不锈钢迷你缸 (ISO6432)'; en='MS Series Stainless Mini Cylinder (ISO6432)'; kw='ms iso6432 minigang stainless mini' }) $SERIES.Add(@{ code='MSC'; cat='MIN'; zh='MSC 系列不锈钢迷你缸 (ISO6432)'; en='MSC Series Stainless Mini Cylinder (ISO6432)'; kw='msc iso6432 minigang stainless mini' }) $SERIES.Add(@{ code='MAL'; cat='ALU'; zh='MAL 系列铝合金迷你缸'; en='MAL Series Aluminium Mini Cylinder'; kw='mal lvhejin minigang aluminium mini' }) $SERIES.Add(@{ code='MALC'; cat='ALU'; zh='MALC 系列铝合金迷你缸'; en='MALC Series Aluminium Mini Cylinder'; kw='malc lvhejin minigang aluminium mini' }) $CATS = New-Object System.Collections.Generic.List[object] $CATS.Add(@{ code='AUT'; zh='汽车行业气缸 Automotive'; en='Automotive Cylinders' }) $CATS.Add(@{ code='MIN'; zh='不锈钢迷你缸 (ISO6432)'; en='Stainless Mini Cylinders (ISO6432)' }) $CATS.Add(@{ code='ALU'; zh='铝合金迷你缸'; en='Aluminium Mini Cylinders' }) $MOUNT_DISP = @{ ''='基本型'; FA='前盖固定型 FA'; FB='后盖固定型 FB'; CA='后盖单耳固定型 CA'; CB='后盖双耳固定型 CB'; LB='前后固定型 LB'; YB='后盖支座固定型 YB'; TC='中间摇摆式 TC'; 'TC-M'='摇摆式附脚座 TC-M'; SDB='后盖摇摆式 SDB' } $MAGNET_DISP = @{ ''='不附磁石'; M='附磁石 M' } $ROD_DISP = @{ ''='碳钢'; E='不锈钢 E' } $CAP_DISP = @{ ''='标准摆尾型'; U='标准平尾型 U'; CM='标准圆尾型 CM' } $STROKES = @(10, 20, 30, 50, 75, 100) $STROKES_KSB = @(0, 10, 20, 30, 50, 75, 100) $BORE_STD = @(32, 40, 50, 63, 80, 100, 125) $BORE_KC = @(32, 40, 50, 63, 80, 100, 125, 160, 200, 250, 320) $BORE_KCS = @(32, 40, 50, 63, 80, 100) $BORE_M = @(8, 10, 12, 16, 20, 25, 32, 40) $BORE_MINI = @(16, 20, 25, 32, 40) $MOUNT_STD = @('', 'FA', 'FB', 'CA', 'CB', 'LB', 'YB') $MOUNT_TIE = @('', 'FA', 'FB', 'CA', 'CB', 'LB', 'TC', 'TC-M') $MOUNT_MINI = @('', 'LB', 'FA', 'FB', 'SDB') # 每系列: 参数定义 (code/zh/en/unit/type/values) + 变体组合规则 function Get-SeriesSpec([string]$code) { switch ($code) { 'KC' { $params = @( @('type', '型号', 'Type', 'enum', @('00', '02', '03')), @('bore', '缸径', 'Bore', 'number', $BORE_KC), @('stroke', '行程', 'Stroke', 'number', $STROKES), @('magnet', '磁石', 'Magnet', 'enum', @('', 'M')), @('mount', '固定形式', 'Mounting', 'enum', $MOUNT_STD) ) $rules = @() $rules += @{ if=@{param='mount'; op='eq'; value='YB'}; then=@{param='bore'; op='in'; values=@(32,40,50,63,80,100,125)} } return @{ params=$params; rules=$rules; twin=$false } } 'KCB' { $params = @( @('bore', '缸径', 'Bore', 'number', $BORE_STD), @('stroke', '行程', 'Stroke', 'number', $STROKES), @('magnet', '磁石', 'Magnet', 'enum', @('', 'M')), @('mount', '固定形式', 'Mounting', 'enum', $MOUNT_STD) ) return @{ params=$params; rules=@(); twin=$false } } 'KSB' { $params = @( @('bore', '缸径', 'Bore', 'number', $BORE_STD), @('stroke1', '行程1', 'Stroke 1', 'number', $STROKES_KSB), @('stroke2', '行程2', 'Stroke 2', 'number', $STROKES_KSB), @('magnet', '磁石', 'Magnet', 'enum', @('', 'M')), @('mount', '固定形式', 'Mounting', 'enum', $MOUNT_STD) ) return @{ params=$params; rules=@(); twin=$false } } 'KCS' { $params = @( @('type', '型号', 'Type', 'enum', @('00', '02', '03')), @('bore', '缸径', 'Bore', 'number', $BORE_KCS), @('stroke', '行程', 'Stroke', 'number', $STROKES), @('magnet', '磁石', 'Magnet', 'enum', @('', 'M')), @('mount', '固定形式', 'Mounting', 'enum', $MOUNT_STD) ) return @{ params=$params; rules=@(); twin=$false } } 'KS' { $params = @( @('type', '型号', 'Type', 'enum', @('00', '02', '03')), @('bore', '缸径', 'Bore', 'number', $BORE_STD), @('stroke', '行程', 'Stroke', 'number', $STROKES), @('magnet', '磁石', 'Magnet', 'enum', @('', 'M')), @('mount', '固定形式', 'Mounting', 'enum', $MOUNT_TIE) ) $rules = @() $rules += @{ if=@{param='type'; op='in'; values=@('02','03')}; then=@{param='mount'; op='in'; values=@('FA','FB','LB','TC','TC-M')} } return @{ params=$params; rules=$rules; twin=$false } } 'KSU' { $params = @( @('type', '型号', 'Type', 'enum', @('00', '02', '03')), @('bore', '缸径', 'Bore', 'number', $BORE_STD), @('stroke', '行程', 'Stroke', 'number', $STROKES), @('magnet', '磁石', 'Magnet', 'enum', @('', 'M')), @('mount', '固定形式', 'Mounting', 'enum', $MOUNT_TIE) ) $rules = @() $rules += @{ if=@{param='type'; op='in'; values=@('02','03')}; then=@{param='mount'; op='in'; values=@('FA','FB','LB','TC','TC-M')} } return @{ params=$params; rules=$rules; twin=$false } } 'M' { $params = @( @('action', '动作形式', 'Action', 'enum', @('N', 'S', 'T', 'ND', 'NJ')), @('bore', '缸径', 'Bore', 'number', $BORE_M), @('stroke', '行程', 'Stroke', 'number', $STROKES), @('magnet', '磁石', 'Magnet', 'enum', @('', 'M')), @('rod', '活塞杆材质', 'Rod Material', 'enum', @('E', '')), @('mount', '固定形式', 'Mounting', 'enum', $MOUNT_MINI), @('cap', '后盖形式', 'Cap Type', 'enum', @('', 'U', 'CM')) ) return @{ params=$params; rules=@(); twin=$false } } 'MS' { $params = @( @('action', '动作形式', 'Action', 'enum', @('N', 'D', 'J')), @('bore', '缸径', 'Bore', 'number', $BORE_MINI), @('stroke', '行程', 'Stroke', 'number', $STROKES), @('magnet', '磁石', 'Magnet', 'enum', @('', 'M')), @('rod', '活塞杆材质', 'Rod Material', 'enum', @('E', '')), @('mount', '固定形式', 'Mounting', 'enum', $MOUNT_MINI), @('cap', '后盖形式', 'Cap Type', 'enum', @('', 'U', 'CM')) ) return @{ params=$params; rules=@(); twin=$false } } 'MSC' { $params = @( @('action', '动作形式', 'Action', 'enum', @('C', 'D', 'J')), @('bore', '缸径', 'Bore', 'number', $BORE_MINI), @('stroke', '行程', 'Stroke', 'number', $STROKES), @('magnet', '磁石', 'Magnet', 'enum', @('', 'M')), @('rod', '活塞杆材质', 'Rod Material', 'enum', @('E', '')), @('mount', '固定形式', 'Mounting', 'enum', $MOUNT_MINI), @('cap', '后盖形式', 'Cap Type', 'enum', @('', 'U', 'CM')) ) return @{ params=$params; rules=@(); twin=$false } } 'MAL' { $params = @( @('type', '型号', 'Type', 'enum', @('MAL', 'MSAL', 'MTAL', 'MALD', 'MALJ')), @('bore', '缸径', 'Bore', 'number', $BORE_MINI), @('stroke', '行程', 'Stroke', 'number', $STROKES), @('magnet', '磁石', 'Magnet', 'enum', @('', 'M')), @('rod', '活塞杆材质', 'Rod Material', 'enum', @('E', '')), @('mount', '固定形式', 'Mounting', 'enum', $MOUNT_MINI), @('cap', '后盖形式', 'Cap Type', 'enum', @('', 'U', 'CM')) ) return @{ params=$params; rules=@(); twin=$false } } 'MALC' { $params = @( @('type', '型号', 'Type', 'enum', @('MALC', 'MALCD', 'MALCJ')), @('bore', '缸径', 'Bore', 'number', $BORE_MINI), @('stroke', '行程', 'Stroke', 'number', $STROKES), @('magnet', '磁石', 'Magnet', 'enum', @('', 'M')), @('rod', '活塞杆材质', 'Rod Material', 'enum', @('E', '')), @('mount', '固定形式', 'Mounting', 'enum', $MOUNT_MINI), @('cap', '后盖形式', 'Cap Type', 'enum', @('', 'U', 'CM')) ) return @{ params=$params; rules=@(); twin=$false } } default { return @{ params=@(); rules=@(); twin=$false } } } } # ==================== STEP 几何生成 (简化圆柱组合) ==================== function Format-Num([double]$v) { return $v.ToString('0.###', $inv) } function New-CylinderEntities([int]$id, [double]$x, [double]$y, [double]$z, [double]$r, [double]$len) { $X = Format-Num $x; $Y = Format-Num $y; $Z = Format-Num $z $ZT = Format-Num ($z + $len); $R = Format-Num $r $VY = Format-Num ($y + $r) $e = @( "#$($id)=DIRECTION('',(0.,0.,1.));", "#$($id+1)=CARTESIAN_POINT('',($X,$Y,$Z));", "#$($id+2)=CARTESIAN_POINT('',($X,$Y,$ZT));", "#$($id+3)=DIRECTION('',(1.,0.,0.));", "#$($id+4)=AXIS2_PLACEMENT_3D('',#$($id+1),#$($id),#$($id+3));", "#$($id+5)=CARTESIAN_POINT('',($X,$VY,$Z));", "#$($id+6)=CARTESIAN_POINT('',($X,$VY,$ZT));", "#$($id+7)=VERTEX_POINT('',#$($id+5));", "#$($id+8)=VERTEX_POINT('',#$($id+6));", "#$($id+9)=CIRCLE('',#$($id+4),$R);", "#$($id+10)=EDGE_CURVE('',#$($id+7),#$($id+7),#$($id+9),.T.);", "#$($id+11)=CYLINDRICAL_SURFACE('',#$($id+4),$R);", "#$($id+12)=PLANE('',#$($id+4));", "#$($id+13)=DIRECTION('',(0.,0.,1.));", "#$($id+14)=AXIS2_PLACEMENT_3D('',#$($id+2),#$($id+13),#$($id+3));", "#$($id+15)=PLANE('',#$($id+14));", "#$($id+16)=CIRCLE('',#$($id+14),$R);", "#$($id+17)=EDGE_CURVE('',#$($id+8),#$($id+8),#$($id+16),.T.);", "#$($id+18)=DIRECTION('',(0.,0.,1.));", "#$($id+19)=VECTOR('',#$($id+18),1.);", "#$($id+20)=LINE('',#$($id+5),#$($id+19));", "#$($id+21)=EDGE_CURVE('',#$($id+7),#$($id+8),#$($id+20),.T.);", "#$($id+22)=ORIENTED_EDGE('',*,*,#$($id+10),.T.);", "#$($id+23)=ORIENTED_EDGE('',*,*,#$($id+17),.T.);", "#$($id+24)=ORIENTED_EDGE('',*,*,#$($id+21),.T.);", "#$($id+25)=ORIENTED_EDGE('',*,*,#$($id+17),.T.);", "#$($id+26)=ORIENTED_EDGE('',*,*,#$($id+21),.F.);", "#$($id+27)=ORIENTED_EDGE('',*,*,#$($id+10),.F.);", "#$($id+28)=EDGE_LOOP('',(#$($id+22)));", "#$($id+29)=EDGE_LOOP('',(#$($id+23)));", "#$($id+30)=EDGE_LOOP('',(#$($id+24),#$($id+25),#$($id+26),#$($id+27)));", "#$($id+31)=ADVANCED_FACE('',(#$($id+30)),#$($id+11),.T.);", "#$($id+32)=ADVANCED_FACE('',(#$($id+28)),#$($id+12),.F.);", "#$($id+33)=ADVANCED_FACE('',(#$($id+29)),#$($id+15),.T.);", "#$($id+34)=CLOSED_SHELL('',(#$($id+31),#$($id+32),#$($id+33)));", "#$($id+35)=MANIFOLD_SOLID_BREP('',#$($id+34));" ) return $e } function Write-StepFile([string]$path, [double]$bore, [double]$stroke, [bool]$twin, [bool]$mini) { # 尺寸 (占位近似, 真实尺寸待 NX 母模导出替换) $wall = if ($mini) { 2.0 } else { 3.0 } $bodyR = $bore / 2.0 + $wall $capR = $bore / 2.0 + $(if ($mini) { 3.5 } else { 6.0 }) $capLen = if ($mini) { 6.0 } else { 8.0 } $fcapLen = if ($mini) { 8.0 } else { 10.0 } $bodyLen = $stroke + $bore * 1.1 + $(if ($mini) { 20.0 } else { 30.0 }) $rodR = [Math]::Max($bore / $(if ($mini) { 6.0 } else { 7.0 }), $(if ($mini) { 2.0 } else { 4.0 })) $rodBot = $bodyLen - $(if ($mini) { 8.0 } else { 12.0 }) $rodLen = $stroke + $(if ($mini) { 20.0 } else { 26.0 }) $rodOff = if ($twin) { $rodR * 2.0 } else { 0.0 } $lines = New-Object System.Collections.Generic.List[string] $lines.Add('ISO-10303-21;') $lines.Add('HEADER;') $lines.Add("FILE_DESCRIPTION(('ONEBOT simplified sample geometry'),'2;1');") $lines.Add("FILE_NAME('$(Split-Path $path -Leaf)','2026-08-22T00:00:00',('ONEBOT'),('ONEBOT'),'','','');") $lines.Add("FILE_SCHEMA(('CONFIG_CONTROL_DESIGN'));") $lines.Add('ENDSEC;') $lines.Add('DATA;') $ids = New-Object System.Collections.Generic.List[int] $nextId = 1 $cyls = New-Object System.Collections.Generic.List[object] $cyls.Add(@{ x = 0.0; y = 0.0; z = -$capLen; r = $capR; len = $capLen }) $cyls.Add(@{ x = 0.0; y = 0.0; z = 0.0; r = $bodyR; len = $bodyLen }) $cyls.Add(@{ x = 0.0; y = 0.0; z = $bodyLen; r = $capR; len = $fcapLen }) $cyls.Add(@{ x = -$rodOff; y = 0.0; z = $rodBot; r = $rodR; len = $rodLen }) if ($twin) { $cyls.Add(@{ x = $rodOff; y = 0.0; z = $rodBot; r = $rodR; len = $rodLen }) } foreach ($cyl in $cyls) { [string[]]$entities = New-CylinderEntities -id $nextId -x $cyl['x'] -y $cyl['y'] -z $cyl['z'] -r $cyl['r'] -len $cyl['len'] $lines.AddRange($entities) $ids.Add($nextId + 35) $nextId += 36 } # 尾部共享实体: 注入 STYLED_ITEM 颜色 (occt-import-js 对无颜色模型会崩) $styledIds = New-Object System.Collections.Generic.List[string] $c = $nextId foreach ($s in $ids) { $lines.Add("#$c=COLOUR_RGB('',0.65,0.72,0.85);") $lines.Add("#$($c+1)=FILL_AREA_STYLE_COLOUR('',#$c);") $lines.Add("#$($c+2)=SURFACE_STYLE_FILL_AREA(#$($c+1));") $lines.Add("#$($c+3)=SURFACE_SIDE_STYLE('',(#$($c+2)));") $lines.Add("#$($c+4)=SURFACE_STYLE_USAGE(.BOTH.,#$($c+3));") $lines.Add("#$($c+5)=PRESENTATION_STYLE_ASSIGNMENT((#$($c+4)));") $lines.Add("#$($c+6)=STYLED_ITEM('',(#$($c+5)),#$s);") $styledIds.Add("#$($c+6)") $c += 7 } $items = (($ids | ForEach-Object { "#$_" }) + $styledIds) -join ',' $lines.Add("#$c=SHAPE_REPRESENTATION('',($items),#$($c+1));") $lines.Add("#$($c+1)=(GEOMETRIC_REPRESENTATION_CONTEXT(3)GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#$($c+2)))GLOBAL_UNIT_ASSIGNED_CONTEXT((#$($c+3),#$($c+4),#$($c+5)))REPRESENTATION_CONTEXT('',''));") $lines.Add("#$($c+2)=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.E-06),#$($c+3),'');") $lines.Add("#$($c+3)=(LENGTH_UNIT()NAMED_UNIT(*)SI_UNIT(.MILLI.,.METRE.));") $lines.Add("#$($c+4)=(NAMED_UNIT(*)PLANE_ANGLE_UNIT()SI_UNIT($,.RADIAN.));") $lines.Add("#$($c+5)=(NAMED_UNIT(*)SI_UNIT($,.STERADIAN.)SOLID_ANGLE_UNIT());") $lines.Add("#$($c+6)=PRODUCT('ONEBOT','ONEBOT','',(#$($c+7)));") $lines.Add("#$($c+7)=PRODUCT_CONTEXT('',#$($c+8),'mechanical');") $lines.Add("#$($c+8)=APPLICATION_CONTEXT('configuration controlled 3d designs of mechanical parts and assemblies');") $lines.Add("#$($c+9)=PRODUCT_DEFINITION_FORMATION('','',#$($c+6));") $lines.Add("#$($c+10)=PRODUCT_DEFINITION('design','',#$($c+9),#$($c+11));") $lines.Add("#$($c+11)=PRODUCT_DEFINITION_CONTEXT('part definition',#$($c+8),'design');") $lines.Add("#$($c+12)=PRODUCT_DEFINITION_SHAPE('','',#$($c+10));") $lines.Add("#$($c+13)=SHAPE_DEFINITION_REPRESENTATION(#$($c+12),#$c);") $lines.Add('ENDSEC;') $lines.Add('END-ISO-10303-21;') $enc = New-Object System.Text.UTF8Encoding($false) [System.IO.File]::WriteAllLines($path, $lines.ToArray(), $enc) } # 双轴型号判定 (两根活塞杆) function Is-TwinType([string]$code, [string]$type) { if ($code -eq 'M') { return ($type -eq 'ND' -or $type -eq 'NJ') } if ($code -eq 'MS') { return ($type -eq 'D' -or $type -eq 'J') } if ($code -eq 'MSC') { return ($type -eq 'D' -or $type -eq 'J') } if ($code -eq 'MAL') { return ($type -eq 'MALD' -or $type -eq 'MALJ') } if ($code -eq 'MALC') { return ($type -eq 'MALCD' -or $type -eq 'MALCJ') } return ($type -eq '02' -or $type -eq '03') } # 规则求值 (与 C# Configurator 同语义: 违反规则的组合不生成) function Cond-Matches($cond, $combo) { if (-not $combo.ContainsKey($cond['param'])) { return $false } $val = [string]$combo[$cond['param']] switch ($cond['op']) { 'eq' { return ([string]$val -eq [string]$cond['value']) } 'ne' { return ([string]$val -ne [string]$cond['value']) } 'in' { foreach ($v in $cond['values']) { if ([string]$val -eq [string]$v) { return $true } } return $false } default { return $false } } } function Rule-Violated($rules, $combo) { foreach ($r in $rules) { if ((Cond-Matches $r['if'] $combo) -and -not (Cond-Matches $r['then'] $combo)) { return $true } } return $false } function Cond-ToJson($c) { if ($c['op'] -eq 'in') { $vals = ($c['values'] | ForEach-Object { '"' + $_ + '"' }) -join ',' return '{"Param":"' + $c['param'] + '","Op":"in","Value":[' + $vals + ']}' } return '{"Param":"' + $c['param'] + '","Op":"' + $c['op'] + '","Value":"' + $c['value'] + '"}' } function Rule-ToJson($r) { return '{"If":' + (Cond-ToJson $r['if']) + ',"Then":' + (Cond-ToJson $r['then']) + '}' } # ==================== 主流程 ==================== $seriesJsonList = New-Object System.Collections.Generic.List[string] $stepNames = New-Object System.Collections.Generic.HashSet[string] $totalVariants = 0 $totalSteps = 0 foreach ($s in $SERIES) { $code = $s.code $spec = Get-SeriesSpec $code $mini = ($s.cat -eq 'MIN' -or $s.cat -eq 'ALU') # 变体组合: 参数值笛卡尔积 $combos = New-Object System.Collections.Generic.List[object] $combos.Add(@{}) foreach ($p in $spec.params) { $next = New-Object System.Collections.Generic.List[object] foreach ($c in $combos) { foreach ($v in $p[4]) { $nc = @{} foreach ($k in $c.Keys) { $nc[$k] = $c[$k] } $nc[$p[0]] = $v $next.Add($nc) } } $combos = $next } # CSV 行 + 变体 JSON + STEP (按 几何键: 系列+型号段+缸径+行程) $csvLines = New-Object System.Collections.Generic.List[string] $headerCols = @('model_code') foreach ($p in $spec.params) { $headerCols += $p[0] } $headerCols += 'step_file' $csvLines.Add(($headerCols -join ',')) $varJson = New-Object System.Collections.Generic.List[string] $geoCodes = New-Object System.Collections.Generic.HashSet[string] foreach ($c in $combos) { # 规则过滤: 违反手册约束的组合不生成 (如 大缸径+YB、双轴型+受限固定形式) if (Rule-Violated $spec.rules $c) { continue } $typeSeg = '' if ($spec.params[0][0] -eq 'type' -or $spec.params[0][0] -eq 'action') { $typeSeg = [string]$c[$spec.params[0][0]] } $bore = [double]$c['bore'] $stroke = if ($c.ContainsKey('stroke')) { [double]$c['stroke'] } else { [double]$c['stroke1'] } # 型号编码 $model = '' if ($code -eq 'KSB') { $model = "KSB$($c['bore'])-$($c['stroke1'])x$($c['stroke2'])$($c['magnet'])$($c['mount'])" } elseif ($s.cat -eq 'MIN' -or $code -eq 'MAL' -or $code -eq 'MALC') { $model = "$typeSeg$($c['bore'])-$($c['stroke'])$($c['magnet'])$($c['rod'])$($c['mount'])$($c['cap'])" } else { $model = "$code$typeSeg$($c['bore'])-$($c['stroke'])$($c['magnet'])$($c['mount'])" } # 几何 STEP (共用) $geoKey = "$code-$typeSeg-${bore}x$stroke" $stepName = "$code-$typeSeg-${bore}x$stroke.step" if (-not $geoCodes.Contains($geoKey)) { [void]$geoCodes.Add($geoKey) if (-not $stepNames.Contains($stepName)) { Write-StepFile (Join-Path $stepDir $stepName) $bore $stroke (Is-TwinType $code $typeSeg) $mini [void]$stepNames.Add($stepName) $totalSteps++ } } # 变体 JSON 与 CSV $paramsJson = New-Object System.Collections.Generic.List[string] foreach ($p in $spec.params) { $v = $c[$p[0]] if ($p[3] -eq 'number') { $paramsJson.Add('"' + $p[0] + '":' + [string]$v) } else { $paramsJson.Add('"' + $p[0] + '":"' + $v + '"') } } $varJson.Add('{"modelCode":"' + $model + '","params":{' + ($paramsJson -join ',') + '},"step":"step/' + $stepName + '"}') $rowVals = @($model) foreach ($p in $spec.params) { $rowVals += [string]$c[$p[0]] } $rowVals += $stepName $csvLines.Add(($rowVals -join ',')) $totalVariants++ } # 系列 meta JSON (series\.meta.json, 与向导保存配置同构; 变体不存, 构建时从 CSV 读) $paramsJson = New-Object System.Collections.Generic.List[string] foreach ($p in $spec.params) { $display = '' if ($p[0] -eq 'mount') { $entries = New-Object System.Collections.Generic.List[string] foreach ($k in $MOUNT_DISP.Keys) { $entries.Add('"' + $k + '":"' + $MOUNT_DISP[$k] + '"') } $display = '"display":{' + ($entries -join ',') + '}' } elseif ($p[0] -eq 'magnet') { $display = '"display":{"":"不附磁石","M":"附磁石 M"}' } elseif ($p[0] -eq 'rod') { $display = '"display":{"":"碳钢","E":"不锈钢 E"}' } elseif ($p[0] -eq 'cap') { $display = '"display":{"":"标准摆尾型","U":"标准平尾型 U","CM":"标准圆尾型 CM"}' } $vals = New-Object System.Collections.Generic.List[string] foreach ($v in $p[4]) { if ($p[3] -eq 'number') { $vals.Add([string]$v) } else { $vals.Add('"' + $v + '"') } } $paramsJson.Add('{"code":"' + $p[0] + '","nameZh":"' + $p[1] + '","nameEn":"' + $p[2] + '","unit":"' + $(if ($p[3] -eq 'number') { 'mm' } else { '' }) + '","type":"' + $p[3] + '","keywords":"' + $p[0] + '","values":[' + ($vals -join ',') + ']' + $(if ($display) { ',' + $display } else { '' }) + '}') } $rulesJson = (($spec.rules | ForEach-Object { Rule-ToJson $_ }) -join ',') # 手册页附件 (尺寸图窗口对照用): catalog\manual\<系列>\pXX.png → source 相对路径 $manAtt = New-Object System.Collections.Generic.List[string] if (Test-Path (Join-Path $manDir $code)) { foreach ($mf in (Get-ChildItem (Join-Path $manDir $code) -Filter '*.png' | Sort-Object Name)) { $manAtt.Add('{"kind":"manual","lang":"","source":"manual/' + $code + '/' + $mf.Name + '","model":null}') } } $sjson = '{"code":"' + $code + '","nameZh":"' + $s.zh + '","nameEn":"' + $s.en + '","keywords":"' + $s.kw + '",' + '"parameters":[' + ($paramsJson -join ',') + '],' + '"rules":[' + $rulesJson + '],' + '"naming":{"stepNameTemplate":"{model}.step"},' + '"attachments":[],"variants":[]}' $metaJson = '{"series":' + $sjson + ',"attachments":[' + ($manAtt -join ',') + ']}' [System.IO.File]::WriteAllText((Join-Path $seriesDir ($code + '.meta.json')), $metaJson, (New-Object System.Text.UTF8Encoding($false))) # 写 CSV [System.IO.File]::WriteAllLines((Join-Path $csvDir ($code + '.csv')), $csvLines.ToArray(), (New-Object System.Text.UTF8Encoding($true))) Write-Output ("系列 " + $code + ": " + $combos.Count + " 个变体, 几何 STEP " + $geoCodes.Count + " 个") } # catalog.json (源目录: 目录级定义 + 分类系列列表; series 定义在 series\*.meta.json) $catJson = New-Object System.Collections.Generic.List[string] foreach ($c in $CATS) { $codes = ($SERIES | Where-Object { $_.cat -eq $c.code } | ForEach-Object { '"' + $_.code + '"' }) -join ',' $catJson.Add('{"code":"' + $c.code + '","nameZh":"' + $c.zh + '","nameEn":"' + $c.en + '","keywords":"","series":[' + $codes + ']}') } $catalogJson = '{"schemaVersion":"1.0","catalogName":"ONEBOT 气动元件目录","catalogNameEn":"ONEBOT Pneumatic Catalog",' + '"catalogVersion":"2026.08","defaultLang":"zh","langs":["zh","en"],' + '"categories":[' + ($catJson -join ',') + '],' + '"series":[]}' [System.IO.File]::WriteAllText((Join-Path $OutDir 'catalog.json'), $catalogJson, (New-Object System.Text.UTF8Encoding($false))) Write-Output "" Write-Output ("完成: " + $SERIES.Count + " 个系列, " + $totalVariants + " 个变体, " + $totalSteps + " 个几何 STEP") Write-Output ("源目录: " + $OutDir + " (catalog.json + series\*.meta.json + csv\ + step\)") Write-Output ("下一步打包 (C# 统一内核): OnebotCatalog\bin\OnebotCatalog.exe --buildfull <源目录> <输出.opc>")