Design Express Res initial commit

This commit is contained in:
2026-08-22 17:20:02 +08:00
commit ba67ff7da7
23 changed files with 4135 additions and 0 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
+83
View File
@@ -0,0 +1,83 @@
# Design Express Res 部署脚本: 复制 DLL + 注册每用户 COM + 生成 .addin (无需管理员权限)
# 架构: DesignExpress.Core.dll(独立核心) + DesignExpress.InventorLink.dll(Inventor 适配)
$ErrorActionPreference = "Stop"
$invVer = "2022"
$addinDir = Join-Path $env:APPDATA "Autodesk\Inventor $invVer\Addins"
$clsid = "{D422E0BF-02D0-4DAE-958A-7BBA80B718E9}"
$progId = "DesignExpress.InventorLink.AddInServer"
$asmName = "DesignExpress.InventorLink, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"
$srcCore = Join-Path $PSScriptRoot "DesignExpress.Core.dll"
$srcLink = Join-Path $PSScriptRoot "DesignExpress.InventorLink.dll"
if (-not (Test-Path $srcCore)) { Write-Host "未找到 DesignExpress.Core.dll"; exit 1 }
if (-not (Test-Path $srcLink)) { Write-Host "未找到 DesignExpress.InventorLink.dll"; exit 1 }
New-Item -ItemType Directory -Force -Path $addinDir | Out-Null
Copy-Item -LiteralPath $srcCore -Destination (Join-Path $addinDir "DesignExpress.Core.dll") -Force
$dstLink = Join-Path $addinDir "DesignExpress.InventorLink.dll"
Copy-Item -LiteralPath $srcLink -Destination $dstLink -Force
Write-Host "DLL 已复制: $addinDir"
# 每用户 COM 注册 (HKCU\Software\Classes)
$clsBase = "HKCU:\Software\Classes\CLSID\$clsid"
Remove-Item -Path $clsBase -Recurse -Force -ErrorAction SilentlyContinue
New-Item -Path $clsBase -Force | Out-Null
Set-ItemProperty -Path $clsBase -Name "(default)" -Value $progId
New-Item -Path "$clsBase\InprocServer32" -Force | Out-Null
Set-ItemProperty -Path "$clsBase\InprocServer32" -Name "(default)" -Value "mscoree.dll"
Set-ItemProperty -Path "$clsBase\InprocServer32" -Name "Class" -Value $progId
Set-ItemProperty -Path "$clsBase\InprocServer32" -Name "Assembly" -Value $asmName
$codebase = "file:///" + ($dstLink.Replace('\', '/'))
Set-ItemProperty -Path "$clsBase\InprocServer32" -Name "CodeBase" -Value $codebase
Set-ItemProperty -Path "$clsBase\InprocServer32" -Name "ThreadingModel" -Value "Both"
$progBase = "HKCU:\Software\Classes\$progId"
Remove-Item -Path $progBase -Recurse -Force -ErrorAction SilentlyContinue
New-Item -Path "$progBase\CLSID" -Force | Out-Null
Set-ItemProperty -Path "$progBase\CLSID" -Name "(default)" -Value $clsid
Write-Host "COM 注册完成 (HKCU)"
# 生成 .addin
$addinXml = @"
<Addin Type="Standard">
<ClassId>$clsid</ClassId>
<ClientId>$clsid</ClientId>
<DisplayName>Design Express Res</DisplayName>
<Description>设计标注检查与设计数据查询工具(Core+Link 架构)</Description>
<Assembly>$dstLink</Assembly>
<LoadOnStartUp>1</LoadOnStartUp>
<UserUnloadable>1</UserUnloadable>
<Hidden>0</Hidden>
<SupportedSoftwareVersionGreaterThan>25..</SupportedSoftwareVersionGreaterThan>
<DataVersion>1</DataVersion>
<UserInterfaceVersion>1</UserInterfaceVersion>
</Addin>
"@
$addinFile = Join-Path $addinDir "DesignExpress.InventorLink.addin"
[System.IO.File]::WriteAllText($addinFile, $addinXml, [System.Text.Encoding]::UTF8)
Write-Host ".addin 已生成: $addinFile"
# 清理旧版本文件
Remove-Item (Join-Path $addinDir "InventorDesignCheck.dll") -Force -ErrorAction SilentlyContinue
Remove-Item (Join-Path $addinDir "InventorDesignCheck.addin") -Force -ErrorAction SilentlyContinue
# 关键: 修复 AddInLoadRules(加载规则文件), 确保本加载项允许加载
# 该文件为 UTF-16 二进制, 每条记录: "{GUID}" + DWORD(0=允许, 1=禁止)
$rulesFile = Join-Path $addinDir "AddInLoadRules"
if (Test-Path $rulesFile) {
$b = [System.IO.File]::ReadAllBytes($rulesFile)
$u = [System.Text.Encoding]::Unicode.GetString($b)
$guid = "{D422E0BF-02D0-4DAE-958A-7BBA80B718E9}"
$pos = $u.IndexOf($guid)
if ($pos -ge 0) {
$bytePos = $pos * 2 + $guid.Length * 2
if ([BitConverter]::ToUInt32($b, $bytePos) -ne 0) {
$b[$bytePos] = 0; $b[$bytePos + 1] = 0; $b[$bytePos + 2] = 0; $b[$bytePos + 3] = 0
[System.IO.File]::WriteAllBytes($rulesFile, $b)
Write-Host "已修复加载规则(允许加载)"
}
}
}
Write-Host "部署完成! 请重启 Inventor, 功能区将出现 [Design Express Res] 选项卡。"
+59
View File
@@ -0,0 +1,59 @@
# Design Express Res - SolidWorks Link 部署脚本 (无需管理员权限, 但 HKLM 注册需手动确认一次)
# 说明: 首次部署后需正常退出并重启 SolidWorks 一次, 让选项卡定义持久化到注册表
$ErrorActionPreference = "Stop"
$swDir = Join-Path $env:APPDATA "DesignExpress\SolidWorks"
$clsid = "{4A7E023E-EDD8-4642-B337-425E02394C81}"
$progId = "DesignExpress.SolidWorksLink.SwAddin"
$asmName = "DesignExpress.SolidWorksLink, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"
$srcCore = Join-Path $PSScriptRoot "DesignExpress.Core.dll"
$srcLink = Join-Path $PSScriptRoot "DesignExpress.SolidWorksLink.dll"
if (-not (Test-Path $srcCore)) { Write-Host "未找到 DesignExpress.Core.dll"; exit 1 }
if (-not (Test-Path $srcLink)) { Write-Host "未找到 DesignExpress.SolidWorksLink.dll"; exit 1 }
New-Item -ItemType Directory -Force -Path $swDir | Out-Null
Copy-Item -LiteralPath $srcCore -Destination (Join-Path $swDir "DesignExpress.Core.dll") -Force
$dstLink = Join-Path $swDir "DesignExpress.SolidWorksLink.dll"
Copy-Item -LiteralPath $srcLink -Destination $dstLink -Force
Write-Host "DLL 已复制: $swDir"
# 每用户 COM 注册 (HKCU)
$clsBase = "HKCU:\Software\Classes\CLSID\$clsid"
Remove-Item -Path $clsBase -Recurse -Force -ErrorAction SilentlyContinue
New-Item -Path $clsBase -Force | Out-Null
Set-ItemProperty -Path $clsBase -Name "(default)" -Value $progId
New-Item -Path "$clsBase\InprocServer32" -Force | Out-Null
Set-ItemProperty -Path "$clsBase\InprocServer32" -Name "(default)" -Value "mscoree.dll"
Set-ItemProperty -Path "$clsBase\InprocServer32" -Name "Class" -Value $progId
Set-ItemProperty -Path "$clsBase\InprocServer32" -Name "Assembly" -Value $asmName
$codebase = "file:///" + ($dstLink.Replace('\', '/'))
Set-ItemProperty -Path "$clsBase\InprocServer32" -Name "CodeBase" -Value $codebase
Set-ItemProperty -Path "$clsBase\InprocServer32" -Name "ThreadingModel" -Value "Both"
$progBase = "HKCU:\Software\Classes\$progId"
Remove-Item -Path $progBase -Recurse -Force -ErrorAction SilentlyContinue
New-Item -Path "$progBase\CLSID" -Force | Out-Null
Set-ItemProperty -Path "$progBase\CLSID" -Name "(default)" -Value $clsid
# SolidWorks 加载项注册 (当前用户, 启动时加载)
$swAddinKey = "HKCU:\Software\SolidWorks\AddIns\$clsid"
Remove-Item -Path $swAddinKey -Recurse -Force -ErrorAction SilentlyContinue
New-Item -Path $swAddinKey -Force | Out-Null
Set-ItemProperty -Path $swAddinKey -Name "(default)" -Value 1 -Type DWord
Set-ItemProperty -Path $swAddinKey -Name "Title" -Value "Design Express Res"
Set-ItemProperty -Path $swAddinKey -Name "Description" -Value "设计标注检查与设计数据查询工具"
$swStartupKey = "HKCU:\Software\SolidWorks\AddInsStartup\$clsid"
New-Item -Path $swStartupKey -Force | Out-Null
Set-ItemProperty -Path $swStartupKey -Name "(default)" -Value 1 -Type DWord
Write-Host "SolidWorks 加载项已注册(启动时自动加载)"
Write-Host ""
Write-Host "=== 下一步(重要) ==="
Write-Host "1. 若首次安装, 请以管理员身份导入注册表(加载项清单需要 HKLM):"
Write-Host " reg add ""HKLM\SOFTWARE\SolidWorks\AddIns\$clsid"" /ve /t REG_SZ /d 1 /f"
Write-Host " reg add ""HKLM\SOFTWARE\SolidWorks\AddIns\$clsid"" /v Title /t REG_SZ /d ""Design Express Res"" /f"
Write-Host "2. 启动 SolidWorks, 在 工具→插件 中勾选 Design Express Res"
Write-Host "3. 打开一个零件文档 -> 出现 Design Express Res 选项卡"
Write-Host "4. 正常退出 SolidWorks 并重新打开(持久化选项卡定义)"
+96
View File
@@ -0,0 +1,96 @@
using System;
using System.Collections.Generic;
namespace DesignExpress.Core
{
public class CheckResult
{
public string Stage;
public string Obj;
public string CheckID;
public string Name;
public string Result;
public string Note;
public string Basis;
}
public static class AppState
{
public static ICadHost Host;
public static List<CheckResult> Results = new List<CheckResult>();
public static void Add(string stage, string obj, string id, string name,
string result, string note, string basis = "")
{
Results.Add(new CheckResult
{
Stage = stage, Obj = obj, CheckID = id, Name = name,
Result = result, Note = note, Basis = basis
});
try
{
if (Host != null) Host.SetStatusText("[" + stage + "] " + obj + " - " + name);
}
catch { }
}
public static void Count(out int pass, out int warn, out int fail, out int info)
{
pass = 0; warn = 0; fail = 0; info = 0;
foreach (CheckResult r in Results)
{
switch (r.Result)
{
case "通过": pass++; break;
case "警告": warn++; break;
case "未通过": fail++; break;
default: info++; break;
}
}
}
public static string GetProp(Dictionary<string, string> props, params string[] names)
{
if (props == null) return "";
foreach (string n in names)
{
foreach (KeyValuePair<string, string> kv in props)
{
if (string.Equals(kv.Key, n, StringComparison.OrdinalIgnoreCase))
return kv.Value ?? "";
}
}
return "";
}
public static bool IsDefaultMaterial(string s)
{
s = (s ?? "").Trim();
return s == "" || s.Equals("默认", StringComparison.OrdinalIgnoreCase) ||
s.Equals("Default", StringComparison.OrdinalIgnoreCase) ||
s.Equals("Generic", StringComparison.OrdinalIgnoreCase);
}
// 在多个文档的属性中搜索关键词
public static bool HitKeywords(IEnumerable<DocInfo> docs, string keys, out string hits)
{
hits = "";
string[] kArr = keys.Split(',');
foreach (DocInfo d in docs)
{
if (d == null) continue;
string txt = GetProp(d.Props, "Comments", "备注", "注释", "Keywords", "关键词",
"Description", "描述", "名称", "Title", "标题");
foreach (string k in kArr)
{
if ((txt + d.Name).ToUpper().Contains(k.ToUpper()))
{
if (hits.Length < 300) hits += d.Name + "[" + k + "]; ";
return true;
}
}
}
return false;
}
}
}
+667
View File
@@ -0,0 +1,667 @@
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace DesignExpress.Core
{
// 检查引擎: 只处理 DocSnapshot 纯数据, 不依赖任何 CAD 系统
public static class CheckEngine
{
// ================= 零件阶段 =================
public static void RunPartChecks(DocSnapshot doc)
{
string objName = "零件";
if (doc.Dirty)
AppState.Add("零件", objName, "P01", "文档保存状态", "警告", "存在未保存的修改", "建议检查前保存");
else
AppState.Add("零件", objName, "P01", "文档保存状态", "通过", "已保存", "");
CheckPartProps(doc, objName);
CheckModelAnnotations(doc, objName);
CheckThreads(doc, objName);
CheckMaterialContact(doc, objName);
AppState.Add("零件", objName, "P15", "焊接标注检查", "信息",
"模型焊接符号由工程图 D19 焊接技术要求关键词检查覆盖(双面焊/打磨/标准号)", "spec 文件");
CheckSurfaceTreat(doc, objName);
CheckStrength(doc, objName);
FitCheckLoop(doc);
}
private static void CheckPartProps(DocSnapshot doc, string objName)
{
string v;
v = AppState.GetProp(doc.Props, "Part Number", "零件代号", "零件号", "图号");
if (v == "") AppState.Add("零件", objName, "P02", "零件代号", "未通过", "未填写", "标题栏必填项");
else AppState.Add("零件", objName, "P02", "零件代号", "通过", v, "");
v = AppState.GetProp(doc.Props, "Description", "描述", "名称", "部件名称");
if (v == "") AppState.Add("零件", objName, "P03", "名称", "未通过", "未填写", "标题栏必填项");
else AppState.Add("零件", objName, "P03", "名称", "通过", v, "");
string mat = doc.MaterialName;
if (mat == "") mat = AppState.GetProp(doc.Props, "Material", "材料", "材质");
if (AppState.IsDefaultMaterial(mat))
AppState.Add("零件", objName, "P04", "材料", "未通过", "材料未指定或为默认", "配合面/接触面必须明确材料");
else
AppState.Add("零件", objName, "P04", "材料", "通过", mat, "");
v = AppState.GetProp(doc.Props, "Designer", "设计者", "设计");
if (v == "") AppState.Add("零件", objName, "P05", "设计者", "未通过", "未填写", "");
else AppState.Add("零件", objName, "P05", "设计者", "通过", v, "");
v = AppState.GetProp(doc.Props, "Checked By", "检查者", "审核者", "校核");
if (v == "") AppState.Add("零件", objName, "P06", "审核者", "未通过", "未填写", "");
else AppState.Add("零件", objName, "P06", "审核者", "通过", v, "");
v = AppState.GetProp(doc.Props, "Revision Number", "修订号", "版本号");
if (v == "") AppState.Add("零件", objName, "P07", "修订号", "警告", "未填写修订号", "建议填写以追溯版本");
else AppState.Add("零件", objName, "P07", "修订号", "通过", v, "");
}
private static void CheckModelAnnotations(DocSnapshot doc, string objName)
{
ModelInfo m = doc.Model;
if (m == null) m = new ModelInfo();
int nDim = m.DimCount, nFcf = m.FcfCount, nTex = m.TexCount, nDat = m.DatumCount, nTn = m.ThreadNoteCount;
if (nDim < 0)
AppState.Add("零件", objName, "P08", "模型标注统计", "信息", "无法读取模型标注", "");
else if (nDim == 0)
AppState.Add("零件", objName, "P08", "模型标注统计", "警告", "模型上没有任何尺寸标注, 建议使用三维技术标注(PMI)", "设计标注要求");
else
AppState.Add("零件", objName, "P08", "模型标注统计", "通过",
"模型尺寸 " + nDim + " 个; 形位公差 " + (nFcf < 0 ? "?" : nFcf.ToString()) +
" 个; 粗糙度 " + (nTex < 0 ? "?" : nTex.ToString()) +
" 个; 基准 " + (nDat < 0 ? "?" : nDat.ToString()) +
" 个; 螺纹孔标注 " + (nTn < 0 ? "?" : nTn.ToString()) + " 个", "");
if (nDim > 0)
{
if (!m.TolReadable)
AppState.Add("零件", objName, "P08b", "尺寸公差覆盖", "信息", "部分尺寸公差信息无法读取", "");
else if (m.TolCount == 0)
AppState.Add("零件", objName, "P08b", "尺寸公差覆盖", "警告", nDim + " 个尺寸均未标注公差", "配合尺寸应标注公差");
else
AppState.Add("零件", objName, "P08b", "尺寸公差覆盖", "通过", m.TolCount + "/" + nDim + " 个尺寸带公差", "");
}
if (nFcf < 0)
AppState.Add("零件", objName, "P08c", "形位公差标注", "信息", "无法读取", "");
else if (nFcf == 0)
AppState.Add("零件", objName, "P08c", "形位公差标注", "警告", "模型未标注任何形位公差(如跳动/同轴度/垂直度/平面度)", "关键配合特征应标注形位公差");
else
AppState.Add("零件", objName, "P08c", "形位公差标注", "通过", "共 " + nFcf + " 个形位公差标注", "");
if (nTex < 0)
AppState.Add("零件", objName, "P08d", "粗糙度标注", "信息", "无法读取", "");
else if (nTex == 0)
AppState.Add("零件", objName, "P08d", "粗糙度标注", "警告", "模型未标注表面粗糙度", "配合面/密封面应标注粗糙度");
else
AppState.Add("零件", objName, "P08d", "粗糙度标注", "通过", "共 " + nTex + " 个粗糙度标注(首个: " + m.FirstTexText + ")", "");
}
private static void CheckThreads(DocSnapshot doc, string objName)
{
ModelInfo m = doc.Model;
int n = m == null ? -1 : m.ThreadFeatureCount;
if (n < 0)
AppState.Add("零件", objName, "P13", "螺纹孔检查", "信息", "无法读取螺纹特征", "");
else if (n == 0)
AppState.Add("零件", objName, "P13", "螺纹孔检查", "通过", "无螺纹特征", "");
else
AppState.Add("零件", objName, "P13", "螺纹孔检查", "通过", "共 " + n + " 处螺纹特征(示例: " + m.ThreadDesignation + ")", "螺纹代号/深度/攻深需在工程图中完整标注");
}
private static void CheckMaterialContact(DocSnapshot doc, string objName)
{
string v = AppState.GetProp(doc.Props, "物料接触", "接触类别", "接触", "ContactClass", "MaterialContact");
bool direct = false;
if (v != "")
{
if (v.Contains("直接")) direct = true;
AppState.Add("零件", objName, "P14", "物料接触检查", "通过",
direct ? "属性标注: 直接接触 (需满足 GMP 材料/粗糙度/无死角要求)" : "属性标注: " + v,
direct ? "GMP" : "");
}
else
{
SimpleInput f = new SimpleInput("物料接触", "该零件是否与物料接触?\r\n1=直接接触\r\n2=间接接触(外表/非接触面)\r\n3=非接触\r\n0=跳过", "1");
if (f.ShowDialog() == DialogResult.OK)
{
int ans = 0;
int.TryParse(f.Value.Trim(), out ans);
switch (ans)
{
case 1: direct = true;
AppState.Add("零件", objName, "P14", "物料接触检查", "通过", "人工确认: 直接接触", "建议在 iProperties 自定义属性中标注'物料接触=直接接触'以固化");
break;
case 2: AppState.Add("零件", objName, "P14", "物料接触检查", "通过", "人工确认: 间接接触", ""); break;
case 3: AppState.Add("零件", objName, "P14", "物料接触检查", "通过", "人工确认: 非接触", ""); break;
default: AppState.Add("零件", objName, "P14", "物料接触检查", "警告", "未声明物料接触类别, 无法执行 GMP 专项检查", "建议标注接触类别"); break;
}
}
else
AppState.Add("零件", objName, "P14", "物料接触检查", "警告", "未声明物料接触类别, 无法执行 GMP 专项检查", "建议标注接触类别");
}
if (direct)
{
string mat = doc.MaterialName;
if (mat == "") mat = AppState.GetProp(doc.Props, "Material", "材料", "材质");
if (AppState.IsDefaultMaterial(mat))
AppState.Add("零件", objName, "P14b", "GMP 接触材料", "未通过", "材料未指定或为默认", "直接接触物料必须用卫生级不锈钢(316L)");
else
{
string rec, basis, vv;
vv = KB.GmpMaterialJudge(mat, out rec, out basis);
AppState.Add("零件", objName, "P14b", "GMP 接触材料", vv, "材料: " + mat + "; " + rec, basis);
}
ModelInfo m = doc.Model;
int nTex = m == null ? 0 : m.TexCount;
if (nTex > 0)
AppState.Add("零件", objName, "P14c", "GMP 接触面粗糙度", "通过", "已标注粗糙度(共 " + nTex + " 个)", "接触面 Ra<=0.8");
else
AppState.Add("零件", objName, "P14c", "GMP 接触面粗糙度", "未通过", "接触面未标注粗糙度", "接触面 Ra<=0.8");
if (m == null || !m.MinFilletReadable)
AppState.Add("零件", objName, "P14d", "GMP 无死角检查", "信息", "无法读取圆角特征", "");
else if (m.MinFilletRadiusMM > 0 && m.MinFilletRadiusMM < 10)
AppState.Add("零件", objName, "P14d", "GMP 无死角检查", "警告",
"存在小于 R10 的圆角(最小 R" + m.MinFilletRadiusMM.ToString("0.0") + " mm), 夹角处易积料",
"GMP: 夹角处圆角>=R10, 内夹角>=135度");
else
AppState.Add("零件", objName, "P14d", "GMP 无死角检查", "通过",
m.MinFilletRadiusMM > 0 ? "最小圆角 R" + m.MinFilletRadiusMM.ToString("0.0") + " mm >= R10" : "未发现圆角特征, 请人工确认",
"GMP 无死角要求");
}
}
private static void CheckSurfaceTreat(DocSnapshot doc, string objName)
{
string v = AppState.GetProp(doc.Props, "表面处理", "表面", "Surfacing", "镀层", "防锈", "SurfaceTreatment");
if (v == "") v = AppState.GetProp(doc.Props, "Comments", "备注", "注释");
if (v != "" && (v.Contains("镀") || v.Contains("氧化") || v.Contains("抛光") ||
v.Contains("防锈") || v.Contains("发黑") || v.Contains("喷砂")))
AppState.Add("零件", objName, "P16", "表面处理/防锈", "通过", v, "");
else
AppState.Add("零件", objName, "P16", "表面处理/防锈", "警告", "未标注表面处理/防锈要求(如镀镍是否需磨削/抛光、防锈包装)", "请确认是否适用");
}
private static void CheckStrength(DocSnapshot doc, string objName)
{
string mat = doc.MaterialName;
if (mat == "") mat = AppState.GetProp(doc.Props, "Material", "材料", "材质");
if (AppState.IsDefaultMaterial(mat))
AppState.Add("零件", objName, "P17", "结构强度/仿真", "未通过", "材料未指定, 无法进行强度分析", "需先指定材料");
else
AppState.Add("零件", objName, "P17", "结构强度/仿真", "信息",
"材料: " + mat + "; 建议使用 CAD 内置应力分析或导出模型进行有限元/仿真校核", "AI/CAE 校核指引");
}
private static void FitCheckLoop(DocSnapshot doc)
{
SimpleInput ask = new SimpleInput("公差配合检查", "是否进行公差配合检查?\r\n1=是\r\n0=否(跳过)", "1");
if (ask.ShowDialog() != DialogResult.OK) return;
int go = 0;
int.TryParse(ask.Value.Trim(), out go);
if (go != 1)
{
AppState.Add("零件", "零件", "P09", "公差配合检查", "信息", "用户跳过配合检查", "");
return;
}
ICadHost host = AppState.Host;
while (true)
{
double diaMM = 0;
string faceName = "手动输入";
try
{
if (host != null) diaMM = host.PickCylinderDiameterMM(out faceName);
}
catch { diaMM = 0; }
if (diaMM <= 0)
{
SimpleInput di = new SimpleInput("公差配合检查", "请输入配合直径 (mm):", "30");
if (di.ShowDialog() != DialogResult.OK) return;
double.TryParse(di.Value.Trim(), out diaMM);
if (diaMM <= 0) return;
}
FitForm form = new FitForm(diaMM, faceName);
if (form.ShowDialog() != DialogResult.OK) break;
foreach (FitItem it in form.Items)
{
string verdict = it.Verdict;
int seg = KB.SegIndex(it.DiaMM);
string note = "配合面: " + faceName + "; 直径 " + it.DiaMM.ToString("0.0") + " mm" +
(seg < 0 ? " (>500mm 超出表范围)" : " (尺寸段 " + KB.SegName(seg) + ")") +
"; 实际代码 " + it.Code + "; 推荐 " + it.Recommend + "; " + verdict;
AppState.Add("零件", "零件", "P09", "公差配合检查(" + it.Role + ")", verdict, note, it.Basis);
FitAdjacentChecks(doc, it.Role, it.DiaMM, faceName);
}
if (MessageBox.Show("配合检查结果已加入。是否继续检查其他配合面?", "公差配合检查",
MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes)
break;
}
}
private static void FitAdjacentChecks(DocSnapshot doc, string role, double diaMM, string faceName)
{
string objName = "零件(" + faceName + ")";
ModelInfo m = doc.Model;
int nFcf = m == null ? 0 : m.FcfCount;
int nTex = m == null ? 0 : m.TexCount;
if (nFcf > 0)
AppState.Add("零件", objName, "P10", "配合面形位公差", "通过", "模型已标注形位公差(共 " + nFcf + " 个)", "轴承位建议标注径向跳动/同轴度, 等级 IT5~IT6");
else
AppState.Add("零件", objName, "P10", "配合面形位公差", "未通过", "配合面未标注形位公差", "轴承轴颈建议标注径向跳动/同轴度(IT5~IT6); 座孔标注同轴度/垂直度");
if (nTex > 0)
AppState.Add("零件", objName, "P11", "配合面粗糙度", "通过", "已标注粗糙度(共 " + nTex + " 个)", "推荐: " + KB.RaSuggest(role));
else
AppState.Add("零件", objName, "P11", "配合面粗糙度", "未通过", "配合面未标注表面粗糙度", "推荐: " + KB.RaSuggest(role));
if (role.StartsWith("轴承"))
{
int seg = KB.SegIndex(diaMM);
string tolNote;
if (seg > 0)
tolNote = "尺寸段 " + KB.SegName(seg) + " 对应 IT6=" + KB.ItTol(6, seg) + "um, IT7=" + KB.ItTol(7, seg) + "um";
else
tolNote = "直径超过 500mm, 无内置数据";
AppState.Add("零件", objName, "P12", "轴承位公差等级", "信息", tolNote, "轴承轴颈公差等级一般取 IT5~IT6, 座孔 IT7");
}
}
// ================= 装配阶段 =================
public static void RunAssemblyChecks(DocSnapshot doc)
{
string objName = "装配";
if (doc.Dirty)
AppState.Add("装配", objName, "A01", "文档保存状态", "警告", "存在未保存的修改", "");
else
AppState.Add("装配", objName, "A01", "文档保存状态", "通过", "已保存", "");
GmpMaterialCheck(doc, objName);
SealCheck(doc, objName);
CleanCheck(doc, objName);
GmpRoughnessCheck(doc, objName);
InterferenceCheck(doc, objName);
StandardPartsCheck(doc, objName);
ElectricsCheck(doc, objName);
UtilityCheck(doc, objName);
AppState.Add("装配", objName, "A12", "运动干涉(动态)", "信息",
"建议使用 CAD 驱动约束/运动仿真验证运动干涉; 本工具提供静态干涉结果", "运动学检查需要仿真环境");
}
private static void GmpMaterialCheck(DocSnapshot doc, string objName)
{
int nPart = 0, nOk = 0, nWarn = 0, nBad = 0;
string badList = "";
foreach (DocInfo d in doc.OpenDocs)
{
if (d == null) continue;
nPart++;
string rec, basis, v;
v = KB.GmpMaterialJudge(d.Material, out rec, out basis);
if (v == "合理") nOk++;
else if (v == "可接受" || v == "警告") nWarn++;
else nBad++;
if (v != "合理" && badList.Length < 400)
badList += d.Name + "(" + d.Material + "): " + rec + " | ";
}
if (nPart == 0)
AppState.Add("装配", objName, "A02", "GMP 材料合规", "信息", "未发现已加载的零件文档(仅检查已打开文档)", "");
else if (nBad == 0 && nWarn == 0)
AppState.Add("装配", objName, "A02", "GMP 材料合规", "通过", "全部 " + nPart + " 个零件材料符合 GMP 要求", "GMP 设备设计规范");
else if (nBad == 0)
AppState.Add("装配", objName, "A02", "GMP 材料合规", "警告", nWarn + "/" + nPart + " 个零件材料需关注: " + badList, "GMP 设备设计规范");
else
AppState.Add("装配", objName, "A02", "GMP 材料合规", "未通过", nBad + " 个零件材料不合格, " + nWarn + " 个需关注: " + badList, "GMP 设备设计规范");
}
private static void SealCheck(DocSnapshot doc, string objName)
{
string names = "";
int n = 0;
AssemblyInfo asm = doc.Asm;
if (asm != null)
{
foreach (string nm in asm.OccurrenceNames)
{
if (KB.IsSealPartName(nm))
{
n++;
if (names.Length < 300) names += nm + "; ";
}
}
}
if (n > 0)
{
AppState.Add("装配", objName, "A03", "密封件存在性", "通过", "发现 " + n + " 个密封元件: " + names, "GMP 密封要求");
AppState.Add("装配", objName, "A04", "密封设计提示", "信息",
"请核对: 密封槽尺寸是否符合 GB/T 3452.3(O型圈槽); 密封材料需符合 FDA 要求; 动密封面 Ra<=0.8", "GB/T 3452.3");
}
else
{
DialogResult ans = MessageBox.Show("未在装配中发现密封元件(名称不含 密封圈/O型圈/垫片 等关键词)。\r\n该设备是否需要密封设计?",
"密封检查", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (ans == DialogResult.Yes)
AppState.Add("装配", objName, "A03", "密封件存在性", "未通过", "未发现密封元件, 需补充密封设计或确认命名规范", "GMP 密封要求");
else
AppState.Add("装配", objName, "A03", "密封件存在性", "通过", "无密封要求(用户确认)", "");
}
}
private static void CleanCheck(DocSnapshot doc, string objName)
{
string hits;
List<DocInfo> docs = new List<DocInfo>();
docs.Add(new DocInfo { Name = doc.DisplayName, Props = doc.Props, Material = doc.MaterialName });
docs.AddRange(doc.OpenDocs);
if (AppState.HitKeywords(docs, "清洁,清洗,CIP,SIP,死角,排空,卫生,可清洗", out hits))
AppState.Add("装配", objName, "A05", "清洁性设计要求", "通过", "已体现清洁性设计说明: " + hits, "GMP 清洁性要求");
else
AppState.Add("装配", objName, "A05", "清洁性设计要求", "警告", "未在文档属性中发现清洁/清洗/CIP/死角等设计要求说明", "GMP 要求: 设备应可清洁、无死角、易排空");
}
private static void GmpRoughnessCheck(DocSnapshot doc, string objName)
{
string noTex = "";
int nPart = 0, nWith = 0;
foreach (DocInfo d in doc.OpenDocs)
{
if (d == null) continue;
nPart++;
if (d.TexCount > 0) nWith++;
else if (noTex.Length < 300) noTex += d.Name + "; ";
}
if (nPart == 0)
AppState.Add("装配", objName, "A06", "GMP 粗糙度标注", "信息", "未发现已加载的零件文档", "");
else if (nWith == nPart)
AppState.Add("装配", objName, "A06", "GMP 粗糙度标注", "通过", "所有 " + nPart + " 个零件均标注了粗糙度", "GMP 接触面 Ra<=0.8");
else
AppState.Add("装配", objName, "A06", "GMP 粗糙度标注", "警告", (nPart - nWith) + "/" + nPart + " 个零件未标注粗糙度: " + noTex, "GMP 接触面 Ra<=0.8; 建议物料接触面电解抛光");
}
private static void InterferenceCheck(DocSnapshot doc, string objName)
{
AssemblyInfo asm = doc.Asm;
if (asm == null)
{
AppState.Add("装配", objName, "A07", "静态干涉", "信息", "无法读取装配信息", "");
return;
}
if (asm.InterferenceState == 2)
{
AppState.Add("装配", objName, "A07", "静态干涉", "信息", "干涉分析执行失败(" + asm.InterferenceError + "), 请使用 CAD 自带干涉检查验证", "");
return;
}
if (asm.InterferenceState == 0)
AppState.Add("装配", objName, "A07", "静态干涉", "通过", "未发现静态干涉(共检查 " + asm.TopLevelCount + " 个顶层零部件)", "干涉检查");
else
{
string detail = "";
for (int j = 0; j < asm.Interferences.Count && detail.Length < 300; j++)
{
InterferencePair p = asm.Interferences[j];
detail += p.Name1 + " 与 " + p.Name2 + " 体积 " + p.VolumeCM3.ToString("0.00") + "cm3; ";
}
AppState.Add("装配", objName, "A07", "静态干涉", "未通过", "发现 " + asm.Interferences.Count + " 处静态干涉: " + detail, "干涉检查");
}
}
private static void StandardPartsCheck(DocSnapshot doc, string objName)
{
AssemblyInfo asm = doc.Asm;
string stdNames = "";
int stdN = 0, totalN = 0;
if (asm != null)
{
totalN = asm.OccurrenceNames.Count;
foreach (string nm in asm.OccurrenceNames)
{
if (KB.IsStdPartName(nm))
{
stdN++;
if (stdNames.Length < 200) stdNames += nm + "; ";
}
}
}
if (totalN == 0)
AppState.Add("装配", objName, "A09", "外购件优先", "信息", "未统计到零部件", "");
else
{
double ratio = (double)stdN / (double)totalN;
AppState.Add("装配", objName, "A09", "外购件优先", "通过",
"标准/外购件 " + stdN + "/" + totalN + " (" + ratio.ToString("0%") + "): " + stdNames, "优先选用标准外购件");
}
}
private static void ElectricsCheck(DocSnapshot doc, string objName)
{
string hits;
List<DocInfo> docs = new List<DocInfo>();
docs.Add(new DocInfo { Name = doc.DisplayName, Props = doc.Props, Material = doc.MaterialName });
docs.AddRange(doc.OpenDocs);
if (AppState.HitKeywords(docs, "西门子,SIEMENS,联网,以太网,PROFINET,PLC,IP65,防爆,电气,变频器,伺服", out hits))
AppState.Add("装配", objName, "A10", "电气选型/西门子联网", "通过", hits, "电气元件选型技术要求");
else
AppState.Add("装配", objName, "A10", "电气选型/西门子联网", "警告", "未发现电气选型/西门子联网相关说明", "如涉及电气元件, 需注明选型及联网(如西门子 PROFINET)要求");
}
private static void UtilityCheck(DocSnapshot doc, string objName)
{
string hits;
List<DocInfo> docs = new List<DocInfo>();
docs.Add(new DocInfo { Name = doc.DisplayName, Props = doc.Props, Material = doc.MaterialName });
docs.AddRange(doc.OpenDocs);
if (AppState.HitKeywords(docs, "施工,动力,公用工程,压缩空气,纯化水,蒸汽,排风,水电气,安装要求,配孔,吊顶", out hits))
AppState.Add("装配", objName, "A11", "施工/公共动力系统", "通过", hits, "施工及公用动力安装技术要求");
else
AppState.Add("装配", objName, "A11", "施工/公共动力系统", "警告", "未发现施工/公共动力系统相关说明(压缩空气/纯化水/蒸汽/排风/配孔/吊顶等)", "如涉及需补充技术要求");
}
// ================= 工程图阶段 =================
private class SpecGroup
{
public string ID;
public string Name;
public string Keys;
}
private static readonly SpecGroup[] Specs = new SpecGroup[]
{
new SpecGroup { ID = "D18", Name = "物料接触类别声明", Keys = "接触,物料接触,直接接触,间接接触,接触类别" },
new SpecGroup { ID = "D19", Name = "焊接技术要求", Keys = "焊接,双面焊,单面焊,打磨,焊缝,焊接标准" },
new SpecGroup { ID = "D20", Name = "死角/夹角要求", Keys = "死角,无死角,夹角,圆角,清洁,排空,R10,135" },
new SpecGroup { ID = "D21", Name = "公差要求完整性", Keys = "公差,配合,精度,未注公差" },
new SpecGroup { ID = "D22", Name = "轴承位公差标注", Keys = "轴承,跳动,同轴,旋转精度" },
new SpecGroup { ID = "D23", Name = "螺纹孔标注", Keys = "螺纹,螺孔,攻丝,丝锥,M6,M8,M10,M12,M16" },
new SpecGroup { ID = "D24", Name = "表面处理/镀镍", Keys = "镀镍,镀铬,镀锌,电镀,抛光,表面处理,磨床,磨削,防锈,发黑,阳极氧化" },
new SpecGroup { ID = "D25", Name = "平面度/平行度", Keys = "平面度,平行度,垂直度" },
new SpecGroup { ID = "D26", Name = "特殊角度管加工", Keys = "角度管,弯管,折弯,倾斜,特殊角度" },
new SpecGroup { ID = "D27", Name = "装配治具与检验", Keys = "治具,检具,检验,检测,夹具" },
new SpecGroup { ID = "D28", Name = "加工辅助件要求", Keys = "辅助件,工艺,毛坯,余量,加工基准" },
new SpecGroup { ID = "D29", Name = "外购件优先", Keys = "外购,标准件,优先采购" },
new SpecGroup { ID = "D30", Name = "测试要求", Keys = "测试,试验,试压,气密,检漏,出厂测试" },
new SpecGroup { ID = "D31", Name = "装配要求", Keys = "装配,安装,扭矩,紧固,间隙" },
new SpecGroup { ID = "D32", Name = "防锈要求", Keys = "防锈,防锈油,防锈剂,包装,海运,真空包装" },
new SpecGroup { ID = "D33", Name = "施工/公共动力系统", Keys = "施工,动力,公用工程,水电气,压缩空气,蒸汽,纯化水,排风" },
new SpecGroup { ID = "D34", Name = "配孔技术要求", Keys = "配孔,配钻,现场配,扩孔" },
new SpecGroup { ID = "D35", Name = "吊顶配件要求", Keys = "吊顶,吊架,吊装,悬挂" },
new SpecGroup { ID = "D36", Name = "电气元件选型", Keys = "电气,元件,选型,防爆,防护等级,IP65" },
new SpecGroup { ID = "D37", Name = "西门子联网器件", Keys = "西门子,SIEMENS,联网,以太网,PROFINET,总线" },
new SpecGroup { ID = "D38", Name = "结构强度/仿真", Keys = "强度,仿真,有限元,FEA,应力" },
new SpecGroup { ID = "D39", Name = "夹角R10/135度确认", Keys = "夹角,135,R10,圆角" }
};
public static void RunDrawingChecks(DocSnapshot doc)
{
string objName = "工程图";
if (doc.Dirty)
AppState.Add("工程图", objName, "D00", "文档保存状态", "警告", "存在未保存的修改", "");
else
AppState.Add("工程图", objName, "D00", "文档保存状态", "通过", "已保存", "");
int idx = 0;
foreach (SheetInfo sh in doc.Sheets)
{
idx++;
CheckSheet(doc, sh, idx);
}
RunSpecChecks(doc);
}
private static void CheckSheet(DocSnapshot doc, SheetInfo sh, int idx)
{
string objName = "图纸" + idx + "(" + sh.Name + ")";
if (!sh.HasBorder && !sh.HasTitleBlock)
AppState.Add("工程图", objName, "D01", "图框", "未通过", "图纸没有图框/标题栏", "");
else if (!sh.HasTitleBlock)
AppState.Add("工程图", objName, "D01", "图框", "警告", "有图框但无标题栏", "");
else
AppState.Add("工程图", objName, "D01", "图框", "通过", "标题栏: " + sh.TitleBlockName, "");
CheckTitleBlockProps(doc, objName);
int nViews = sh.ViewCount;
int nAnn = sh.DimCount + sh.FcfCount + sh.TexCount + sh.CenterlineCount + sh.BalloonCount + sh.NotesTexts.Count;
if (nViews == 0 && nAnn == 0)
AppState.Add("工程图", objName, "D03", "空白图纸", "未通过", "图纸无视图且无任何标注", "");
else
AppState.Add("工程图", objName, "D03", "空白图纸", "通过", "视图 " + nViews + " 个, 标注 " + nAnn + " 个", "");
if (sh.UpdateError > 0)
AppState.Add("工程图", objName, "D04", "视图更新状态", "未通过", sh.UpdateError + " 个视图更新出错", "");
else if (sh.UpdateNeeded > 0)
AppState.Add("工程图", objName, "D04", "视图更新状态", "未通过", sh.UpdateNeeded + " 个视图需要更新", "出图前必须更新视图");
else
AppState.Add("工程图", objName, "D04", "视图更新状态", "通过", "所有视图已更新", "");
AppState.Add("工程图", objName, "D05", "剖视图统计", "通过", "共 " + sh.SectionCount + " 个剖视图", "剖视图需带剖面线及剖切位置标注");
if (sh.BorderRect != null)
{
int nOut = 0;
foreach (RectD r in sh.ViewRects)
{
if (RectOut(r, sh.BorderRect, 0.5)) nOut++;
}
if (nOut > 0)
AppState.Add("工程图", objName, "D06", "视图越界", "未通过", nOut + " 个视图超出图框", "");
else
AppState.Add("工程图", objName, "D06", "视图越界", "通过", "视图均在图框内", "");
}
else
AppState.Add("工程图", objName, "D06", "视图越界", "信息", "无图框可参考, 跳过", "");
AppState.Add("工程图", objName, "D07", "尺寸数量", "通过", "共 " + sh.DimCount + " 个尺寸", "");
if (sh.DimCount == 0)
AppState.Add("工程图", objName, "D08", "尺寸公差", "信息", "无尺寸可统计", "");
else if (sh.DimTolCount == 0 && sh.DimCount >= 5)
AppState.Add("工程图", objName, "D08", "尺寸公差", "警告", "共 " + sh.DimCount + " 个尺寸均未标注公差", "配合/关键尺寸必须标注公差");
else
AppState.Add("工程图", objName, "D08", "尺寸公差", "通过", sh.DimTolCount + "/" + sh.DimCount + " 个尺寸带公差", "");
if (sh.DimQuestionCount > 0)
AppState.Add("工程图", objName, "D09", "尺寸文本异常", "未通过", sh.DimQuestionCount + " 个尺寸文本含\"?\"(测量失败或未更新)", "");
else
AppState.Add("工程图", objName, "D09", "尺寸文本异常", "通过", "尺寸文本正常", "");
AppState.Add("工程图", objName, "D10", "形位公差", "通过", "共 " + sh.FcfCount + " 个", "");
AppState.Add("工程图", objName, "D11", "基准符号", "信息", "CAD API 不支持自动读取基准符号, 请人工核对", "");
AppState.Add("工程图", objName, "D12", "表面粗糙度", "通过", "共 " + sh.TexCount + " 个", "");
AppState.Add("工程图", objName, "D13", "焊接符号", "信息", "CAD API 不支持自动读取焊接符号, 由 D19 焊接技术要求关键词检查覆盖", "spec 文件");
AppState.Add("工程图", objName, "D14", "孔标注", "信息", "CAD API 不支持自动读取孔标注, 由 D23 螺纹孔关键词检查覆盖", "spec 文件");
AppState.Add("工程图", objName, "D15", "中心线/件号", "通过", "中心线 " + sh.CenterlineCount + " 个, 件号 " + sh.BalloonCount + " 个", "");
if (sh.BorderRect != null)
{
int nOut = 0;
string firstNote = "";
foreach (RectD r in sh.FcfRects)
{
if (RectOut(r, sh.BorderRect, 0.2)) { nOut++; if (firstNote == "") firstNote = "形位公差"; }
}
foreach (RectD r in sh.TexRects)
{
if (RectOut(r, sh.BorderRect, 0.2)) { nOut++; if (firstNote == "") firstNote = "粗糙度"; }
}
if (nOut > 0)
AppState.Add("工程图", objName, "D16", "标注越界", "未通过", nOut + " 个标注超出图框, 如: " + firstNote, "");
else
AppState.Add("工程图", objName, "D16", "标注越界", "通过", "标注均在图框内", "");
}
else
AppState.Add("工程图", objName, "D16", "标注越界", "信息", "无图框可参考, 跳过", "");
AppState.Add("工程图", objName, "D17", "图幅", "信息", sh.SheetWidth + " x " + sh.SheetHeight + " (cm)", "");
}
private static bool RectOut(RectD r, RectD b, double margin)
{
if (r == null || b == null) return false;
return (r.L < b.L - margin) || (r.T < b.T - margin) || (r.R > b.R + margin) || (r.B > b.B + margin);
}
private static void CheckTitleBlockProps(DocSnapshot doc, string objName)
{
string missing = "";
string v = AppState.GetProp(doc.Props, "Part Number", "零件代号", "零件号", "图号");
if (v == "") missing += "零件代号/图号 ";
v = AppState.GetProp(doc.Props, "Description", "描述", "名称", "部件名称");
if (v == "") missing += "名称 ";
v = AppState.GetProp(doc.Props, "Material", "材料", "材质");
if (v == "") missing += "材料 ";
v = AppState.GetProp(doc.Props, "Designer", "设计者", "设计");
if (v == "") missing += "设计者 ";
v = AppState.GetProp(doc.Props, "Checked By", "检查者", "审核者", "校核");
if (v == "") missing += "审核者 ";
v = AppState.GetProp(doc.Props, "Revision Number", "修订号", "版本号");
if (v == "") missing += "修订号 ";
if (missing == "")
AppState.Add("工程图", objName, "D02", "标题栏属性", "通过", "图号/名称/材料/设计者/审核者/修订号 均已填写", "");
else
AppState.Add("工程图", objName, "D02", "标题栏属性", "未通过", "未填写: " + missing, "标题栏信息完整性");
}
private static void RunSpecChecks(DocSnapshot doc)
{
string notes = "";
foreach (SheetInfo sh in doc.Sheets)
{
foreach (string t in sh.NotesTexts)
notes += " " + t;
}
notes += " " + AppState.GetProp(doc.Props, "Comments", "备注", "注释", "Keywords", "关键词", "Description", "描述");
foreach (SpecGroup g in Specs)
{
string[] keys = g.Keys.Split(',');
bool found = false;
string hitKey = "";
foreach (string k in keys)
{
if (notes.ToUpper().Contains(k.ToUpper()))
{
found = true;
hitKey = k;
break;
}
}
if (found)
AppState.Add("工程图", "工程图", g.ID, g.Name, "通过", "图纸中已标注相关要求(关键词: " + hitKey + ")", "spec 文件");
else
AppState.Add("工程图", "工程图", g.ID, g.Name, "警告", "图纸技术要求中未发现[" + g.Name + "]相关要求, 如适用请补充标注或人工确认", "spec 文件");
}
}
}
}
+115
View File
@@ -0,0 +1,115 @@
using System;
using System.Windows.Forms;
namespace DesignExpress.Core
{
// Core 统一入口: 供各 CAD Link 调用(Inventor/Creo/SolidWorks)
public static class Checks
{
private static MainForm mForm;
private static void EnsureForm()
{
if (mForm == null || mForm.IsDisposed)
mForm = new MainForm();
}
private static bool Prepare(out DocSnapshot snap, params string[] allowKinds)
{
snap = null;
ICadHost host = AppState.Host;
if (host == null) return false;
object doc = host.GetActiveDocument();
if (doc == null)
{
MessageBox.Show("请先打开文档", "Design Express Res", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
return false;
}
string kind = host.GetDocType(doc);
bool ok = false;
foreach (string k in allowKinds)
{
if (kind == k) ok = true;
}
if (!ok)
{
MessageBox.Show("当前文档类型不支持该检查", "Design Express Res", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
return false;
}
try { snap = host.Capture(doc); }
catch (Exception ex)
{
MessageBox.Show("读取文档失败: " + ex.Message, "Design Express Res", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
return snap != null;
}
public static void PartCheck()
{
DocSnapshot snap;
if (!Prepare(out snap, "Part", "Assembly")) return;
EnsureForm();
mForm.Show();
mForm.Activate();
mForm.RunAndLoad("零件", delegate { CheckEngine.RunPartChecks(snap); });
}
public static void AssemblyCheck()
{
DocSnapshot snap;
if (!Prepare(out snap, "Assembly")) return;
EnsureForm();
mForm.Show();
mForm.Activate();
mForm.RunAndLoad("装配", delegate { CheckEngine.RunAssemblyChecks(snap); });
}
public static void DrawingCheck()
{
DocSnapshot snap;
if (!Prepare(out snap, "Drawing")) return;
EnsureForm();
mForm.Show();
mForm.Activate();
mForm.RunAndLoad("工程图", delegate { CheckEngine.RunDrawingChecks(snap); });
}
public static void ExportReport()
{
if (AppState.Results.Count == 0)
{
MessageBox.Show("暂无检查结果, 请先执行检查", "Design Express Res", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
SaveFileDialog dlg = new SaveFileDialog();
dlg.Title = "保存检查报告";
dlg.Filter = "Excel 报告 (*.xlsx)|*.xlsx|CSV 报告 (*.csv)|*.csv";
dlg.FileName = "检查报告_" + DateTime.Now.ToString("yyyyMMdd_HHmmss") + ".xlsx";
if (dlg.ShowDialog() == DialogResult.OK)
{
string file = dlg.FileName.ToLower().EndsWith(".csv") ? Report.WriteCsv(dlg.FileName) : Report.WriteExcel(dlg.FileName);
MessageBox.Show("报告已生成: " + file, "Design Express Res", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
public static void Help()
{
MessageBox.Show(
"Design Express Res v" + Report.ToolVersion + "\r\n\r\n" +
"零件检查: 标注统计、公差配合(GB/T 275)、形位公差、粗糙度、螺纹孔、物料接触 GMP、表面处理、强度指引\r\n" +
"装配检查: GMP 材料、密封件、清洁性、粗糙度、静态干涉、外购件、电气/西门子、动力系统\r\n" +
"工程图检查: 图框/标题栏、视图更新、尺寸公差、标注统计、22 项 spec 技术要求关键词检查\r\n\r\n" +
"架构: Core 独立核心库 + 各 CAD Link 适配层(Inventor/Creo/SolidWorks), 类似 COMSOL LiveLink。\r\n" +
"报告输出为 Excel(汇总/明细/检查项清单), Excel 不可用时输出 CSV。",
"使用说明", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
// ============ 设计数据查询工具入口(独立系统) ============
public static void CirclipQuery() { QueryForm.ShowQuery(0); }
public static void KeywayQuery() { QueryForm.ShowQuery(1); }
public static void OringQuery() { QueryForm.ShowQuery(2); }
public static void BearingRingQuery() { QueryForm.ShowQuery(3); }
}
}
+208
View File
@@ -0,0 +1,208 @@
using System;
using System.Collections.Generic;
namespace DesignExpress.Core
{
public class QueryRow
{
public string Item;
public string Value;
public string Std;
public string Remark;
}
public enum QueryKind
{
Circlip = 0, // 卡簧槽 GB/T 894.1
Keyway = 1, // 键槽 GB/T 1095
Oring = 2, // O 型密封圈 GB/T 3452.1 (规划中)
BearingRing = 3 // 轴承挡圈 (规划中)
}
// 设计尺寸查询数据引擎: 纯数据表查询, 与 CAD 无关
// 注: 数据为常用参考值, 正式设计请核对最新版标准
public static class DesignQuery
{
private const string DISCLAIMER = "参考值, 以最新标准为准";
// ================= 键槽 (GB/T 1095) =================
private class KeywayRow
{
public double Dmin, Dmax;
public string KeySize; // b×h
public double T1, T2; // 轴槽深, 轮毂槽深
public string LRange; // 键长范围
}
private static readonly KeywayRow[] Keyways = new KeywayRow[]
{
new KeywayRow { Dmin=6, Dmax=8, KeySize="2×2", T1=1.2, T2=1.0, LRange="6~20" },
new KeywayRow { Dmin=8, Dmax=10, KeySize="3×3", T1=1.8, T2=1.4, LRange="6~36" },
new KeywayRow { Dmin=10, Dmax=12, KeySize="4×4", T1=2.5, T2=1.8, LRange="8~45" },
new KeywayRow { Dmin=12, Dmax=17, KeySize="5×5", T1=3.0, T2=2.3, LRange="10~56" },
new KeywayRow { Dmin=17, Dmax=22, KeySize="6×6", T1=3.5, T2=2.8, LRange="14~70" },
new KeywayRow { Dmin=22, Dmax=30, KeySize="8×7", T1=4.0, T2=3.3, LRange="18~90" },
new KeywayRow { Dmin=30, Dmax=38, KeySize="10×8", T1=5.0, T2=3.3, LRange="22~110" },
new KeywayRow { Dmin=38, Dmax=44, KeySize="12×8", T1=5.0, T2=3.3, LRange="28~140" },
new KeywayRow { Dmin=44, Dmax=50, KeySize="14×9", T1=5.5, T2=3.8, LRange="36~160" },
new KeywayRow { Dmin=50, Dmax=58, KeySize="16×10", T1=6.0, T2=4.3, LRange="45~180" },
new KeywayRow { Dmin=58, Dmax=65, KeySize="18×11", T1=7.0, T2=4.4, LRange="50~200" },
new KeywayRow { Dmin=65, Dmax=75, KeySize="20×12", T1=7.5, T2=4.9, LRange="56~220" },
new KeywayRow { Dmin=75, Dmax=85, KeySize="22×14", T1=9.0, T2=5.4, LRange="63~250" },
new KeywayRow { Dmin=85, Dmax=95, KeySize="25×14", T1=9.0, T2=5.4, LRange="70~280" },
new KeywayRow { Dmin=95, Dmax=110, KeySize="28×16", T1=10.0, T2=6.4, LRange="80~320" },
new KeywayRow { Dmin=110, Dmax=130, KeySize="32×18", T1=11.0, T2=7.4, LRange="90~360" },
new KeywayRow { Dmin=130, Dmax=150, KeySize="36×20", T1=12.0, T2=8.4, LRange="100~400" },
new KeywayRow { Dmin=150, Dmax=170, KeySize="40×22", T1=13.0, T2=9.4, LRange="100~400" },
new KeywayRow { Dmin=170, Dmax=200, KeySize="45×25", T1=15.0, T2=10.4, LRange="110~450" },
new KeywayRow { Dmin=200, Dmax=230, KeySize="50×28", T1=17.0, T2=11.4, LRange="125~500" },
new KeywayRow { Dmin=230, Dmax=260, KeySize="56×32", T1=20.0, T2=12.4, LRange="140~500" },
new KeywayRow { Dmin=260, Dmax=290, KeySize="63×32", T1=20.0, T2=12.4, LRange="160~500" },
new KeywayRow { Dmin=290, Dmax=330, KeySize="70×36", T1=22.0, T2=14.4, LRange="180~500" },
new KeywayRow { Dmin=330, Dmax=380, KeySize="80×40", T1=25.0, T2=15.4, LRange="200~500" },
new KeywayRow { Dmin=380, Dmax=440, KeySize="90×45", T1=28.0, T2=17.4, LRange="220~500" },
new KeywayRow { Dmin=440, Dmax=500, KeySize="100×50", T1=31.0, T2=19.5, LRange="250~500" }
};
private static readonly double[] KeyLenSeries =
{ 6, 8, 10, 12, 14, 16, 18, 20, 22, 25, 28, 32, 36, 40, 45, 50, 56, 63, 70, 80, 90, 100,
110, 125, 140, 160, 180, 200, 220, 250, 280, 320, 360, 400, 450, 500 };
private static List<QueryRow> QueryKeyway(double d)
{
List<QueryRow> rows = new List<QueryRow>();
KeywayRow hit = null;
foreach (KeywayRow k in Keyways)
{
if (d > k.Dmin && d <= k.Dmax) { hit = k; break; }
}
if (hit == null)
{
rows.Add(new QueryRow { Item = "查询结果", Value = "超出数据范围", Std = "GB/T 1095", Remark = "支持轴径 6~500mm" });
return rows;
}
rows.Add(new QueryRow { Item = "适用轴径 d", Value = ">" + hit.Dmin + "~" + hit.Dmax, Std = "GB/T 1095", Remark = "" });
rows.Add(new QueryRow { Item = "键公称尺寸 b×h", Value = hit.KeySize, Std = "GB/T 1095", Remark = "" });
rows.Add(new QueryRow { Item = "轴槽深 t1", Value = hit.T1.ToString("0.0"), Std = "GB/T 1095", Remark = "轴直径 - t1 = 槽底直径" });
rows.Add(new QueryRow { Item = "轮毂槽深 t2", Value = hit.T2.ToString("0.0"), Std = "GB/T 1095", Remark = "孔直径 + t2 = 槽底直径" });
rows.Add(new QueryRow { Item = "键长范围 L", Value = hit.LRange, Std = "GB/T 1095", Remark = "从标准长度系列选取" });
// 长度系列中落在范围内的
double lo = 0, hi = 0;
string[] parts = hit.LRange.Split('~');
double.TryParse(parts[0], out lo);
double.TryParse(parts[1], out hi);
List<string> sel = new List<string>();
foreach (double len in KeyLenSeries)
{
if (len >= lo && len <= hi) sel.Add(len.ToString("0"));
}
rows.Add(new QueryRow { Item = "可用键长", Value = string.Join(", ", sel.ToArray()), Std = "GB/T 1095", Remark = DISCLAIMER });
return rows;
}
// ================= 卡簧槽 - 轴用弹性挡圈 (GB/T 894.1 A型) =================
private class CirclipRow
{
public double D; // 轴径
public double OD; // 挡圈外径
public double Depth; // 槽深
public double SlotW; // 槽宽
}
private static readonly CirclipRow[] Circlips = new CirclipRow[]
{
new CirclipRow { D=3, OD=8.2, Depth=0.4, SlotW=0.6 },
new CirclipRow { D=4, OD=9.6, Depth=0.4, SlotW=0.6 },
new CirclipRow { D=5, OD=11.0, Depth=0.5, SlotW=0.7 },
new CirclipRow { D=6, OD=13.0, Depth=0.7, SlotW=0.9 },
new CirclipRow { D=7, OD=14.5, Depth=0.7, SlotW=0.9 },
new CirclipRow { D=8, OD=16.0, Depth=0.7, SlotW=0.9 },
new CirclipRow { D=9, OD=17.5, Depth=0.8, SlotW=1.0 },
new CirclipRow { D=10, OD=19.0, Depth=0.8, SlotW=1.0 },
new CirclipRow { D=11, OD=21.0, Depth=0.8, SlotW=1.0 },
new CirclipRow { D=12, OD=22.5, Depth=0.9, SlotW=1.2 },
new CirclipRow { D=13, OD=24.0, Depth=0.9, SlotW=1.2 },
new CirclipRow { D=14, OD=25.5, Depth=0.9, SlotW=1.2 },
new CirclipRow { D=15, OD=27.0, Depth=0.9, SlotW=1.2 },
new CirclipRow { D=16, OD=28.5, Depth=1.0, SlotW=1.2 },
new CirclipRow { D=17, OD=30.0, Depth=1.0, SlotW=1.2 },
new CirclipRow { D=18, OD=32.0, Depth=1.1, SlotW=1.5 },
new CirclipRow { D=19, OD=33.5, Depth=1.1, SlotW=1.5 },
new CirclipRow { D=20, OD=35.0, Depth=1.1, SlotW=1.5 },
new CirclipRow { D=21, OD=36.5, Depth=1.1, SlotW=1.5 },
new CirclipRow { D=22, OD=38.0, Depth=1.1, SlotW=1.5 },
new CirclipRow { D=24, OD=41.0, Depth=1.2, SlotW=1.5 },
new CirclipRow { D=25, OD=42.5, Depth=1.2, SlotW=1.5 },
new CirclipRow { D=26, OD=44.0, Depth=1.2, SlotW=1.5 },
new CirclipRow { D=28, OD=47.0, Depth=1.3, SlotW=1.8 },
new CirclipRow { D=30, OD=50.0, Depth=1.3, SlotW=1.8 },
new CirclipRow { D=32, OD=53.0, Depth=1.4, SlotW=1.8 },
new CirclipRow { D=34, OD=56.0, Depth=1.4, SlotW=1.8 },
new CirclipRow { D=35, OD=57.5, Depth=1.4, SlotW=1.8 },
new CirclipRow { D=36, OD=59.0, Depth=1.5, SlotW=2.0 },
new CirclipRow { D=38, OD=62.0, Depth=1.5, SlotW=2.0 },
new CirclipRow { D=40, OD=65.0, Depth=1.5, SlotW=2.0 },
new CirclipRow { D=42, OD=68.0, Depth=1.6, SlotW=2.0 },
new CirclipRow { D=45, OD=72.0, Depth=1.6, SlotW=2.0 },
new CirclipRow { D=48, OD=76.0, Depth=1.6, SlotW=2.0 },
new CirclipRow { D=50, OD=79.0, Depth=1.7, SlotW=2.5 }
};
private static List<QueryRow> QueryCirclip(double d)
{
List<QueryRow> rows = new List<QueryRow>();
CirclipRow hit = null;
foreach (CirclipRow c in Circlips)
{
if (Math.Abs(c.D - d) < 0.001) { hit = c; break; }
}
if (hit == null)
{
// 未精确匹配: 取最接近的相邻挡圈
CirclipRow lower = null, upper = null;
foreach (CirclipRow c in Circlips)
{
if (c.D < d && (lower == null || c.D > lower.D)) lower = c;
if (c.D > d && (upper == null || c.D < upper.D)) upper = c;
}
if (lower == null && upper == null)
{
rows.Add(new QueryRow { Item = "查询结果", Value = "超出数据范围", Std = "GB/T 894.1", Remark = "支持轴径 3~50mm" });
return rows;
}
rows.Add(new QueryRow { Item = "查询结果", Value = "非标准轴径, 请就近选用", Std = "GB/T 894.1", Remark = DISCLAIMER });
if (lower != null)
rows.Add(new QueryRow { Item = "小于轴径的挡圈", Value = "d=" + lower.D + " 外径D=" + lower.OD + " 槽深t=" + lower.Depth + " 槽宽m=" + lower.SlotW, Std = "GB/T 894.1", Remark = "" });
if (upper != null)
rows.Add(new QueryRow { Item = "大于轴径的挡圈", Value = "d=" + upper.D + " 外径D=" + upper.OD + " 槽深t=" + upper.Depth + " 槽宽m=" + upper.SlotW, Std = "GB/T 894.1", Remark = "" });
return rows;
}
rows.Add(new QueryRow { Item = "轴径 d", Value = hit.D.ToString("0"), Std = "GB/T 894.1", Remark = "A 型轴用弹性挡圈" });
rows.Add(new QueryRow { Item = "挡圈外径 D", Value = hit.OD.ToString("0.0"), Std = "GB/T 894.1", Remark = "" });
rows.Add(new QueryRow { Item = "沟槽直径 d1", Value = (hit.D - 2 * hit.Depth).ToString("0.0"), Std = "GB/T 894.1", Remark = "槽底直径 = d - 2t" });
rows.Add(new QueryRow { Item = "沟槽深度 t", Value = hit.Depth.ToString("0.0"), Std = "GB/T 894.1", Remark = "" });
rows.Add(new QueryRow { Item = "沟槽宽度 m", Value = hit.SlotW.ToString("0.0"), Std = "GB/T 894.1", Remark = "" });
rows.Add(new QueryRow { Item = "安装说明", Value = "挡圈装入沟槽后应贴合槽底", Std = "GB/T 894.1", Remark = DISCLAIMER });
return rows;
}
// ================= 统一入口 =================
public static List<QueryRow> Query(QueryKind kind, double d)
{
switch (kind)
{
case QueryKind.Keyway: return QueryKeyway(d);
case QueryKind.Circlip: return QueryCirclip(d);
default:
return new List<QueryRow>
{
new QueryRow { Item = "查询结果", Value = "模块开发中", Std = "GB/T 3452.1", Remark = "" }
};
}
}
}
}
+199
View File
@@ -0,0 +1,199 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
namespace DesignExpress.Core
{
public class FitItem
{
public string Role;
public string Code;
public double DiaMM;
public int Load;
public int Purpose;
public string Verdict;
public string Recommend;
public string Basis;
}
// 公差配合检查对话框
public class FitForm : Form
{
public List<FitItem> Items = new List<FitItem>();
private ComboBox cboRole, cboLoad, cboPurpose;
private TextBox txtDia, txtCode;
private Label lblResult;
private ListBox lst;
private Button btnCheck, btnDone;
private double _diaMM;
private string _faceName;
public FitForm(double diaMM, string faceName)
{
_diaMM = diaMM;
_faceName = faceName;
BuildUI();
}
private void BuildUI()
{
Text = "公差配合检查 - 设计标注检查工具";
FormBorderStyle = FormBorderStyle.FixedDialog;
MaximizeBox = false;
MinimizeBox = false;
StartPosition = FormStartPosition.CenterScreen;
ClientSize = new Size(640, 480);
int y = 12;
Label lblFace = new Label();
lblFace.Text = "配合面: " + _faceName + " 直径: " + _diaMM.ToString("0.0") + " mm";
lblFace.Location = new Point(12, y); lblFace.AutoSize = true; lblFace.Font = new Font(lblFace.Font, FontStyle.Bold);
Controls.Add(lblFace);
y += 28;
Label lblRole = new Label(); lblRole.Text = "角色:"; lblRole.Location = new Point(12, y + 3); lblRole.AutoSize = true;
Controls.Add(lblRole);
cboRole = new ComboBox();
cboRole.DropDownStyle = ComboBoxStyle.DropDownList;
cboRole.Items.AddRange(new object[] { "轴承-轴颈(旋转)", "轴承-轴颈(固定)", "轴承-座孔(外圈固定)", "轴承-座孔(外圈旋转)", "一般孔轴配合" });
cboRole.SelectedIndex = 0;
cboRole.Location = new Point(90, y); cboRole.Width = 220;
Controls.Add(cboRole);
cboRole.SelectedIndexChanged += delegate { RoleChanged(); };
y += 30;
Label lblLoad = new Label(); lblLoad.Text = "载荷:"; lblLoad.Location = new Point(12, y + 3); lblLoad.AutoSize = true;
Controls.Add(lblLoad);
cboLoad = new ComboBox();
cboLoad.DropDownStyle = ComboBoxStyle.DropDownList;
cboLoad.Items.AddRange(new object[] { "轻载 (P<=0.07C)", "中载 (0.07C<P<=0.15C)", "重载 (P>0.15C)", "特重/冲击载荷" });
cboLoad.SelectedIndex = 1;
cboLoad.Location = new Point(90, y); cboLoad.Width = 220;
Controls.Add(cboLoad);
y += 30;
Label lblPurpose = new Label(); lblPurpose.Text = "用途:"; lblPurpose.Location = new Point(12, y + 3); lblPurpose.AutoSize = true;
Controls.Add(lblPurpose);
cboPurpose = new ComboBox();
cboPurpose.DropDownStyle = ComboBoxStyle.DropDownList;
cboPurpose.Items.AddRange(new object[] { "滑动(有相对运动)", "定位(固定位置, 可拆)", "传动(传递扭矩)", "过盈连接(不拆卸)" });
cboPurpose.SelectedIndex = 1;
cboPurpose.Location = new Point(90, y); cboPurpose.Width = 220;
Controls.Add(cboPurpose);
y += 30;
Label lblDia = new Label(); lblDia.Text = "直径(mm):"; lblDia.Location = new Point(12, y + 3); lblDia.AutoSize = true;
Controls.Add(lblDia);
txtDia = new TextBox(); txtDia.Text = _diaMM.ToString("0.0"); txtDia.Location = new Point(90, y); txtDia.Width = 100;
Controls.Add(txtDia);
y += 30;
Label lblCode = new Label(); lblCode.Text = "配合代码:"; lblCode.Location = new Point(12, y + 3); lblCode.AutoSize = true;
Controls.Add(lblCode);
txtCode = new TextBox(); txtCode.Text = "k6"; txtCode.Location = new Point(90, y); txtCode.Width = 140;
Controls.Add(txtCode);
Label lblCodeHint = new Label();
lblCodeHint.Text = "轴承轴颈: 如 k6/js6/m6; 座孔: 如 H7/J7/K7; 一般配合: 如 H7/k6";
lblCodeHint.Location = new Point(240, y + 3); lblCodeHint.AutoSize = true;
Controls.Add(lblCodeHint);
y += 34;
btnCheck = new Button(); btnCheck.Text = "检查并加入列表"; btnCheck.Location = new Point(12, y); btnCheck.Width = 130;
btnCheck.Click += delegate { DoCheck(); };
Controls.Add(btnCheck);
btnDone = new Button(); btnDone.Text = "完成 (生成报告)"; btnDone.Location = new Point(150, y); btnDone.Width = 130;
btnDone.Click += delegate { DialogResult = DialogResult.OK; Close(); };
Controls.Add(btnDone);
y += 36;
lblResult = new Label();
lblResult.Text = "";
lblResult.Location = new Point(12, y); lblResult.AutoSize = true; lblResult.MaximumSize = new Size(610, 0);
Controls.Add(lblResult);
y += 60;
lst = new ListBox();
lst.Location = new Point(12, y); lst.Width = 616; lst.Height = 140;
Controls.Add(lst);
RoleChanged();
}
private void RoleChanged()
{
bool bearing = cboRole.SelectedIndex >= 0 && cboRole.SelectedIndex <= 3;
cboLoad.Enabled = bearing;
cboPurpose.Enabled = !bearing;
txtCode.Text = bearing ? "k6" : "H7/k6";
}
private void DoCheck()
{
double dia;
double.TryParse(txtDia.Text, out dia);
if (dia <= 0) { MessageBox.Show("请输入有效直径"); return; }
string code = txtCode.Text.Trim();
if (code == "") { MessageBox.Show("请输入配合代码"); return; }
string verdict, recommend, basis;
int load = cboLoad.SelectedIndex + 1;
int purpose = cboPurpose.SelectedIndex + 1;
if (cboRole.SelectedIndex == 4)
verdict = KB.GeneralFitJudge(code, purpose, out recommend, out basis);
else
verdict = KB.FitJudge(code, (string)cboRole.SelectedItem, load, out recommend, out basis);
FitItem it = new FitItem();
it.Role = (string)cboRole.SelectedItem;
it.Code = code;
it.DiaMM = dia;
it.Load = load;
it.Purpose = purpose;
it.Verdict = verdict;
it.Recommend = recommend;
it.Basis = basis;
Items.Add(it);
lblResult.Text = "判定: " + verdict + " 推荐: " + recommend + "\r\n" + basis;
lblResult.ForeColor = verdict == "合理" ? Color.Green : (verdict == "可接受" ? Color.DarkOrange : Color.Red);
lst.Items.Add(dia.ToString("0.0") + "mm " + it.Role + " 代码 " + code + " -> " + verdict + " (推荐 " + recommend + ")");
txtCode.Clear();
}
}
// 通用单行输入对话框
public class SimpleInput : Form
{
private TextBox txt;
public string Value { get { return txt.Text; } }
public SimpleInput(string title, string prompt, string def)
{
Text = title;
FormBorderStyle = FormBorderStyle.FixedDialog;
MaximizeBox = false; MinimizeBox = false;
StartPosition = FormStartPosition.CenterScreen;
ClientSize = new Size(420, 110);
Label lbl = new Label();
lbl.Text = prompt;
lbl.Location = new Point(12, 12); lbl.AutoSize = true; lbl.MaximumSize = new Size(390, 40);
Controls.Add(lbl);
txt = new TextBox();
txt.Text = def;
txt.Location = new Point(12, 50); txt.Width = 396;
Controls.Add(txt);
Button ok = new Button(); ok.Text = "确定"; ok.DialogResult = DialogResult.OK;
ok.Location = new Point(220, 78); ok.Width = 90;
Controls.Add(ok);
Button cancel = new Button(); cancel.Text = "取消"; cancel.DialogResult = DialogResult.Cancel;
cancel.Location = new Point(318, 78); cancel.Width = 90;
Controls.Add(cancel);
}
}
}
+17
View File
@@ -0,0 +1,17 @@
using System;
namespace DesignExpress.Core
{
// CAD 宿主抽象接口: Core 不依赖任何具体 CAD 系统,
// 由各 CAD 的 Link 适配器(Inventor/Creo/SolidWorks)实现,
// 架构类似 COMSOL LiveLink
public interface ICadHost
{
string AppName { get; }
object GetActiveDocument();
string GetDocType(object doc);
DocSnapshot Capture(object doc);
double PickCylinderDiameterMM(out string faceName);
void SetStatusText(string text);
}
}
+235
View File
@@ -0,0 +1,235 @@
using System;
namespace DesignExpress.Core
{
// 知识库: 机械设计手册常用数据(简化参考版)
// 正式生产请以最新版《机械设计手册》及 GB/T 275, GB/T 1800, GB/T 1801 等标准为准
public static class KB
{
public static int SegIndex(double dMM)
{
if (dMM <= 18) return 1;
if (dMM <= 30) return 2;
if (dMM <= 50) return 3;
if (dMM <= 80) return 4;
if (dMM <= 120) return 5;
if (dMM <= 180) return 6;
if (dMM <= 250) return 7;
if (dMM <= 315) return 8;
if (dMM <= 400) return 9;
if (dMM <= 500) return 10;
return -1;
}
public static string SegName(int seg)
{
switch (seg)
{
case 1: return "<=18";
case 2: return "18~30";
case 3: return "30~50";
case 4: return "50~80";
case 5: return "80~120";
case 6: return "120~180";
case 7: return "180~250";
case 8: return "250~315";
case 9: return "315~400";
case 10: return "400~500";
default: return "?";
}
}
private static readonly double[,] IT = new double[11, 10]
{
{ 0.8, 1, 1.2, 1.5, 2, 2.5, 3.5, 4, 4.5, 5 },
{ 1.2, 1.5, 2, 2.5, 3, 4, 4.5, 5, 6, 7 },
{ 2, 2.5, 3, 4, 5, 6, 7, 8, 9, 10 },
{ 3, 4, 5, 6, 8, 10, 12, 13, 15, 16 },
{ 4, 5, 7, 8, 10, 12, 14, 16, 18, 20 },
{ 6, 8, 10, 12, 15, 18, 20, 23, 25, 27 },
{ 10, 12, 16, 19, 22, 25, 28, 32, 36, 40 },
{ 14, 18, 22, 26, 30, 35, 40, 46, 52, 57 },
{ 25, 30, 36, 43, 52, 62, 72, 81, 89, 97 },
{ 40, 48, 58, 70, 84, 100, 115, 130, 140, 155 },
{ 60, 75, 90, 105, 130, 150, 175, 195, 210, 230 }
};
public static double ItTol(int itNo, int seg)
{
if (itNo >= 1 && itNo <= 11 && seg >= 1 && seg <= 10) return IT[itNo - 1, seg - 1];
return -1;
}
private static string LoadName(int load)
{
switch (load)
{
case 1: return "轻载(P<=0.07C)";
case 2: return "中载(0.07C<P<=0.15C)";
case 3: return "重载(P>0.15C)";
default: return "特重/冲击载荷";
}
}
public static void ParseFit(string code, out string hole, out string shaft)
{
hole = ""; shaft = "";
string c = (code ?? "").Trim().ToUpper();
int p = c.IndexOf('/');
if (p > 0)
{
hole = c.Substring(0, p).Trim();
shaft = c.Substring(p + 1).Trim();
}
else hole = c;
}
public static string FitJudge(string code, string role, int load, out string recommend, out string basis)
{
recommend = "";
string c = (code ?? "").Trim().ToUpper();
string rec = "", acc = "";
basis = "机械设计手册 第2卷 / GB/T 275-2015 滚动轴承配合";
switch (role)
{
case "轴承-轴颈(旋转)":
switch (load)
{
case 1: rec = "js6"; acc = "k6"; break;
case 2: rec = "k6"; acc = "m6"; break;
case 3: rec = "m6"; acc = "n6"; break;
default: rec = "n6"; acc = "p6,r6"; break;
}
basis += ": 内圈与旋转轴, " + LoadName(load) + ", 推荐 " + rec + " (可选 " + acc + ")";
break;
case "轴承-轴颈(固定)":
rec = "h6"; acc = "g6,j6";
basis += ": 内圈与不旋转轴, 推荐 " + rec + " (可选 " + acc + ")";
break;
case "轴承-座孔(外圈固定)":
rec = "H7"; acc = "J7,H8";
basis += ": 外圈与壳体孔(固定载荷), 推荐 " + rec + " (可选 " + acc + ")";
break;
case "轴承-座孔(外圈旋转)":
rec = "K7"; acc = "M7,N7";
basis += ": 外圈与壳体孔(旋转载荷), 推荐 " + rec + " (可选 " + acc + ")";
break;
default:
basis = "未知角色";
return "信息";
}
recommend = rec;
if (c == "") return "信息";
if (c == rec) return "合理";
if (("," + acc + ",").Contains("," + c + ",")) return "可接受";
return "不合理";
}
public static string GeneralFitJudge(string code, int purpose, out string recommend, out string basis)
{
string hole, shaft;
ParseFit(code, out hole, out shaft);
string c = (code ?? "").Trim().ToUpper();
string fitType, fitUse;
LookupFitTable(hole + "/" + shaft, out fitType, out fitUse);
string rec = "", acc = "", purposeName = "";
basis = "机械设计手册 第2卷 孔轴公差与配合 (GB/T 1801)";
switch (purpose)
{
case 1: purposeName = "滑动配合"; rec = "H8/f7"; acc = "H9/d9,H7/g6"; break;
case 2: purposeName = "定位配合"; rec = "H7/js6"; acc = "H7/h6,H7/k6"; break;
case 3: purposeName = "传动配合"; rec = "H7/n6"; acc = "H7/p6"; break;
default: purposeName = "过盈连接"; rec = "H7/s6"; acc = "H7/r6,H7/u6"; break;
}
recommend = rec;
basis += ": " + purposeName + ", 推荐 " + rec + " (可选 " + acc + ")";
if (fitType == "")
{
basis += "; 当前代码 " + c + " 不在常用配合表中, 请核对";
return "信息";
}
string needType = "间隙";
if (purpose == 2) needType = "间隙/过渡";
if (purpose == 3) needType = "过渡/过盈";
if (purpose == 4) needType = "过盈";
if (c == rec) return "合理";
if (("," + acc + ",").Contains("," + c + ",")) return "可接受";
if (needType.Contains(fitType))
{
basis += "; 当前 " + c + " 为" + fitType + "配合(" + fitUse + "), 类型方向正确但非优先推荐";
return "可接受";
}
basis += "; 当前 " + c + " 为" + fitType + "配合(" + fitUse + "), 与用途不符";
return "不合理";
}
public static void LookupFitTable(string code, out string fitType, out string fitUse)
{
fitType = ""; fitUse = "";
switch ((code ?? "").Trim().ToUpper())
{
case "H11/C11": fitType = "间隙"; fitUse = "低精度/粗糙滑动, 吊架等"; break;
case "H9/D9": fitType = "间隙"; fitUse = "较松滑动, 联轴器导向"; break;
case "H8/F7": fitType = "间隙"; fitUse = "一般滑动, 机床导轨低速"; break;
case "H7/G6": fitType = "间隙"; fitUse = "精密滑动, 导向件/移动齿轮"; break;
case "H7/H6": fitType = "间隙"; fitUse = "小间隙定位, 挂轮/联轴器"; break;
case "H7/JS6": fitType = "过渡"; fitUse = "定位, 手轮/隔套"; break;
case "H7/K6": fitType = "过渡"; fitUse = "精密定位, 隔套/滚动轴承内圈"; break;
case "H7/N6": fitType = "过渡"; fitUse = "较紧定位/传动, 齿轮与轴/连杆"; break;
case "H7/P6": fitType = "过盈"; fitUse = "小过盈, 定位兼传小扭矩"; break;
case "H7/R6": fitType = "过盈"; fitUse = "一般过盈, 受冲击装配"; break;
case "H7/S6": fitType = "过盈"; fitUse = "液压装配过盈"; break;
case "H7/U6": fitType = "过盈"; fitUse = "大过盈, 重型压装"; break;
}
}
public static string RaSuggest(string role)
{
if (role.StartsWith("轴承-轴颈")) return "0.4~0.8 um (磨削)";
if (role.StartsWith("轴承-座孔")) return "0.8~1.6 um (精镗/铰)";
if (role == "密封") return "动密封面 0.4~0.8; 静密封面 0.8~1.6 um";
if (role == "GMP") return "物料接触面 Ra<=0.8 (电解抛光 0.4)";
return "一般配合面 1.6~3.2 um";
}
public static string GmpMaterialJudge(string matName, out string rec, out string basis)
{
string m = (matName ?? "").Trim().ToUpper();
basis = "GMP 设备设计规范: 与物料接触部件应使用耐腐蚀、易清洁材料";
rec = "";
if (m == "") { rec = "应填写材料并选用卫生级不锈钢"; return "未通过"; }
if (m.Contains("316L") || m.Contains("904L") || m.Contains("2205")) { rec = m + " 符合 GMP 物料接触要求(首选)"; return "合理"; }
if (m.Contains("316")) { rec = m + " 符合 GMP 物料接触要求"; return "合理"; }
if (m.Contains("钛") || m.Contains("哈氏")) { rec = m + " 耐腐蚀, 符合 GMP 要求"; return "合理"; }
if (m.Contains("304") || m.Contains("不锈钢")) { rec = "304 级可接受, 接触面建议电解抛光; 长期接触含氯介质建议 316L"; return "可接受"; }
if (m.Contains("PTFE") || m.Contains("聚四氟") || m.Contains("PFA") || m.Contains("PVDF") ||
m.Contains("EPDM") || m.Contains("三元乙丙") || m.Contains("FKM") || m.Contains("氟胶") ||
m.Contains("硅胶") || m.Contains("PEEK") || m.Contains("聚丙烯") || m.Contains("玻璃"))
{ rec = m + " 可用于密封/非金属部件, 需确认符合 FDA/药典要求及温度等级"; return "可接受"; }
if (m.Contains("铝") || m == "AL") { rec = "铝合金不可直接接触物料, 需阳极氧化/隔离处理"; return "警告"; }
if (m.Contains("钢") || m.Contains("铁")) { rec = "碳钢/普通钢不可接触物料, 需改不锈钢或加涂层隔离"; return "未通过"; }
if (m.Contains("铜") || m.Contains("锌")) { rec = "铜/锌及其合金不宜直接接触药品物料"; return "未通过"; }
rec = "无法识别的材料, 请人工确认";
return "警告";
}
private static readonly string[] SealKeys = { "O型圈", "O形圈", "O-RING", "密封圈", "密封垫", "密封条", "密封件", "SEAL", "GASKET", "垫片", "EPDM", "三元乙丙", "氟胶", "硅胶圈" };
private static readonly string[] StdKeys = { "螺栓", "螺钉", "螺母", "垫圈", "垫片", "销", "键", "轴承", "电机", "减速机", "气缸", "阀", "泵", "传感器", "PLC", "变频器", "伺服", "型材", "钢管", "法兰", "接头", "卡箍", "视镜", "密封圈", "联轴器", "导向轴", "导轨", "弹簧", "内六角", "GB", "TB", "ISO", "DIN", "JB" };
public static bool IsSealPartName(string nm)
{
string u = (nm ?? "").ToUpper();
foreach (string k in SealKeys)
if (u.Contains(k.ToUpper())) return true;
return false;
}
public static bool IsStdPartName(string nm)
{
string u = (nm ?? "").ToUpper();
foreach (string k in StdKeys)
if (u.Contains(k.ToUpper())) return true;
return false;
}
}
}
+190
View File
@@ -0,0 +1,190 @@
using System;
using System.Drawing;
using System.Windows.Forms;
namespace DesignExpress.Core
{
// 检查主窗口: TableLayoutPanel 百分比布局, 左树 25% + 右表 75%, 无固定尺寸不遮罩
public class MainForm : Form
{
private DataGridView grid;
private TreeView tree;
private Label lblSummary;
private StatusStrip status;
private string mStage = "";
public MainForm()
{
BuildUI();
}
private void BuildUI()
{
Text = "Design Express Res - 设计标注检查";
StartPosition = FormStartPosition.CenterScreen;
WindowState = FormWindowState.Normal;
Rectangle wa = Screen.PrimaryScreen.WorkingArea;
Size = new Size(wa.Width / 2, wa.Height / 2);
MinimumSize = new Size(720, 460);
// 主布局: 两列百分比 25% / 75%
TableLayoutPanel master = new TableLayoutPanel();
master.Dock = DockStyle.Fill;
master.ColumnCount = 2;
master.RowCount = 1;
master.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 25F));
master.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 75F));
master.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
master.Margin = new Padding(0);
master.Padding = new Padding(0);
// 左: 检查项树
tree = new TreeView();
tree.Dock = DockStyle.Fill;
tree.FullRowSelect = true;
tree.ShowLines = true;
tree.ShowPlusMinus = true;
tree.HideSelection = false;
tree.BorderStyle = BorderStyle.FixedSingle;
FillTree();
master.Controls.Add(tree, 0, 0);
// 右: 汇总 + 结果表格
Panel right = new Panel();
right.Dock = DockStyle.Fill;
right.Margin = new Padding(0);
grid = new DataGridView();
grid.Dock = DockStyle.Fill;
grid.ReadOnly = true;
grid.AllowUserToAddRows = false;
grid.AllowUserToDeleteRows = false;
grid.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
grid.MultiSelect = false;
grid.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
grid.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize;
grid.BorderStyle = BorderStyle.None;
grid.Columns.Add("CheckID", "编号");
grid.Columns.Add("Name", "检查项");
grid.Columns.Add("Obj", "对象");
grid.Columns.Add("Result", "结果");
grid.Columns.Add("Note", "说明");
grid.Columns.Add("Basis", "判定依据");
grid.Columns["Note"].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
grid.Columns["Basis"].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
right.Controls.Add(grid);
lblSummary = new Label();
lblSummary.Dock = DockStyle.Top;
lblSummary.AutoSize = true;
lblSummary.TextAlign = ContentAlignment.MiddleLeft;
lblSummary.Padding = new Padding(8, 6, 0, 6);
lblSummary.Font = new Font("Microsoft YaHei", 9.5f);
lblSummary.Text = "就绪: 请先打开文档, 然后在功能区点击检查按钮";
right.Controls.Add(lblSummary);
master.Controls.Add(right, 1, 0);
// Dock 顺序: Fill 先 Add, Bottom 后 Add
Controls.Add(master);
status = new StatusStrip();
Controls.Add(status);
Shown += delegate { LoadCurrentResults(); };
}
private void FillTree()
{
tree.Nodes.Clear();
TreeNode root = new TreeNode("检查项清单");
TreeNode part = new TreeNode("零件阶段 (P)");
part.Nodes.Add("P01~P07 保存状态/代号/名称/材料/设计者/审核者/修订号");
part.Nodes.Add("P08 模型标注统计/公差覆盖/形位公差/粗糙度");
part.Nodes.Add("P09 公差配合检查 (GB/T 275/GB/T 1801)");
part.Nodes.Add("P10~P12 配合面形位公差/粗糙度/公差等级");
part.Nodes.Add("P13 螺纹孔检查");
part.Nodes.Add("P14 物料接触 + GMP 专项");
part.Nodes.Add("P15 焊接标注");
part.Nodes.Add("P16 表面处理/防锈");
part.Nodes.Add("P17 结构强度/仿真指引");
root.Nodes.Add(part);
TreeNode asm = new TreeNode("装配阶段 (A)");
asm.Nodes.Add("A01 文档保存状态");
asm.Nodes.Add("A02 GMP 材料合规");
asm.Nodes.Add("A03/A04 密封件存在性 + 密封设计提示");
asm.Nodes.Add("A05 清洁性设计要求");
asm.Nodes.Add("A06 GMP 粗糙度标注");
asm.Nodes.Add("A07 静态干涉检查");
asm.Nodes.Add("A08 运动干涉(动态)指引");
asm.Nodes.Add("A09 外购件优先统计");
asm.Nodes.Add("A10 电气选型/西门子联网");
asm.Nodes.Add("A11 施工/公共动力系统");
asm.Nodes.Add("A12 整机强度/仿真指引");
root.Nodes.Add(asm);
TreeNode drw = new TreeNode("工程图阶段 (D)");
drw.Nodes.Add("D00~D09 保存/图框/标题栏/空白/更新/剖视/越界/尺寸/公差/文本");
drw.Nodes.Add("D10~D17 形位公差/基准/粗糙度/焊接/孔标注/中心线/越界/图幅");
drw.Nodes.Add("D18 物料接触类别声明");
drw.Nodes.Add("D19 焊接技术要求(双面焊/打磨)");
drw.Nodes.Add("D20 死角/夹角要求(R10/135度)");
drw.Nodes.Add("D21~D25 公差/轴承位/螺纹孔/镀镍/平面度");
drw.Nodes.Add("D26~D32 角度管/治具/辅助件/外购/测试/装配/防锈");
drw.Nodes.Add("D33~D39 施工动力/配孔/吊顶/电气/西门子/强度/夹角");
root.Nodes.Add(drw);
tree.Nodes.Add(root);
root.ExpandAll();
}
public void LoadCurrentResults()
{
grid.Rows.Clear();
int pass = 0, warn = 0, fail = 0, info = 0;
foreach (CheckResult r in AppState.Results)
{
int idx = grid.Rows.Add(r.CheckID, r.Name, r.Obj, r.Result, r.Note, r.Basis);
DataGridViewRow row = grid.Rows[idx];
switch (r.Result)
{
case "通过": row.DefaultCellStyle.BackColor = System.Drawing.Color.FromArgb(0xE8, 0xFF, 0xE8); break;
case "警告": row.DefaultCellStyle.BackColor = System.Drawing.Color.FromArgb(0xFF, 0xFF, 0xE0); break;
case "未通过": row.DefaultCellStyle.BackColor = System.Drawing.Color.FromArgb(0xFF, 0xE0, 0xE0); break;
default: row.DefaultCellStyle.BackColor = System.Drawing.Color.FromArgb(0xF0, 0xF0, 0xF0); break;
}
switch (r.Result)
{
case "通过": pass++; break;
case "警告": warn++; break;
case "未通过": fail++; break;
default: info++; break;
}
}
lblSummary.Text = string.Format("检查项: {0} 通过: {1} 警告: {2} 未通过: {3} 信息: {4}",
AppState.Results.Count, pass, warn, fail, info);
status.Items.Clear();
status.Items.Add("当前阶段: " + (mStage == "" ? "全部" : mStage));
}
public void RunAndLoad(string stage, Action run)
{
try
{
Cursor = Cursors.WaitCursor;
AppState.Results.Clear();
mStage = stage;
run();
LoadCurrentResults();
}
catch (Exception ex)
{
MessageBox.Show("检查出错: " + ex.Message, "Design Express Res", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
Cursor = Cursors.Default;
}
}
}
}
+264
View File
@@ -0,0 +1,264 @@
using System;
using System.Drawing;
using System.Windows.Forms;
namespace DesignExpress.Core
{
// 设计数据查询窗口: 一个按钮对应一个功能页, 无切换菜单
// TableLayoutPanel 百分比布局: 头部 AutoSize(自适应文字高度), 左输入 35% / 右结果 65%
public class QueryForm : Form
{
private static QueryForm mForm;
private class ModuleDef
{
public string Title; // 窗口标题
public string SubTitle; // 页头主标题
public string Desc; // 功能说明
}
private static readonly ModuleDef[] Modules = new ModuleDef[]
{
new ModuleDef { Title = "卡簧槽查询 (GB/T 894)", SubTitle = "轴用/孔用弹性挡圈槽尺寸查询", Desc = "输入轴径或孔径, 查询挡圈型号、槽径、槽宽、槽深等设计尺寸" },
new ModuleDef { Title = "键槽查询 (GB/T 1095)", SubTitle = "平键键槽尺寸查询", Desc = "输入轴径, 查询键的公称尺寸(b×h×L)、轴槽深、轮毂槽深、键槽长度系列等" },
new ModuleDef { Title = "O 型密封圈查询 (GB/T 3452.1)", SubTitle = "O 型密封圈设计", Desc = "输入密封轴径/孔径, 查询圈规格、沟槽尺寸(径向/轴向密封)、压缩量、挡圈配合等" },
new ModuleDef { Title = "轴承挡圈查询", SubTitle = "轴承挡圈设计", Desc = "输入轴承外径/内径, 查询挡圈规格、挡边高度、安装槽尺寸、轴向定位设计等" }
};
private Label lblTitle;
private Label lblDesc;
private FlowLayoutPanel inFlow;
private DataGridView grid;
private Label outHint;
public QueryForm(int moduleIndex)
{
BuildUI(moduleIndex);
}
public static void ShowQuery(int moduleIndex)
{
if (moduleIndex < 0 || moduleIndex >= Modules.Length) return;
if (mForm == null || mForm.IsDisposed)
mForm = new QueryForm(moduleIndex);
mForm.ApplyModule(moduleIndex);
mForm.Show();
mForm.Activate();
}
private void BuildUI(int moduleIndex)
{
StartPosition = FormStartPosition.CenterScreen;
WindowState = FormWindowState.Normal;
Rectangle wa = Screen.PrimaryScreen.WorkingArea;
Size = new Size(wa.Width / 2, wa.Height / 2);
MinimumSize = new Size(700, 460);
// 主布局: 2 行(头部 AutoSize + 主体 100%), 主体 2 列(左 35% / 右 65%)
TableLayoutPanel master = new TableLayoutPanel();
master.Dock = DockStyle.Fill;
master.ColumnCount = 2;
master.RowCount = 2;
master.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 35F));
master.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 65F));
master.RowStyles.Add(new RowStyle(SizeType.AutoSize));
master.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
master.Margin = new Padding(0);
master.Padding = new Padding(0);
// 头部标题区: 占满两列, AutoSize 高度, 文字永远不会被裁剪
FlowLayoutPanel headFlow = new FlowLayoutPanel();
headFlow.AutoSize = true;
headFlow.Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right;
headFlow.FlowDirection = FlowDirection.TopDown;
headFlow.WrapContents = false;
headFlow.Padding = new Padding(10, 8, 0, 2);
headFlow.Margin = new Padding(0);
lblTitle = new Label();
lblTitle.Font = new Font("Microsoft YaHei", 12.5f, FontStyle.Bold);
lblTitle.AutoSize = true;
lblTitle.Margin = new Padding(0, 0, 0, 2);
headFlow.Controls.Add(lblTitle);
lblDesc = new Label();
lblDesc.Font = new Font("Microsoft YaHei", 9f);
lblDesc.ForeColor = System.Drawing.Color.Gray;
lblDesc.AutoSize = true;
lblDesc.Margin = new Padding(0);
headFlow.Controls.Add(lblDesc);
master.Controls.Add(headFlow, 0, 0);
master.SetColumnSpan(headFlow, 2);
// 左: 输入参数 (35%)
GroupBox input = new GroupBox();
input.Dock = DockStyle.Fill;
input.Text = "输入参数";
input.Font = new Font("Microsoft YaHei", 10f);
input.Margin = new Padding(6, 3, 3, 6);
inFlow = new FlowLayoutPanel();
inFlow.Dock = DockStyle.Fill;
inFlow.FlowDirection = FlowDirection.TopDown;
inFlow.WrapContents = false;
inFlow.Padding = new Padding(10, 8, 10, 8);
inFlow.AutoScroll = true;
input.Controls.Add(inFlow);
master.Controls.Add(input, 0, 1);
// 右: 查询结果 (65%)
GroupBox result = new GroupBox();
result.Dock = DockStyle.Fill;
result.Text = "查询结果";
result.Font = new Font("Microsoft YaHei", 10f);
result.Margin = new Padding(3, 3, 6, 6);
grid = new DataGridView();
grid.Dock = DockStyle.Fill;
grid.ReadOnly = true;
grid.AllowUserToAddRows = false;
grid.AllowUserToDeleteRows = false;
grid.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
grid.RowHeadersVisible = false;
grid.BackgroundColor = Color.White;
grid.BorderStyle = BorderStyle.None;
grid.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize;
grid.Columns.Add("Item", "项目");
grid.Columns.Add("Value", "数值");
grid.Columns.Add("Std", "标准");
grid.Columns.Add("Remark", "备注");
result.Controls.Add(grid);
outHint = new Label();
outHint.Dock = DockStyle.Top;
outHint.AutoSize = true;
outHint.TextAlign = ContentAlignment.MiddleLeft;
outHint.Padding = new Padding(6, 4, 0, 4);
outHint.ForeColor = System.Drawing.Color.Gray;
outHint.Font = new Font("Microsoft YaHei", 9.5f);
result.Controls.Add(outHint);
master.Controls.Add(result, 1, 1);
Controls.Add(master);
ApplyModule(moduleIndex);
}
// 切换到指定功能模块(无菜单, 直接换内容)
private void ApplyModule(int moduleIndex)
{
ModuleDef m = Modules[moduleIndex];
Text = "Design Express Res - " + m.Title;
lblTitle.Text = m.SubTitle;
lblDesc.Text = m.Desc;
BuildModuleUI((QueryKind)moduleIndex);
}
private void BuildModuleUI(QueryKind kind)
{
inFlow.Controls.Clear();
if (kind == QueryKind.Keyway || kind == QueryKind.Circlip)
{
Label hint = new Label();
hint.Text = "输入轴径, 点击查询获取设计尺寸";
hint.Font = new Font("Microsoft YaHei", 9.5f);
hint.ForeColor = System.Drawing.Color.Gray;
hint.AutoSize = true;
hint.Margin = new Padding(0, 0, 0, 6);
inFlow.Controls.Add(hint);
TextBox txtDia = new TextBox();
txtDia.Width = 110;
txtDia.Font = new Font("Microsoft YaHei", 10f);
txtDia.Text = "30";
Button btnQuery = new Button();
btnQuery.Text = "查询";
btnQuery.Width = 70;
btnQuery.Height = 26;
btnQuery.Font = new Font("Microsoft YaHei", 9.5f);
AddInputRow("轴径 d (mm):", txtDia, btnQuery);
Label note = new Label();
note.Text = "数据为常用参考值, 正式设计请核对最新版标准";
note.Font = new Font("Microsoft YaHei", 8.5f);
note.ForeColor = System.Drawing.Color.Gray;
note.AutoSize = true;
note.Margin = new Padding(0, 10, 0, 0);
inFlow.Controls.Add(note);
btnQuery.Click += delegate { DoQuery(kind, txtDia.Text); };
txtDia.KeyDown += delegate(object s, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter) DoQuery(kind, txtDia.Text);
};
}
else
{
Label hint1 = new Label();
hint1.Text = "参数输入区(开发中)";
hint1.Font = new Font("Microsoft YaHei", 10f, FontStyle.Bold);
hint1.AutoSize = true;
hint1.Margin = new Padding(0, 0, 0, 6);
inFlow.Controls.Add(hint1);
foreach (string t in new string[] {
"后续在此放置:",
"直径/尺寸输入框",
"标准选择(系列/等级)",
"查询按钮" })
{
Label l = new Label();
l.Text = t;
l.AutoSize = true;
l.ForeColor = System.Drawing.Color.Gray;
l.Margin = new Padding(0, 0, 0, 2);
inFlow.Controls.Add(l);
}
}
outHint.Text = "请输入参数后点击查询";
}
private void AddInputRow(string labelText, TextBox txt, Button btn)
{
FlowLayoutPanel row = new FlowLayoutPanel();
row.AutoSize = true;
row.WrapContents = false;
row.FlowDirection = FlowDirection.LeftToRight;
row.Margin = new Padding(0, 2, 0, 2);
Label l = new Label();
l.Text = labelText;
l.AutoSize = true;
l.Font = new Font("Microsoft YaHei", 10f);
l.Margin = new Padding(0, 5, 6, 0);
row.Controls.Add(l);
if (txt != null) row.Controls.Add(txt);
if (btn != null) row.Controls.Add(btn);
inFlow.Controls.Add(row);
}
private void DoQuery(QueryKind kind, string diaText)
{
double d;
if (!double.TryParse(diaText.Trim(), out d) || d <= 0)
{
MessageBox.Show("请输入有效直径(mm)", "Design Express Res", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
return;
}
grid.Rows.Clear();
var rows = DesignQuery.Query(kind, d);
foreach (QueryRow r in rows)
{
grid.Rows.Add(r.Item, r.Value, r.Std, r.Remark);
}
outHint.Text = "查询完成: 共 " + rows.Count + " 项结果 (输入直径 d=" + d.ToString("0.0") + " mm)";
}
}
}
+251
View File
@@ -0,0 +1,251 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
namespace DesignExpress.Core
{
// 报告输出: Excel(COM 动态调用) 优先, CSV 兜底
public static class Report
{
public const string ToolVersion = "1.3.0";
public static string WriteExcel(string path)
{
try
{
Type xlType = Type.GetTypeFromProgID("Excel.Application");
if (xlType == null) return WriteCsv(path);
dynamic xl = Activator.CreateInstance(xlType);
try
{
xl.Visible = false;
xl.DisplayAlerts = false;
dynamic wb = xl.Workbooks.Add();
dynamic ws1 = wb.Worksheets[1];
ws1.Name = "汇总";
dynamic ws2 = wb.Worksheets.Add(Type.Missing, ws1);
ws2.Name = "明细";
dynamic ws3 = wb.Worksheets.Add(Type.Missing, ws2);
ws3.Name = "检查项清单";
ws1.Cells[1, 1] = "设计标注检查报告";
ws1.Cells[2, 1] = "检查时间: " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
ws1.Cells[3, 1] = "工具版本: " + ToolVersion;
int pass, warn, fail, info;
AppState.Count(out pass, out warn, out fail, out info);
ws1.Cells[5, 1] = "检查项总数"; ws1.Cells[5, 2] = AppState.Results.Count;
ws1.Cells[6, 1] = "通过"; ws1.Cells[6, 2] = pass;
ws1.Cells[7, 1] = "警告"; ws1.Cells[7, 2] = warn;
ws1.Cells[8, 1] = "未通过"; ws1.Cells[8, 2] = fail;
ws1.Cells[9, 1] = "信息"; ws1.Cells[9, 2] = info;
ws1.Cells[11, 1] = "按检查项统计:";
ws1.Cells[12, 1] = "编号"; ws1.Cells[12, 2] = "检查项";
ws1.Cells[12, 3] = "总数"; ws1.Cells[12, 4] = "通过";
ws1.Cells[12, 5] = "警告"; ws1.Cells[12, 6] = "未通过";
BuildCheckStat(ws1, 13);
ws1.Columns["A:F"].AutoFit();
ws2.Cells[1, 1] = "序号"; ws2.Cells[1, 2] = "阶段";
ws2.Cells[1, 3] = "检查对象"; ws2.Cells[1, 4] = "编号";
ws2.Cells[1, 5] = "检查项"; ws2.Cells[1, 6] = "结果";
ws2.Cells[1, 7] = "说明"; ws2.Cells[1, 8] = "判定依据";
int row = 2;
foreach (CheckResult r in AppState.Results)
{
ws2.Cells[row, 1] = row - 1;
ws2.Cells[row, 2] = r.Stage;
ws2.Cells[row, 3] = r.Obj;
ws2.Cells[row, 4] = r.CheckID;
ws2.Cells[row, 5] = r.Name;
ws2.Cells[row, 6] = r.Result;
ws2.Cells[row, 7] = r.Note;
ws2.Cells[row, 8] = r.Basis;
dynamic cell = ws2.Cells[row, 6];
if (r.Result == "通过") cell.Interior.Color = 0x00C8FFC8;
else if (r.Result == "警告") cell.Interior.Color = 0x00C8FFFF;
else if (r.Result == "未通过") cell.Interior.Color = 0x00C8C8FF;
else cell.Interior.Color = 0x00E1E1E1;
row++;
}
ws2.Columns["A:H"].AutoFit();
ws3.Cells[1, 1] = "编号"; ws3.Cells[1, 2] = "检查项";
ws3.Cells[1, 3] = "阶段"; ws3.Cells[1, 4] = "说明";
string[,] L = CheckList;
for (int i = 0; i < L.GetLength(0); i++)
{
ws3.Cells[i + 2, 1] = L[i, 0];
ws3.Cells[i + 2, 2] = L[i, 1];
ws3.Cells[i + 2, 3] = L[i, 2];
ws3.Cells[i + 2, 4] = L[i, 3];
}
ws3.Columns["A:D"].AutoFit();
string save = path.ToLower().EndsWith(".xlsx") ? path : path + ".xlsx";
wb.SaveAs(save, 51);
wb.Close(false);
xl.Quit();
return save;
}
finally
{
try { xl.Quit(); } catch { }
}
}
catch
{
return WriteCsv(path);
}
}
private static string[,] CheckList = BuildCheckList();
private static string[,] BuildCheckList()
{
string[,] L = new string[75, 4];
string[] rows = new string[]
{
"P01,文档保存状态,零件,是否有未保存修改",
"P02,零件代号,零件,标题栏必填",
"P03,名称,零件,标题栏必填",
"P04,材料,零件,必填且非默认",
"P05,设计者,零件,标题栏必填",
"P06,审核者,零件,标题栏必填",
"P07,修订号,零件,版本追溯",
"P08,模型标注统计,零件,PMI 三维标注完整性",
"P08b,尺寸公差覆盖,零件,配合尺寸必须带公差",
"P08c,形位公差标注,零件,跳动/同轴度/平面度等",
"P08d,粗糙度标注,零件,配合/密封面必须标注",
"P09,公差配合检查,零件,轴承配合按 GB/T 275, 一般配合按 GB/T 1801",
"P10,配合面形位公差,零件,轴承位跳动/同轴度 IT5~IT6",
"P11,配合面粗糙度,零件,轴颈 0.4~0.8, 座孔 0.8~1.6",
"P12,轴承位公差等级,零件,轴颈 IT5~IT6, 座孔 IT7",
"P13,螺纹孔检查,零件,螺纹特征及标注齐全",
"P14,物料接触检查,零件,直接/间接/非接触分类",
"P14b,GMP 接触材料,零件,直接接触须用 316L 级",
"P14c,GMP 接触面粗糙度,零件,Ra<=0.8",
"P14d,GMP 无死角检查,零件,圆角>=R10, 内夹角>=135度",
"P15,焊接标注检查,零件,双面焊/打磨要求",
"P16,表面处理/防锈,零件,镀镍/磨床/抛光/防锈包装",
"P17,结构强度/仿真,零件,材料指定+CAE 校核指引",
"A01,文档保存状态,装配,是否有未保存修改",
"A02,GMP 材料合规,装配,全部零件材料合规",
"A03,密封件存在性,装配,密封圈/O型圈/垫片识别",
"A04,密封设计提示,装配,GB/T 3452.3 密封槽/FDA",
"A05,清洁性设计要求,装配,清洁/CIP/死角/排空",
"A06,GMP 粗糙度标注,装配,接触面 Ra<=0.8",
"A07,静态干涉,装配,干涉分析",
"A08,运动干涉(动态),装配,驱动约束/运动仿真",
"A09,外购件优先,装配,标准/外购件比例",
"A10,电气选型/西门子联网,装配,西门子/PROFINET/防爆/IP等级",
"A11,施工/公共动力系统,装配,压缩空气/纯化水/蒸汽/排风/配孔/吊顶",
"A12,整机强度/仿真指引,装配,建议应力分析/有限元校核",
"D00,文档保存状态,工程图,是否有未保存修改",
"D01,图框/标题栏,工程图,每张图纸必须有图框",
"D02,标题栏属性,工程图,图号/名称/材料/设计/审核/修订号",
"D03,空白图纸,工程图,无视图无标注",
"D04,视图更新状态,工程图,出图前必须更新视图",
"D05,剖视图统计,工程图,剖面线/剖切位置标注",
"D06,视图越界,工程图,视图超出图框",
"D07,尺寸数量,工程图,信息项",
"D08,尺寸公差,工程图,配合/关键尺寸必须带公差",
"D09,尺寸文本异常,工程图,含?的尺寸(测量失败)",
"D10,形位公差,工程图,信息项",
"D11,基准符号,工程图,API 不支持自动读取",
"D12,表面粗糙度,工程图,信息项",
"D13,焊接符号,工程图,API 不支持自动读取, 由 D19 覆盖",
"D14,孔标注,工程图,API 不支持自动读取, 由 D23 覆盖",
"D15,中心线/件号,工程图,信息项",
"D16,标注越界,工程图,标注超出图框",
"D17,图幅信息,工程图,信息项",
"D18,物料接触类别声明,工程图,直接/间接接触标注",
"D19,焊接技术要求,工程图,双面焊/打磨/焊接标准",
"D20,死角/夹角要求,工程图,无死角/夹角>=135度",
"D21,公差要求完整性,工程图,未注公差说明",
"D22,轴承位公差标注,工程图,跳动/同轴/旋转精度",
"D23,螺纹孔标注,工程图,螺纹代号/深度/攻深",
"D24,表面处理/镀镍,工程图,镀镍后是否磨床/抛光",
"D25,平面度/平行度,工程图,关键面平面度要求",
"D26,特殊角度管加工,工程图,角度管/弯管要求",
"D27,装配治具与检验,工程图,治具/检具/检验要求",
"D28,加工辅助件要求,工程图,工艺/毛坯/余量",
"D29,外购件优先,工程图,标准件优先采购",
"D30,测试要求,工程图,试压/气密/出厂测试",
"D31,装配要求,工程图,安装/扭矩/间隙",
"D32,防锈要求,工程图,防锈/包装/海运",
"D33,施工/公共动力系统,工程图,压缩空气/纯化水/蒸汽/排风",
"D34,配孔技术要求,工程图,配孔/配钻/现场配",
"D35,吊顶配件要求,工程图,吊顶/吊架/吊装",
"D36,电气元件选型,工程图,防爆/IP等级/选型",
"D37,西门子联网器件,工程图,西门子/PROFINET/联网",
"D38,结构强度/仿真,工程图,强度校核/仿真说明",
"D39,夹角R10/135度确认,工程图,死角夹角要求确认"
};
for (int i = 0; i < rows.Length; i++)
{
string[] p = rows[i].Split(',');
for (int j = 0; j < 4; j++)
if (j < p.Length) L[i, j] = p[j];
}
return L;
}
private static void BuildCheckStat(dynamic ws, int startRow)
{
List<string> ids = new List<string>();
List<string> names = new List<string>();
foreach (CheckResult r in AppState.Results)
{
if (!ids.Contains(r.CheckID))
{
ids.Add(r.CheckID);
names.Add(r.Name);
}
}
int row = startRow;
for (int i = 0; i < ids.Count; i++)
{
int nT = 0, nP = 0, nW = 0, nF = 0;
foreach (CheckResult r in AppState.Results)
{
if (r.CheckID == ids[i])
{
nT++;
if (r.Result == "通过") nP++;
else if (r.Result == "警告") nW++;
else if (r.Result == "未通过") nF++;
}
}
ws.Cells[row, 1] = ids[i];
ws.Cells[row, 2] = names[i];
ws.Cells[row, 3] = nT;
ws.Cells[row, 4] = nP;
ws.Cells[row, 5] = nW;
ws.Cells[row, 6] = nF;
row++;
}
}
public static string WriteCsv(string path)
{
string save = path.ToLower().EndsWith(".csv") ? path : path + ".csv";
StringBuilder sb = new StringBuilder();
sb.AppendLine("序号,阶段,检查对象,编号,检查项,结果,说明,判定依据");
int i = 1;
foreach (CheckResult r in AppState.Results)
{
sb.Append(i).Append(',').Append(r.Stage).Append(',').Append(r.Obj).Append(',')
.Append(r.CheckID).Append(',').Append(r.Name).Append(',').Append(r.Result).Append(',')
.Append("\"").Append(r.Note.Replace("\"", "\"\"")).Append("\",\"")
.Append(r.Basis.Replace("\"", "\"\"")).AppendLine("\"");
i++;
}
System.IO.File.WriteAllText(save, sb.ToString(), new UTF8Encoding(true));
return save;
}
}
}
+90
View File
@@ -0,0 +1,90 @@
using System;
using System.Collections.Generic;
namespace DesignExpress.Core
{
// 文档快照: Link 层把 CAD 文档状态拍成纯数据, Core 只处理快照, 与 CAD 完全解耦
public class DocSnapshot
{
public string Kind = ""; // Part / Assembly / Drawing
public bool Dirty;
public string DisplayName = "";
public string Folder = "";
public Dictionary<string, string> Props = new Dictionary<string, string>();
public string MaterialName = "";
public ModelInfo Model; // 零件/装配的三维标注
public AssemblyInfo Asm; // 装配
public List<SheetInfo> Sheets = new List<SheetInfo>(); // 工程图
public List<DocInfo> OpenDocs = new List<DocInfo>(); // 会话中已打开的文档
}
public class DocInfo
{
public string Name = "";
public Dictionary<string, string> Props = new Dictionary<string, string>();
public string Material = "";
public int TexCount = 0;
}
public class ModelInfo
{
public int DimCount = -1;
public int FcfCount = -1;
public int TexCount = -1;
public int DatumCount = -1;
public int ThreadNoteCount = -1;
public int TolCount = 0;
public bool TolReadable = true;
public string FirstTexText = "";
public int ThreadFeatureCount = -1;
public string ThreadDesignation = "";
public bool MinFilletReadable = false;
public double MinFilletRadiusMM = -1;
}
public class AssemblyInfo
{
public List<string> OccurrenceNames = new List<string>();
public List<InterferencePair> Interferences = new List<InterferencePair>();
public int InterferenceState = 0; // 0=无干涉 1=发现干涉 2=检查失败
public string InterferenceError = "";
public int TopLevelCount = 0;
}
public class InterferencePair
{
public string Name1 = "";
public string Name2 = "";
public double VolumeCM3 = 0;
}
public class RectD
{
public double L, T, R, B;
}
public class SheetInfo
{
public string Name = "";
public bool HasBorder = false;
public bool HasTitleBlock = false;
public string TitleBlockName = "";
public double SheetWidth = 0, SheetHeight = 0;
public RectD BorderRect; // null 表示无图框
public int ViewCount = 0;
public int UpdateNeeded = 0;
public int UpdateError = 0;
public int SectionCount = 0;
public List<RectD> ViewRects = new List<RectD>();
public int DimCount = 0;
public int DimTolCount = 0;
public int DimQuestionCount = 0;
public int FcfCount = 0;
public int TexCount = 0;
public int CenterlineCount = 0;
public int BalloonCount = 0;
public List<string> NotesTexts = new List<string>();
public List<RectD> FcfRects = new List<RectD>();
public List<RectD> TexRects = new List<RectD>();
}
}
+236
View File
@@ -0,0 +1,236 @@
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Runtime.InteropServices;
namespace DesignExpress.Core
{
// 功能按钮图标类型
public enum ToolIcon
{
Part, // 零件-齿轮
Asm, // 装配-方块
Drw, // 工程图-图纸
Export, // 导出-报告
Help, // 说明-问号
Circlip, // 卡簧槽-开口卡簧
Keyway, // 键槽-轴槽
Oring, // O型圈-圆环
Bearing // 轴承挡圈-轴承
}
// 共享图标生成器: 仓鼠品牌形象 + 功能符号, 供 Inventor(.ico) 和 SolidWorks(图标条BMP) 复用
public static class ToolIcons
{
private static readonly Color[] BgColors = new Color[]
{
Color.FromArgb(0x2E, 0x75, 0xB6), // Part 蓝
Color.FromArgb(0x33, 0x8A, 0x4E), // Asm 绿
Color.FromArgb(0xE6, 0x8A, 0x00), // Drw 橙
Color.FromArgb(0x7B, 0x4F, 0xB0), // Export 紫
Color.FromArgb(0x5F, 0x5F, 0x5F), // Help 灰
Color.FromArgb(0x1F, 0x8A, 0x8A), // Circlip 青
Color.FromArgb(0x8A, 0x5A, 0x2E), // Keyway 棕
Color.FromArgb(0xC0, 0x3A, 0x2B), // Oring 红
Color.FromArgb(0xD2, 0x5E, 0x8A) // Bearing 粉
};
public static Color IconColor(ToolIcon icon)
{
return BgColors[(int)icon];
}
// 绘制单个图标到指定位置: 中央大功能符号 + 底部小仓鼠品牌点缀
private static void DrawIcon(Graphics g, int ox, int oy, int size, ToolIcon icon, Color bg)
{
Color fur = Color.FromArgb(0xE8, 0xC0, 0x7D);
Color ear = Color.FromArgb(0xC8, 0x8A, 0x4F);
Color dark = Color.FromArgb(0x3A, 0x24, 0x10);
Color pink = Color.FromArgb(0xF2, 0x9E, 0xB6);
using (GraphicsPath path = RoundedRect(ox + 1, oy + 1, size - 2, size - 2, 6))
using (SolidBrush bgb = new SolidBrush(bg))
{
g.FillPath(bgb, path);
}
using (SolidBrush bEar = new SolidBrush(ear))
using (SolidBrush bFur = new SolidBrush(fur))
using (SolidBrush bDark = new SolidBrush(dark))
using (SolidBrush bPink = new SolidBrush(pink))
using (SolidBrush bWhite = new SolidBrush(Color.White))
using (Pen pWhite = new Pen(Color.White, 2f))
{
// 中央功能符号(大, 醒目)
int cx = ox + size / 2;
int cy = oy + size / 2 - 2;
switch (icon)
{
case ToolIcon.Part: // 齿轮
g.FillEllipse(bWhite, cx - 4, cy - 4, 8, 8);
g.FillRectangle(bWhite, cx - 6.5f, cy - 1.5f, 13, 3);
g.FillRectangle(bWhite, cx - 1.5f, cy - 6.5f, 3, 13);
g.FillRectangle(bWhite, cx - 5.5f, cy - 5.5f, 3, 3);
g.FillRectangle(bWhite, cx + 2.5f, cy - 5.5f, 3, 3);
g.FillRectangle(bWhite, cx - 5.5f, cy + 2.5f, 3, 3);
g.FillRectangle(bWhite, cx + 2.5f, cy + 2.5f, 3, 3);
break;
case ToolIcon.Asm: // 三个方块(品字)
g.FillRectangle(bWhite, cx - 8, cy - 5, 6, 6);
g.FillRectangle(bWhite, cx + 2, cy - 5, 6, 6);
g.FillRectangle(bWhite, cx - 3, cy + 2, 6, 6);
break;
case ToolIcon.Drw: // 图纸
g.DrawRectangle(pWhite, cx - 6, cy - 6, 12, 12);
g.DrawLine(pWhite, cx - 6, cy - 2, cx + 0, cy - 2);
g.DrawLine(pWhite, cx - 6, cy + 2, cx + 2, cy + 2);
g.DrawLine(pWhite, cx - 2, cy + 6, cx + 3, cy + 1);
break;
case ToolIcon.Export: // 报告表格
g.FillRectangle(bWhite, cx - 6, cy - 6, 12, 12);
using (SolidBrush bBg = new SolidBrush(bg))
{
g.FillRectangle(bBg, cx - 4, cy - 3, 8, 2);
g.FillRectangle(bBg, cx - 4, cy + 0, 8, 2);
g.FillRectangle(bBg, cx - 4, cy + 3, 5, 2);
}
break;
case ToolIcon.Help: // 问号
using (Font f = new Font("Arial", 13f, FontStyle.Bold, GraphicsUnit.Pixel))
{
g.DrawString("?", f, bWhite, cx - 4, cy - 9);
}
break;
case ToolIcon.Circlip: // 开口卡簧
g.DrawArc(pWhite, cx - 6, cy - 5.5f, 12, 11, 40, 280);
break;
case ToolIcon.Keyway: // 轴键槽
g.DrawEllipse(pWhite, cx - 6.5f, cy - 5, 13, 10);
g.FillRectangle(bWhite, cx - 3, cy - 5, 6, 5);
break;
case ToolIcon.Oring: // O型圈
g.DrawEllipse(pWhite, cx - 6.5f, cy - 5, 13, 10);
g.DrawEllipse(pWhite, cx - 3.5f, cy - 2, 7, 4);
break;
case ToolIcon.Bearing: // 轴承
g.DrawEllipse(pWhite, cx - 6.5f, cy - 5, 13, 10);
g.FillEllipse(bWhite, cx - 1.5f, cy - 0.5f, 3, 3);
g.FillEllipse(bWhite, cx - 5f, cy - 4f, 2, 2);
g.FillEllipse(bWhite, cx + 3f, cy - 4f, 2, 2);
g.FillEllipse(bWhite, cx - 5f, cy + 3f, 2, 2);
g.FillEllipse(bWhite, cx + 3f, cy + 3f, 2, 2);
break;
}
}
}
// 直接绘制 Bitmap 并转 IPictureDisp(Inventor 按钮图标)
public static object CreatePictureDisp(ToolIcon icon, int size)
{
try
{
using (Bitmap bmp = new Bitmap(size, size))
{
using (Graphics g = Graphics.FromImage(bmp))
{
g.SmoothingMode = SmoothingMode.AntiAlias;
DrawIcon(g, 0, 0, size, icon, BgColors[(int)icon]);
}
IntPtr hbm = bmp.GetHbitmap();
PICTDESC pd;
pd.cbSizeofstruct = Marshal.SizeOf(typeof(PICTDESC));
pd.picType = 1; // CT_BITMAP
pd.hbitmap = hbm;
pd.hpal = IntPtr.Zero;
Guid iid = new Guid("7BF80981-BF32-101A-8BBB-00AA00300CAB"); // stdole.IPictureDisp 正确 IID(注意是81结尾)
object ppv;
int hr = OleCreatePictureIndirect(ref pd, ref iid, true, out ppv);
if (hr != 0 || ppv == null) return Type.Missing;
return ppv;
}
}
catch { return Type.Missing; }
}
[StructLayout(LayoutKind.Sequential)]
private struct PICTDESC
{
public int cbSizeofstruct;
public int picType;
public IntPtr hbitmap;
public IntPtr hpal;
}
[DllImport("oleaut32.dll")]
private static extern int OleCreatePictureIndirect(ref PICTDESC pDesc, ref Guid riid, bool fOwn, out object ppvObj);
[DllImport("user32.dll")]
private static extern bool DeleteObject(IntPtr hObject);
// 生成单个 .ico 文件
public static string CreateIco(ToolIcon icon, int size, string file)
{
try
{
using (Bitmap bmp = new Bitmap(size, size))
{
using (Graphics g = Graphics.FromImage(bmp))
{
g.SmoothingMode = SmoothingMode.AntiAlias;
g.Clear(Color.FromArgb(0xFF, 0x00, 0xFF));
DrawIcon(g, 0, 0, size, icon, BgColors[(int)icon]);
}
Icon ic = Icon.FromHandle(bmp.GetHicon());
try
{
using (FileStream fs = new FileStream(file, FileMode.Create))
ic.Save(fs);
}
finally
{
DestroyIcon(ic.Handle);
}
}
return file;
}
catch { return ""; }
}
// 生成 SolidWorks 图标条 BMP(每格 size, 横向拼接)
public static string CreateStripBmp(ToolIcon[] icons, int size, string file)
{
try
{
int n = icons.Length;
using (Bitmap bmp = new Bitmap(size * n, size))
{
using (Graphics g = Graphics.FromImage(bmp))
{
g.SmoothingMode = SmoothingMode.AntiAlias;
g.Clear(Color.FromArgb(0xFF, 0x00, 0xFF));
for (int i = 0; i < n; i++)
DrawIcon(g, i * size, 0, size, icons[i], BgColors[(int)icons[i]]);
}
bmp.Save(file, System.Drawing.Imaging.ImageFormat.Bmp);
}
return file;
}
catch { return ""; }
}
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern bool DestroyIcon(IntPtr hIcon);
private static GraphicsPath RoundedRect(int x, int y, int w, int h, int r)
{
GraphicsPath p = new GraphicsPath();
p.AddArc(x, y, r, r, 180, 90);
p.AddArc(x + w - r, y, r, r, 270, 90);
p.AddArc(x + w - r, y + h - r, r, r, 0, 90);
p.AddArc(x, y + h - r, r, r, 90, 90);
p.CloseFigure();
return p;
}
}
}
+211
View File
@@ -0,0 +1,211 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Inventor;
using DesignExpress.Core;
[assembly: ComVisible(true)]
namespace DesignExpress.InventorLink
{
[Guid("D422E0BF-02D0-4DAE-958A-7BBA80B718E9")]
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[ProgId("DesignExpress.InventorLink.AddInServer")]
public class AddInServer : ApplicationAddInServer
{
public const string TabId = "InvDesignCheck_Tab";
public const string PanelId = "InvDesignCheck_Panel";
public const string PanelQueryId = "InvDesignCheck_PanelQuery";
private Application mApp;
private string mClientId;
private static Dictionary<string, Inventor.ButtonDefinition> mDefs = new Dictionary<string, Inventor.ButtonDefinition>();
public void Activate(ApplicationAddInSite addInSiteObject, bool firstTime)
{
try { mApp = (Application)addInSiteObject.Application; } catch { }
try { mClientId = addInSiteObject.Parent.ClientId; } catch { }
if (mApp == null) return;
AppState.Host = new InventorHost(mApp);
try { CreateRibbon(); } catch { }
}
private void CreateRibbon()
{
Inventor.UserInterfaceManager uim = mApp.UserInterfaceManager;
string[] ribNames = { "Part", "Assembly", "Drawing", "Presentation", "ZeroDoc" };
foreach (string n in ribNames)
{
try
{
Inventor.Ribbon ribbon = uim.Ribbons[n];
AddTabToRibbon(ribbon);
}
catch { }
}
}
private void AddTabToRibbon(Inventor.Ribbon ribbon)
{
Inventor.RibbonTab tab = null;
try { tab = ribbon.RibbonTabs[TabId]; } catch { }
if (tab == null)
tab = ribbon.RibbonTabs.Add("Design Express Res", TabId, "", "", false, false);
Inventor.RibbonPanel panel = null;
try { panel = tab.RibbonPanels[PanelId]; } catch { }
if (panel == null)
panel = tab.RibbonPanels.Add("设计检查", PanelId, "", "", false);
if (panel.CommandControls.Count == 0)
{
panel.CommandControls.AddButton(GetDef("零件检查", "InvDesignCheck_CmdPart",
"对当前零件进行三维标注、公差配合、GMP 检查",
"零件检查:标注统计、公差配合(GB/T 275)、形位公差、粗糙度、螺纹孔、物料接触 GMP、表面处理",
Checks.PartCheck));
panel.CommandControls.AddButton(GetDef("装配检查", "InvDesignCheck_CmdAsm",
"对当前装配进行 GMP、密封、干涉、外购件检查",
"装配检查:GMP 材料合规、密封件、清洁性、粗糙度、静态干涉、外购件、电气/西门子、动力系统",
Checks.AssemblyCheck));
panel.CommandControls.AddButton(GetDef("工程图检查", "InvDesignCheck_CmdDrw",
"对当前工程图进行标注与 spec 技术要求检查",
"工程图检查:图框、标题栏、视图更新、尺寸公差、标注统计、spec 技术要求关键词齐全性",
Checks.DrawingCheck));
panel.CommandControls.AddButton(GetDef("导出报告", "InvDesignCheck_CmdExport",
"导出检查报告(Excel/CSV)",
"导出检查报告(Excel/CSV)",
Checks.ExportReport));
panel.CommandControls.AddButton(GetDef("使用说明", "InvDesignCheck_CmdHelp",
"查看检查项清单与使用说明",
"查看检查项清单与使用说明",
Checks.Help));
}
Inventor.RibbonPanel panelQuery = null;
try { panelQuery = tab.RibbonPanels[PanelQueryId]; } catch { }
if (panelQuery == null)
panelQuery = tab.RibbonPanels.Add("设计数据查询", PanelQueryId, "", "", false);
if (panelQuery.CommandControls.Count == 0)
{
panelQuery.CommandControls.AddButton(GetDef("卡簧槽查询", "InvDesignCheck_CmdCirclip",
"轴用/孔用弹性挡圈槽尺寸查询 (GB/T 894)",
"卡簧槽: 输入轴径/孔径, 查询挡圈型号、槽径、槽宽、槽深",
Checks.CirclipQuery));
panelQuery.CommandControls.AddButton(GetDef("键槽查询", "InvDesignCheck_CmdKeyway",
"平键键槽尺寸查询 (GB/T 1095)",
"键槽: 输入轴径, 查询键尺寸 b×h、轴槽深、轮毂槽深",
Checks.KeywayQuery));
panelQuery.CommandControls.AddButton(GetDef("O型密封圈查询", "InvDesignCheck_CmdOring",
"O 型密封圈设计与沟槽尺寸查询 (GB/T 3452.1)",
"O型密封圈: 查询圈规格、沟槽尺寸、压缩量",
Checks.OringQuery));
panelQuery.CommandControls.AddButton(GetDef("轴承挡圈查询", "InvDesignCheck_CmdBRing",
"轴承挡圈与挡边设计查询",
"轴承挡圈: 查询挡圈规格、挡边高度、安装槽尺寸",
Checks.BearingRingQuery));
}
}
private Inventor.ButtonDefinition GetDef(string display, string internalName,
string desc, string tip, Action action)
{
Inventor.ButtonDefinition def;
lock (mDefs)
{
if (mDefs.TryGetValue(internalName, out def)) return def;
// Inventor 图标需要 IPictureDisp(直接内存绘制, 不经过文件)
object stdIcon = Type.Missing, largeIcon = Type.Missing;
try
{
ToolIcon ic = IconEnum(IconName(internalName));
stdIcon = ToolIcons.CreatePictureDisp(ic, 16);
largeIcon = ToolIcons.CreatePictureDisp(ic, 32);
}
catch { }
try
{
def = mApp.CommandManager.ControlDefinitions.AddButtonDefinition(
display, internalName, CommandTypesEnum.kNonShapeEditCmdType, mClientId, desc, tip,
stdIcon, largeIcon, ButtonDisplayEnum.kAlwaysDisplayText);
}
catch
{
def = mApp.CommandManager.ControlDefinitions.AddButtonDefinition(
display, internalName, CommandTypesEnum.kNonShapeEditCmdType, mClientId, desc, tip,
Type.Missing, Type.Missing, ButtonDisplayEnum.kAlwaysDisplayText);
}
mDefs[internalName] = def;
// 关键: 直接订阅按钮的 OnExecute 事件处理点击(与 Joyea 一致, 不依赖 ExecuteCommand 路由)
try
{
Inventor.ButtonDefinitionSink_Event evt = (Inventor.ButtonDefinitionSink_Event)def;
evt.OnExecute += delegate(NameValueMap context)
{
try { action(); }
catch (Exception ex) { System.Windows.Forms.MessageBox.Show("执行出错: " + ex.Message); }
};
}
catch { }
}
return def;
}
private static string IconName(string internalName)
{
if (internalName.EndsWith("Part")) return "part";
if (internalName.EndsWith("Asm")) return "asm";
if (internalName.EndsWith("Drw")) return "drw";
if (internalName.EndsWith("Export")) return "export";
if (internalName.EndsWith("Help")) return "help";
if (internalName.EndsWith("Circlip")) return "circlip";
if (internalName.EndsWith("Keyway")) return "keyway";
if (internalName.EndsWith("Oring")) return "oring";
if (internalName.EndsWith("BRing")) return "bearing";
return "help";
}
private static ToolIcon IconEnum(string name)
{
switch (name)
{
case "part": return ToolIcon.Part;
case "asm": return ToolIcon.Asm;
case "drw": return ToolIcon.Drw;
case "export": return ToolIcon.Export;
case "help": return ToolIcon.Help;
case "circlip": return ToolIcon.Circlip;
case "keyway": return ToolIcon.Keyway;
case "oring": return ToolIcon.Oring;
case "bearing": return ToolIcon.Bearing;
default: return ToolIcon.Help;
}
}
public void ExecuteCommand(int commandID)
{
// OnExecute 事件为主要路径, 此方法保留兼容(无操作)
}
public object Automation { get { return null; } }
public void Deactivate()
{
try
{
Inventor.UserInterfaceManager uim = mApp.UserInterfaceManager;
string[] ribNames = { "Part", "Assembly", "Drawing", "Presentation", "ZeroDoc" };
foreach (string n in ribNames)
{
try
{
Inventor.Ribbon ribbon = uim.Ribbons[n];
Inventor.RibbonTab tab = null;
try { tab = ribbon.RibbonTabs[TabId]; } catch { }
if (tab != null) { try { tab.Delete(); } catch { } }
}
catch { }
}
}
catch { }
}
}
}
+458
View File
@@ -0,0 +1,458 @@
using System;
using System.Collections.Generic;
using Inventor;
using DesignExpress.Core;
namespace DesignExpress.InventorLink
{
// Inventor 适配器: 实现 ICadHost, 把 Inventor 文档状态拍成 Core 快照
public class InventorHost : ICadHost
{
private Application mApp;
public InventorHost(Application app)
{
mApp = app;
}
public string AppName { get { return "Inventor"; } }
public object GetActiveDocument()
{
try { return mApp.ActiveDocument; } catch { return null; }
}
public string GetDocType(object doc)
{
try
{
dynamic d = doc;
DocumentTypeEnum t = (DocumentTypeEnum)d.DocumentType;
if (t == DocumentTypeEnum.kPartDocumentObject) return "Part";
if (t == DocumentTypeEnum.kAssemblyDocumentObject) return "Assembly";
if (t == DocumentTypeEnum.kDrawingDocumentObject) return "Drawing";
}
catch { }
return "";
}
public void SetStatusText(string text)
{
try { mApp.StatusBarText = text; } catch { }
}
public double PickCylinderDiameterMM(out string faceName)
{
faceName = "手动输入";
try
{
object pick = mApp.CommandManager.Pick(SelectionFilterEnum.kPartFaceCylindricalFilter,
"请选择轴承/配合圆柱面(轴或孔), 取消则手动输入直径");
if (pick != null)
{
Face f = pick as Face;
if (f != null)
{
object geom = f.Geometry;
if (geom is Cylinder)
{
double d = ((Cylinder)geom).Radius * 20;
try { faceName = Convert.ToString(((dynamic)f).Name); } catch { }
return d;
}
}
}
}
catch { }
return 0;
}
public DocSnapshot Capture(object doc)
{
DocSnapshot snap = new DocSnapshot();
dynamic d = doc;
snap.Kind = GetDocType(doc);
try { snap.Dirty = (bool)d.Dirty; } catch { }
try { snap.DisplayName = Convert.ToString(d.DisplayName); } catch { }
try
{
string full = Convert.ToString(d.FullDocumentName);
int p = full.LastIndexOf('\\');
if (p > 0) snap.Folder = full.Substring(0, p + 1);
}
catch { }
snap.Props = ReadProps(doc);
if (snap.Kind == "Part" || snap.Kind == "Assembly")
{
snap.MaterialName = ReadMaterial(d);
snap.Model = ReadModelInfo(d);
snap.Asm = new AssemblyInfo();
}
if (snap.Kind == "Assembly")
{
ReadAssemblyInfo(d, snap);
}
if (snap.Kind == "Drawing")
{
ReadDrawingInfo(d, snap);
}
ReadOpenDocs(snap);
return snap;
}
private Dictionary<string, string> ReadProps(object doc)
{
Dictionary<string, string> props = new Dictionary<string, string>();
try
{
dynamic d = doc;
foreach (PropertySet ps in d.PropertySets)
{
foreach (Property p in ps)
{
try
{
string key = Convert.ToString(p.Name);
string val = Convert.ToString(p.Value);
if (!string.IsNullOrEmpty(key) && !props.ContainsKey(key))
props[key] = val ?? "";
}
catch { }
}
}
}
catch { }
return props;
}
private string ReadMaterial(dynamic d)
{
try { return Convert.ToString(d.ComponentDefinition.Material.Name); } catch { return ""; }
}
private ModelInfo ReadModelInfo(dynamic d)
{
ModelInfo m = new ModelInfo();
try
{
dynamic ma = d.ComponentDefinition.ModelAnnotations;
try { m.DimCount = (int)ma.ModelDimensions.Count; } catch { }
try { m.FcfCount = (int)ma.ModelFeatureControlFrames.Count; } catch { }
try { m.TexCount = (int)ma.ModelSurfaceTextureSymbols.Count; } catch { }
try { m.DatumCount = (int)ma.ModelDatumIdentifiers.Count; } catch { }
try { m.ThreadNoteCount = (int)ma.ModelHoleThreadNotes.Count; } catch { }
if (m.DimCount > 0)
{
try
{
int tol = 0;
for (int i = 1; i <= m.DimCount; i++)
{
try
{
dynamic dd = ma.ModelDimensions[i];
dynamic tolObj = dd.Definition.Tolerance;
if (tolObj != null && tolObj.ToleranceType != ToleranceTypeEnum.kDefaultTolerance)
tol++;
}
catch { m.TolReadable = false; }
}
m.TolCount = tol;
}
catch { m.TolReadable = false; }
}
if (m.TexCount > 0)
{
try { m.FirstTexText = Convert.ToString(ma.ModelSurfaceTextureSymbols[1].Text); } catch { }
}
}
catch { }
try
{
dynamic tf = d.ComponentDefinition.Features.ThreadFeatures;
m.ThreadFeatureCount = (int)tf.Count;
if (m.ThreadFeatureCount > 0)
{
try { m.ThreadDesignation = Convert.ToString(tf[1].Designation); } catch { }
}
}
catch { }
try
{
dynamic ff = d.ComponentDefinition.Features.FilletFeatures;
int n = (int)ff.Count;
double minR = -1;
bool found = false;
for (int i = 1; i <= n; i++)
{
try
{
double r = (double)ff[i].Radius;
if (r > 0)
{
if (!found || r < minR) minR = r;
found = true;
}
}
catch { }
}
m.MinFilletReadable = found;
m.MinFilletRadiusMM = found ? minR * 10 : -1;
}
catch { }
return m;
}
private void ReadAssemblyInfo(dynamic d, DocSnapshot snap)
{
AssemblyInfo asm = snap.Asm;
try
{
dynamic occs = d.ComponentDefinition.Occurrences;
try { asm.TopLevelCount = (int)occs.Count; } catch { }
CollectOccurrenceNames(occs, asm.OccurrenceNames);
}
catch { }
try
{
dynamic acd = d.ComponentDefinition;
ObjectCollection set1 = mApp.TransientObjects.CreateObjectCollection();
dynamic occs = acd.Occurrences;
int cnt = (int)occs.Count;
for (int i = 1; i <= cnt; i++)
set1.Add(occs[i]);
dynamic res = acd.AnalyzeInterference(set1, null);
int n = (int)res.Count;
if (n > 0) asm.InterferenceState = 1;
for (int j = 1; j <= n && j <= 50; j++)
{
InterferencePair pair = new InterferencePair();
try
{
dynamic ir = res[j];
try { pair.Name1 = Convert.ToString(ir.OccurrenceOne.Name); } catch { }
try { pair.Name2 = Convert.ToString(ir.OccurrenceTwo.Name); } catch { }
try { pair.VolumeCM3 = (double)ir.Volume; } catch { }
asm.Interferences.Add(pair);
}
catch { }
}
}
catch (Exception ex)
{
asm.InterferenceState = 2;
asm.InterferenceError = ex.Message;
}
}
private void CollectOccurrenceNames(dynamic occs, List<string> names)
{
try
{
int cnt = (int)occs.Count;
for (int i = 1; i <= cnt; i++)
{
dynamic occ = occs[i];
if (occ == null) continue;
try { names.Add(Convert.ToString(occ.Name)); } catch { }
try
{
dynamic sub = occ.SubOccurrences;
if (sub != null) CollectOccurrenceNames(sub, names);
}
catch { }
}
}
catch { }
}
private void ReadDrawingInfo(dynamic d, DocSnapshot snap)
{
try
{
dynamic dd = d;
int nSheets = (int)dd.Sheets.Count;
for (int i = 1; i <= nSheets; i++)
{
SheetInfo sh = new SheetInfo();
try
{
dynamic sheet = dd.Sheets[i];
sh.Name = Convert.ToString(sheet.Name);
try { sh.SheetWidth = (double)sheet.Width; } catch { }
try { sh.SheetHeight = (double)sheet.Height; } catch { }
try
{
dynamic bd = sheet.Border;
if (bd != null)
{
sh.HasBorder = true;
try
{
dynamic box = bd.RangeBox;
RectD r = new RectD();
r.L = (double)box.MinPoint.X; r.T = (double)box.MinPoint.Y;
r.R = (double)box.MaxPoint.X; r.B = (double)box.MaxPoint.Y;
sh.BorderRect = r;
}
catch { }
}
}
catch { }
try
{
dynamic tb = sheet.TitleBlock;
if (tb != null)
{
sh.HasTitleBlock = true;
try { sh.TitleBlockName = Convert.ToString(tb.Name); } catch { }
}
}
catch { }
try
{
dynamic views = sheet.DrawingViews;
sh.ViewCount = (int)views.Count;
for (int j = 1; j <= sh.ViewCount; j++)
{
dynamic vw = views[j];
try
{
if (!(bool)vw.IsUpdateComplete) sh.UpdateNeeded++;
}
catch { }
try
{
if ((DrawingViewTypeEnum)vw.ViewType == DrawingViewTypeEnum.kSectionDrawingViewType)
sh.SectionCount++;
}
catch { }
try
{
dynamic pos = vw.Position;
double w = (double)vw.Width, h = (double)vw.Height;
RectD r = new RectD();
r.L = (double)pos.X - w / 2; r.R = (double)pos.X + w / 2;
r.T = (double)pos.Y - h / 2; r.B = (double)pos.Y + h / 2;
sh.ViewRects.Add(r);
}
catch { }
}
}
catch { }
try
{
dynamic dims = sheet.DrawingDimensions;
sh.DimCount = (int)dims.Count;
for (int j = 1; j <= sh.DimCount; j++)
{
dynamic dm = dims[j];
try
{
if (dm.Tolerance.ToleranceType != ToleranceTypeEnum.kDefaultTolerance)
sh.DimTolCount++;
}
catch { }
try
{
if (Convert.ToString(dm.Text.Text).Contains("?")) sh.DimQuestionCount++;
}
catch { }
}
}
catch { }
try { sh.FcfCount = (int)sheet.FeatureControlFrames.Count; } catch { }
try { sh.TexCount = (int)sheet.SurfaceTextureSymbols.Count; } catch { }
try { sh.CenterlineCount = (int)sheet.Centerlines.Count; } catch { }
try { sh.BalloonCount = (int)sheet.Balloons.Count; } catch { }
try
{
dynamic notes = sheet.DrawingNotes;
int nn = (int)notes.Count;
for (int j = 1; j <= nn; j++)
{
try { sh.NotesTexts.Add(Convert.ToString(notes[j].Text)); } catch { }
}
}
catch { }
try
{
for (int j = 1; j <= sh.FcfCount; j++)
{
try
{
dynamic ann = sheet.FeatureControlFrames[j];
dynamic p1 = mApp.TransientGeometry.CreatePoint2d();
dynamic p2 = mApp.TransientGeometry.CreatePoint2d();
ann.GetBounds(p1, p2);
RectD r = new RectD();
r.L = (double)p1.X; r.T = (double)p1.Y; r.R = (double)p2.X; r.B = (double)p2.Y;
sh.FcfRects.Add(r);
}
catch { }
}
for (int j = 1; j <= sh.TexCount; j++)
{
try
{
dynamic ann = sheet.SurfaceTextureSymbols[j];
dynamic p1 = mApp.TransientGeometry.CreatePoint2d();
dynamic p2 = mApp.TransientGeometry.CreatePoint2d();
ann.GetBounds(p1, p2);
RectD r = new RectD();
r.L = (double)p1.X; r.T = (double)p1.Y; r.R = (double)p2.X; r.B = (double)p2.Y;
sh.TexRects.Add(r);
}
catch { }
}
}
catch { }
snap.Sheets.Add(sh);
}
catch { }
}
}
catch { }
}
private void ReadOpenDocs(DocSnapshot snap)
{
try
{
dynamic docs = mApp.Documents;
int n = (int)docs.Count;
for (int i = 1; i <= n; i++)
{
dynamic dd = docs[i];
if (dd == null) continue;
try
{
DocumentTypeEnum t = (DocumentTypeEnum)dd.DocumentType;
if (t != DocumentTypeEnum.kPartDocumentObject) continue;
}
catch { continue; }
DocInfo info = new DocInfo();
try { info.Name = Convert.ToString(dd.DisplayName); } catch { }
info.Props = ReadProps(dd);
info.Material = AppState.GetProp(info.Props, "Material", "材料", "材质");
if (info.Material == "")
{
try { info.Material = Convert.ToString(dd.ComponentDefinition.Material.Name); } catch { }
}
try { info.TexCount = (int)dd.ComponentDefinition.ModelAnnotations.ModelSurfaceTextureSymbols.Count; } catch { }
snap.OpenDocs.Add(info);
}
}
catch { }
}
}
}
+326
View File
@@ -0,0 +1,326 @@
using System;
using System.Collections.Generic;
using SolidWorks.Interop.sldworks;
using DesignExpress.Core;
namespace DesignExpress.SolidWorksLink
{
// SolidWorks 适配器: 实现 ICadHost, 把 SW 文档状态拍成 Core 快照
public class SolidWorksHost : ICadHost
{
private ISldWorks mSwApp;
public SolidWorksHost(ISldWorks swApp)
{
mSwApp = swApp;
}
public string AppName { get { return "SolidWorks"; } }
public object GetActiveDocument()
{
try { return mSwApp.IActiveDoc2; } catch { return null; }
}
public string GetDocType(object doc)
{
try
{
// 注意: 不能用 dynamic 调 GetType()(会解析成 Object.GetType), 必须编译时调用
ModelDoc2 m = doc as ModelDoc2;
if (m != null)
{
int t = m.GetType();
if (t == 1) return "Part"; // swDocPART
if (t == 2) return "Assembly"; // swDocASSEMBLY
if (t == 3) return "Drawing"; // swDocDRAWING
}
}
catch { }
return "";
}
public void SetStatusText(string text)
{
try { ((dynamic)mSwApp).SetStatusBarText(text); } catch { }
}
public double PickCylinderDiameterMM(out string faceName)
{
faceName = "手动输入";
try
{
dynamic doc = mSwApp.IActiveDoc2;
if (doc == null) return 0;
dynamic selMgr = doc.ISelectionManager;
if (selMgr == null) return 0;
dynamic face = selMgr.GetSelectedObject6(1, -1);
if (face == null) return 0;
double[] origin = new double[3];
double[] axis = new double[3];
double radius = 0;
face.IGetCylinderParams(origin, axis, out radius);
if (radius > 0) return radius * 2 * 1000; // 米 -> 毫米
}
catch { }
return 0;
}
public DocSnapshot Capture(object doc)
{
DocSnapshot snap = new DocSnapshot();
dynamic d = doc;
snap.Kind = GetDocType(doc);
try { snap.Dirty = (bool)d.GetSaveFlag(); } catch { }
try { snap.DisplayName = Convert.ToString(d.GetTitle()); } catch { }
try
{
string full = Convert.ToString(d.GetPathName());
int p = full.LastIndexOf('\\');
if (p > 0) snap.Folder = full.Substring(0, p + 1);
}
catch { }
snap.Props = ReadProps(d);
snap.MaterialName = ReadMaterial(d);
if (snap.Kind == "Part" || snap.Kind == "Assembly")
{
snap.Model = ReadModelInfo(d);
snap.Asm = new AssemblyInfo();
}
if (snap.Kind == "Assembly")
{
ReadAssemblyInfo(d, snap);
}
if (snap.Kind == "Drawing")
{
ReadDrawingInfo(d, snap);
}
ReadOpenDocs(snap);
return snap;
}
private Dictionary<string, string> ReadProps(dynamic d)
{
Dictionary<string, string> props = new Dictionary<string, string>();
try
{
dynamic ext = d.Extension;
dynamic cpm = ext.CustomPropertyManager("");
if (cpm != null)
{
object names = cpm.GetNames();
if (names is object[])
{
foreach (object n in (object[])names)
{
try
{
string name = Convert.ToString(n);
string val = "", resolved = "";
bool wasResolved = false, link = false;
cpm.Get6(name, false, out val, out resolved, out wasResolved, out link);
if (name != "" && !props.ContainsKey(name))
props[name] = val ?? "";
}
catch { }
}
}
}
}
catch { }
return props;
}
private string ReadMaterial(dynamic d)
{
try
{
string mat = "";
string config = "";
try { config = Convert.ToString(d.ConfigurationManager.ActiveConfiguration.Name); } catch { }
if (d.GetMaterialPropertyName2(config, out mat))
return mat ?? "";
}
catch { }
return "";
}
private ModelInfo ReadModelInfo(dynamic d)
{
ModelInfo m = new ModelInfo();
// 最小圆角(遍历特征中的 Fillet)
try
{
dynamic fm = d.FeatureManager;
dynamic feat = fm.GetFirstFeature();
double minR = -1;
bool found = false;
while (feat != null)
{
try
{
string tn = Convert.ToString(feat.GetTypeName2());
if (tn == "Fillet")
{
dynamic fd = feat.GetDefinition();
double r = (double)fd.Radius;
if (r > 0)
{
if (!found || r < minR) minR = r;
found = true;
}
}
}
catch { }
feat = feat.GetNextFeature();
}
m.MinFilletReadable = found;
m.MinFilletRadiusMM = found ? minR * 1000 : -1;
}
catch { }
return m;
}
private void ReadAssemblyInfo(dynamic d, DocSnapshot snap)
{
AssemblyInfo asm = snap.Asm;
try
{
dynamic comps = d.GetComponents(false);
if (comps is object[])
{
asm.TopLevelCount = ((object[])comps).Length;
CollectComponentNames(comps, asm.OccurrenceNames);
}
}
catch { }
try
{
object comps = d.GetComponents(false);
object pComp = null, pFace = null;
d.ToolsCheckInterference2(0, comps, true, out pComp, out pFace);
if (pComp is object[])
{
int n = ((object[])pComp).Length;
if (n > 0)
{
asm.InterferenceState = 1;
for (int i = 0; i < n && i < 50; i++)
{
InterferencePair pair = new InterferencePair();
try
{
dynamic c1 = ((object[])pComp)[i];
dynamic c2 = null;
if (pFace is object[] && i < ((object[])pFace).Length)
c2 = ((object[])pFace)[i];
try { pair.Name1 = Convert.ToString(c1.Name2); } catch { }
try { pair.Name2 = Convert.ToString(c2.Name2); } catch { }
asm.Interferences.Add(pair);
}
catch { }
}
}
}
}
catch (Exception ex)
{
asm.InterferenceState = 2;
asm.InterferenceError = ex.Message;
}
}
private void CollectComponentNames(object comps, List<string> names)
{
try
{
foreach (object c in (object[])comps)
{
try
{
dynamic comp = c;
names.Add(Convert.ToString(comp.Name2));
object children = comp.GetChildren();
if (children is object[] && ((object[])children).Length > 0)
CollectComponentNames(children, names);
}
catch { }
}
}
catch { }
}
private void ReadDrawingInfo(dynamic d, DocSnapshot snap)
{
try
{
object sheetNamesObj = d.GetSheetNames();
string[] sheetNames = null;
if (sheetNamesObj is string[]) sheetNames = (string[])sheetNamesObj;
if (sheetNamesObj is object[]) sheetNames = Array.ConvertAll((object[])sheetNamesObj, o => Convert.ToString(o));
if (sheetNames == null || sheetNames.Length == 0) return;
foreach (string sn in sheetNames)
{
SheetInfo sh = new SheetInfo();
sh.Name = sn;
try
{
d.ActivateSheet(sn);
dynamic views = d.GetViews();
if (views is object[])
{
sh.ViewCount = ((object[])views).Length;
foreach (object v in (object[])views)
{
try
{
dynamic view = v;
object notes = view.GetNotes();
if (notes is object[])
{
foreach (object n in (object[])notes)
{
try { sh.NotesTexts.Add(Convert.ToString(((dynamic)n).GetText())); } catch { }
}
}
}
catch { }
}
}
}
catch { }
snap.Sheets.Add(sh);
}
}
catch { }
}
private void ReadOpenDocs(DocSnapshot snap)
{
try
{
object docs = mSwApp.GetDocuments();
if (!(docs is object[])) return;
foreach (object d in (object[])docs)
{
DocInfo info = new DocInfo();
dynamic dd = d;
try { info.Name = Convert.ToString(dd.GetTitle()); } catch { }
try
{
int t = (int)dd.GetType();
if (t != 1) continue;
}
catch { continue; }
info.Props = ReadProps(dd);
info.Material = AppState.GetProp(info.Props, "Material", "材料", "材质");
if (info.Material == "") info.Material = ReadMaterial(dd);
snap.OpenDocs.Add(info);
}
}
catch { }
}
}
}
+283
View File
@@ -0,0 +1,283 @@
using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using SolidWorks.Interop.sldworks;
using SolidWorks.Interop.swpublished;
using DesignExpress.Core;
[assembly: ComVisible(true)]
namespace DesignExpress.SolidWorksLink
{
// SolidWorks 加载项外壳: 实现 ISwAddin, 延迟创建 CommandManager 选项卡, 回调全部转到 Core
[Guid("4A7E023E-EDD8-4642-B337-425E02394C81")]
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDual)]
[ProgId("DesignExpress.SolidWorksLink.SwAddin")]
public class SwAddin : ISwAddin
{
private const string AddinClsid = "{4A7E023E-EDD8-4642-B337-425E02394C81}";
private const string Title = "Design Express Res";
private const int GroupId = 23130;
private ISldWorks mSwApp;
private int mCookie;
private DSldWorksEvents_Event mSwEvents;
private ICommandGroup mCmdGroup;
private int[] mCmdIndexes;
private readonly object mTabLock = new object();
static SwAddin()
{
// COM 激活的程序集依赖按宿主进程目录探测, 手动解析同目录的 Core.dll
AppDomain.CurrentDomain.AssemblyResolve += delegate(object sender, ResolveEventArgs args)
{
try
{
string name = new AssemblyName(args.Name).Name;
if (name == "DesignExpress.Core")
{
string dir = Path.GetDirectoryName(typeof(SwAddin).Assembly.Location);
string path = Path.Combine(dir, "DesignExpress.Core.dll");
if (File.Exists(path)) return Assembly.LoadFrom(path);
}
}
catch { }
return null;
};
}
public bool ConnectToSW(object ThisSW, int cookie)
{
try
{
mSwApp = (ISldWorks)ThisSW;
mCookie = cookie;
AppState.Host = new SolidWorksHost(mSwApp);
// 关键: SW 启动早期命令系统未就绪, 命令组/选项卡延迟到文档激活事件创建
try
{
mSwEvents = (DSldWorksEvents_Event)mSwApp;
mSwEvents.ActiveDocChangeNotify += OnActiveDocChange;
}
catch { }
EnsureAll();
return true;
}
catch { return false; }
}
private int OnActiveDocChange()
{
EnsureAll();
return 0;
}
private void EnsureAll()
{
lock (mTabLock)
{
try
{
if (mCmdGroup == null)
{
SetupCommandGroup();
Log("SetupCommandGroup: " + (mCmdGroup != null ? "OK" : "NULL"));
}
if (mCmdGroup != null)
{
// 每次文档激活都幂等确保选项卡存在(文档切换时 SW 可能清除 addin 选项卡)
AddCommandManagerTabs();
}
MakeTabVisible();
}
catch (Exception ex) { Log("EnsureAll 异常: " + ex.Message); }
}
}
public bool DisconnectFromSW()
{
Log("DisconnectFromSW 被调用!");
try
{
ICommandManager cmdMgr = mSwApp.GetCommandManager(mCookie);
cmdMgr.RemoveCommandGroup2(GroupId, true);
}
catch { }
return true;
}
// 启用回调: 返回 1 = 命令始终可用
public int EnableMethod() { return 1; }
// 创建命令组+命令项(菜单可用), 选项卡延迟创建
private void SetupCommandGroup()
{
try
{
ICommandManager cmdMgr = mSwApp.GetCommandManager(mCookie);
mSwApp.SetAddinCallbackInfo(0, this, mCookie);
string iconList = "";
try { iconList = SwIcons.EnsureIcons(); } catch { }
ICommandGroup grp = cmdMgr.CreateCommandGroup(GroupId, Title, Title, "", -1);
if (grp == null) return;
if (iconList != "")
{
try { grp.LargeIconList = iconList; } catch { }
try { grp.SmallIconList = iconList; } catch { }
}
int[] idx = new int[9];
idx[0] = grp.AddCommandItem2("零件检查", -1, "对当前零件进行三维标注、公差配合、GMP 检查", "零件检查", 0, "PartCheck", "EnableMethod", 0, 1);
idx[1] = grp.AddCommandItem2("装配检查", -1, "对当前装配进行 GMP、密封、干涉、外购件检查", "装配检查", 1, "AssemblyCheck", "EnableMethod", 1, 1);
idx[2] = grp.AddCommandItem2("工程图检查", -1, "对当前工程图进行标注与 spec 技术要求检查", "工程图检查", 2, "DrawingCheck", "EnableMethod", 2, 1);
idx[3] = grp.AddCommandItem2("导出报告", -1, "导出检查报告(Excel/CSV)", "导出报告", 3, "ExportReport", "EnableMethod", 3, 1);
idx[4] = grp.AddCommandItem2("使用说明", -1, "查看检查项清单与使用说明", "使用说明", 4, "Help", "EnableMethod", 4, 1);
idx[5] = grp.AddCommandItem2("卡簧槽查询", -1, "轴用/孔用弹性挡圈槽尺寸查询 (GB/T 894)", "卡簧槽查询", 5, "CirclipQuery", "EnableMethod", 5, 1);
idx[6] = grp.AddCommandItem2("键槽查询", -1, "平键键槽尺寸查询 (GB/T 1095)", "键槽查询", 6, "KeywayQuery", "EnableMethod", 6, 1);
idx[7] = grp.AddCommandItem2("O型密封圈查询", -1, "O 型密封圈设计与沟槽尺寸查询 (GB/T 3452.1)", "O型密封圈查询", 7, "OringQuery", "EnableMethod", 7, 1);
idx[8] = grp.AddCommandItem2("轴承挡圈查询", -1, "轴承挡圈与挡边设计查询", "轴承挡圈查询", 8, "BearingRingQuery", "EnableMethod", 8, 1);
try { grp.HasToolbar = true; } catch { }
try { grp.HasMenu = true; } catch { }
grp.Activate();
mCmdGroup = grp;
mCmdIndexes = idx;
}
catch { }
}
// 文档激活后创建 CommandManager 选项卡(SW 命令系统就绪后)
private void AddCommandManagerTabs()
{
try
{
ICommandManager cmdMgr = mSwApp.GetCommandManager(mCookie);
foreach (int docType in new int[] { 1, 2, 3 })
{
try
{
ICommandTab cmdTab = cmdMgr.GetCommandTab(docType, Title);
if (cmdTab == null)
{
cmdTab = cmdMgr.AddCommandTab(docType, Title);
}
if (cmdTab != null)
{
// 幂等: 已有命令则不重复添加, 避免按钮重复
int boxCount = cmdTab.GetCommandTabBoxCount();
bool hasCommands = false;
if (boxCount > 0)
{
object boxes = cmdTab.CommandTabBoxes();
if (boxes is object[])
{
foreach (object b in (object[])boxes)
{
try
{
dynamic box = b;
object ids = null, tts = null;
if (box.GetCommands(out ids, out tts) > 0) hasCommands = true;
}
catch { }
}
}
}
if (hasCommands)
{
Log("docType=" + docType + " 已有命令, 跳过");
}
else
{
ICommandTabBox cmdBox = cmdTab.AddCommandTabBox();
int[] cmdIDs = new int[mCmdIndexes.Length];
int[] textType = new int[mCmdIndexes.Length];
for (int i = 0; i < mCmdIndexes.Length; i++)
{
cmdIDs[i] = mCmdGroup.CommandID[mCmdIndexes[i]];
textType[i] = 2; // swCommandTabButton_TextBelow
}
cmdBox.AddCommands(cmdIDs, textType);
Log("docType=" + docType + " AddCommands 9 个");
}
}
}
catch { }
}
}
catch { }
}
// 文档扩展: 设置选项卡可见并激活(仅运行时创建时可能需强制显示)
private void MakeTabVisible()
{
try
{
ModelDoc2 doc = mSwApp.IActiveDoc2;
if (doc == null) { Log("MakeTabVisible: 无文档"); return; }
ModelDocExtension ext = doc.Extension;
if (ext == null) return;
object tabs = ext.GetCommandTabs();
if (tabs is string[])
{
string[] names = (string[])tabs;
Log("GetCommandTabs 共 " + names.Length + " 个");
for (int i = 0; i < names.Length; i++)
{
Log(" Tab[" + i + "]=" + names[i]);
if (names[i] == Title)
{
ext.set_CommandTabVisible(i, true);
try
{
ICommandManager cmdMgr = mSwApp.GetCommandManager(mCookie);
dynamic ourTab = cmdMgr.GetCommandTab(1, Title);
if (ourTab != null) { ourTab.Active = true; Log("tab.Active=true"); }
else { Log("GetCommandTab 返回 null"); }
}
catch (Exception ex) { Log("tab.Active 失败: " + ex.Message); }
try { ext.ActiveCommandTab = Title; Log("ActiveCommandTab 已设"); } catch (Exception ex) { Log("ActiveCommandTab 失败: " + ex.Message); }
return;
}
}
Log("未找到 " + Title);
}
}
catch (Exception ex) { Log("MakeTabVisible 异常: " + ex.Message); }
}
private static void Log(string msg)
{
try
{
System.IO.File.AppendAllText(
System.IO.Path.Combine(System.IO.Path.GetTempPath(), "DesignExpress_SW.log"),
DateTime.Now.ToString("HH:mm:ss.fff") + " " + msg + System.Environment.NewLine);
}
catch { }
}
// ============ 命令回调(公开方法, SW 通过 IDispatch 调用) ============
public void PartCheck() { Safe(Checks.PartCheck); }
public void AssemblyCheck() { Safe(Checks.AssemblyCheck); }
public void DrawingCheck() { Safe(Checks.DrawingCheck); }
public void ExportReport() { Safe(Checks.ExportReport); }
public void Help() { Safe(Checks.Help); }
public void CirclipQuery() { Safe(Checks.CirclipQuery); }
public void KeywayQuery() { Safe(Checks.KeywayQuery); }
public void OringQuery() { Safe(Checks.OringQuery); }
public void BearingRingQuery() { Safe(Checks.BearingRingQuery); }
private static void Safe(Action action)
{
try { action(); }
catch (Exception ex) { System.Windows.Forms.MessageBox.Show("执行出错: " + ex.Message); }
}
}
}
+34
View File
@@ -0,0 +1,34 @@
using System;
using System.IO;
using DesignExpress.Core;
namespace DesignExpress.SolidWorksLink
{
// SolidWorks 命令图标条: 复用 Core.ToolIcons(仓鼠品牌+功能符号), 生成 9 格 BMP
public static class SwIcons
{
public static string EnsureIcons()
{
try
{
string dir = Path.GetDirectoryName(typeof(SwIcons).Assembly.Location);
string file = Path.Combine(dir, "DesignExpress_icons.bmp");
if (File.Exists(file)) return file;
ToolIcon[] icons = new ToolIcon[]
{
ToolIcon.Part, // 零件检查
ToolIcon.Asm, // 装配检查
ToolIcon.Drw, // 工程图检查
ToolIcon.Export, // 导出报告
ToolIcon.Help, // 使用说明
ToolIcon.Circlip, // 卡簧槽查询
ToolIcon.Keyway, // 键槽查询
ToolIcon.Oring, // O型密封圈查询
ToolIcon.Bearing // 轴承挡圈查询
};
return ToolIcons.CreateStripBmp(icons, 32, file);
}
catch { return ""; }
}
}
}
+113
View File
@@ -0,0 +1,113 @@
# Design Express Res — CAD 设计标注检查与设计数据查询工具
基于 **Core + Link 架构**(类似 COMSOL LiveLink):独立核心库与 CAD 完全解耦,Inventor / SolidWorks 各自通过 Link 适配层调用 Core 功能。
## 架构
```
┌────────────────────────────────────────────┐
│ DesignExpress.Core.dll (独立核心, 不依赖CAD) │
│ - KB 知识库 (GB/T 275/1800/1801/894/1095) │
│ - CheckEngine 检查引擎 (P/A/D 三阶段) │
│ - DesignQuery 设计数据查询 (键槽/卡簧槽已实装)│
│ - UI: MainForm/QueryForm/FitForm │
│ - Report 报告 (Excel/CSV) │
│ - ICadHost 抽象接口 + DocSnapshot 快照 │
│ - ToolIcons 共享图标(仓鼠品牌+功能符号) │
└────────────────────────────────────────────┘
▲ ▲
InventorLink SolidWorksLink
(已交付) (已交付)
```
- **ICadHost 接口**:`GetActiveDocument / GetDocType / Capture / PickCylinderDiameterMM / SetStatusText`
- **DocSnapshot 快照**:Link 层把 CAD 文档状态拍成纯数据,Core 只处理快照
- 新增 CAD 支持 = 实现一个 ICadHost + 该 CAD 的选项卡壳
## 功能
| 系统 | 内容 |
|------|------|
| 设计检查 | 零件(标注/公差配合GB/T 275/形位/粗糙度/螺纹/物料接触GMP/表面处理/强度指引)、装配(GMP材料/密封/清洁/静态干涉/外购件/电气西门子/动力)、工程图(图框/视图/尺寸公差/22项spec关键词检查) |
| 设计数据查询 | 键槽(GB/T 1095)、卡簧槽(GB/T 894.1)已实装; O型密封圈(GB/T 3452.1)、轴承挡圈规划中 |
## 部署
### Inventor
1. 复制 `AddIn/deploy` 文件夹到目标机器
2. 右键 `install.ps1` → **使用 PowerShell 运行**(无需管理员)
3. 重启 Inventor,功能区出现 **[Design Express Res]** 选项卡
### SolidWorks
1. 复制 `AddIn/deploy` 文件夹
2. 运行 `install_solidworks.ps1`(用户级注册)
3. **首次需管理员执行两条 reg add**(脚本末尾有提示):
- `HKLM\SOFTWARE\SolidWorks\AddIns\{4A7E023E-...}` 键(default=1, Title)
4. 启动 SolidWorks → 工具→插件 勾选 Design Express Res
5. 打开零件 → 出现选项卡 → **正常退出并重启一次**(持久化选项卡定义)
## 文件清单
| 路径 | 说明 |
|------|------|
| `AddIn/deploy/DesignExpress.Core.dll` | 独立核心(成品) |
| `AddIn/deploy/DesignExpress.InventorLink.dll` | Inventor 适配层(成品) |
| `AddIn/deploy/DesignExpress.SolidWorksLink.dll` | SolidWorks 适配层(成品) |
| `AddIn/deploy/install.ps1` | Inventor 一键部署脚本 |
| `AddIn/deploy/install_solidworks.ps1` | SolidWorks 部署脚本 |
| `AddIn/src/Core/*.cs` | 核心源码(ICadHost/Snapshots/KB/CheckEngine/DesignQuery/UI/ToolIcons) |
| `AddIn/src/InventorLink/*.cs` | Inventor 适配源码 |
| `AddIn/src/SolidWorksLink/*.cs` | SolidWorks 适配源码 |
## 关键技术点
### Inventor
- **按钮点击**:`ButtonDefinition.OnExecute` 事件(不依赖 ExecuteCommand 路由)
- **加载规则**:`AddInLoadRules` 二进制文件控制加载,install.ps1 自动修复(DWORD=0 允许加载)
### SolidWorks
- **延迟创建**:命令组/选项卡在**文档激活事件**(`ActiveDocChangeNotify`)中创建——SW 启动早期命令系统未就绪,ConnectToSW 时创建会静默失败
- **EnableMethod 回调**:命令必须提供启用方法(返回 1),否则 SW 禁用命令导致按钮不可见
- **tab.Active = true**:选项卡可见性 + 激活必须直接设置,否则不显示在选项卡栏
- **持久化**:SW 正常退出时把选项卡定义写入注册表,重启后读取显示
- **GetDocType 注意**:不能 `dynamic.GetType()`(解析成 Object.GetType),必须编译时调用
### 通用
- **窗口**:1/4 屏幕面积,TableLayoutPanel 百分比布局,无固定尺寸不遮罩
- **报告**:Excel COM 动态调用,未装 Excel 回退 CSV
- **图标**:ToolIcons 共享生成(功能符号集中式),SolidWorks 用 9 格 BMP 图标条;Inventor 因 AddButtonDefinition 对 IPictureDisp 兼容性问题暂用文字按钮(可后续手动补图标)
## 建议的 iProperties 自定义属性
| 属性名 | 取值示例 | 用途 |
|--------|----------|------|
| 物料接触 | 直接接触 / 间接接触 / 非接触 | 触发 GMP 专项检查 |
| 表面处理 | 镀镍+磨削 / 抛光 / 发黑 | 表面处理检查 |
| 材料 | 316L / 304 / PTFE | GMP 材料判定 |
## 编译
```powershell
# Core (无 CAD 依赖)
csc /target:library /platform:x64 /codepage:65001 /out:DesignExpress.Core.dll ^
/r:System.Windows.Forms.dll /r:System.Drawing.dll /r:Microsoft.CSharp.dll /r:System.Core.dll ^
src\Core\*.cs
# Inventor Link
csc /target:library /platform:x64 /codepage:65001 /out:DesignExpress.InventorLink.dll ^
/r:DesignExpress.Core.dll /r:"<Inventor>\Bin\Public Assemblies\Autodesk.Inventor.Interop.dll" ^
/r:System.Windows.Forms.dll /r:System.Drawing.dll /r:Microsoft.CSharp.dll /r:System.Core.dll ^
src\InventorLink\*.cs
# SolidWorks Link
csc /target:library /platform:x64 /codepage:65001 /out:DesignExpress.SolidWorksLink.dll ^
/r:DesignExpress.Core.dll /r:"<SW>\SolidWorks.Interop.sldworks.dll" ^
/r:"<SW>\SolidWorks.Interop.swpublished.dll" /r:"<SW>\SolidWorks.Interop.swconst.dll" ^
/r:System.Windows.Forms.dll /r:System.Drawing.dll /r:Microsoft.CSharp.dll /r:System.Core.dll ^
src\SolidWorksLink\*.cs
```
## 版本历史
- **1.3.0** (2026-08-21): SolidWorks Link 交付; 功能图标(集中式功能符号); 生产版代码清理
- **1.3.1** (2026-08-22): 清理调试代码与临时文件; Inventor 图标方案(IPictureDisp)因 API 兼容性暂缓, 保留文字按钮