947 lines
42 KiB
C#
947 lines
42 KiB
C#
using System;
|
||
using System.Drawing;
|
||
using System.Globalization;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System.Windows.Forms;
|
||
using Autodesk.AutoCAD.ApplicationServices;
|
||
using Autodesk.AutoCAD.DatabaseServices;
|
||
using Autodesk.AutoCAD.Geometry;
|
||
using CadParamPluging.Cad;
|
||
using CadParamPluging.Common;
|
||
using CadParamPluging.Domain;
|
||
using CadParamPluging.Domain.Models;
|
||
using AcadApp = Autodesk.AutoCAD.ApplicationServices.Application;
|
||
|
||
namespace CadParamPluging.UI
|
||
{
|
||
public class ParamDrawingPanel : UserControl
|
||
{
|
||
private readonly ComboBox _cbProjectType;
|
||
private readonly ComboBox _cbDrawingType;
|
||
private readonly ComboBox _cbSheetSize;
|
||
private readonly ComboBox _cbScale;
|
||
private TextBox _txtLog;
|
||
|
||
|
||
public ParamDrawingPanel()
|
||
{
|
||
Dock = DockStyle.Fill;
|
||
|
||
var dropdownOptions = DropdownOptionsStore.Load();
|
||
|
||
var layout = new TableLayoutPanel
|
||
{
|
||
Dock = DockStyle.Fill,
|
||
ColumnCount = 1,
|
||
RowCount = 4,
|
||
AutoSize = true,
|
||
AutoSizeMode = AutoSizeMode.GrowAndShrink,
|
||
Padding = new Padding(8)
|
||
};
|
||
|
||
_cbProjectType = CreateComboBox(dropdownOptions.DeliveryStatuses);
|
||
_cbDrawingType = CreateComboBox(dropdownOptions.ProcessMethods);
|
||
_cbSheetSize = CreateComboBox(dropdownOptions.StructuralFeatures);
|
||
_cbScale = CreateComboBox(dropdownOptions.SpecialConditions);
|
||
|
||
// Initialize template controls here in constructor to follow readonly/non-readonly best practices,
|
||
// though they are no longer readonly.
|
||
var templateGroup = BuildTemplateGroup();
|
||
var drawingGroup = BuildDrawingGroup();
|
||
var actionPanel = BuildActionPanel();
|
||
var logGroup = BuildLogGroup();
|
||
|
||
layout.Controls.Add(templateGroup, 0, 0);
|
||
layout.Controls.Add(drawingGroup, 0, 1);
|
||
layout.Controls.Add(actionPanel, 0, 2);
|
||
layout.Controls.Add(logGroup, 0, 3);
|
||
|
||
Controls.Add(layout);
|
||
}
|
||
|
||
private GroupBox BuildTemplateGroup()
|
||
{
|
||
var group = new GroupBox
|
||
{
|
||
Text = "模板参数",
|
||
Dock = DockStyle.Top,
|
||
AutoSize = true,
|
||
AutoSizeMode = AutoSizeMode.GrowAndShrink
|
||
};
|
||
|
||
var grid = new TableLayoutPanel
|
||
{
|
||
Dock = DockStyle.Fill,
|
||
ColumnCount = 2,
|
||
RowCount = 4,
|
||
AutoSize = true
|
||
};
|
||
|
||
grid.Controls.Add(new Label { Text = "交付状态", AutoSize = true, TextAlign = ContentAlignment.MiddleLeft }, 0, 0);
|
||
grid.Controls.Add(_cbProjectType, 1, 0);
|
||
|
||
grid.Controls.Add(new Label { Text = "工艺方法", AutoSize = true, TextAlign = ContentAlignment.MiddleLeft }, 0, 1);
|
||
grid.Controls.Add(_cbDrawingType, 1, 1);
|
||
|
||
grid.Controls.Add(new Label { Text = "结构特征", AutoSize = true, TextAlign = ContentAlignment.MiddleLeft }, 0, 2);
|
||
grid.Controls.Add(_cbSheetSize, 1, 2);
|
||
|
||
grid.Controls.Add(new Label { Text = "特殊条件", AutoSize = true, TextAlign = ContentAlignment.MiddleLeft }, 0, 3);
|
||
grid.Controls.Add(_cbScale, 1, 3);
|
||
|
||
group.Controls.Add(grid);
|
||
return group;
|
||
}
|
||
|
||
private GroupBox BuildDrawingGroup()
|
||
{
|
||
var group = new GroupBox
|
||
{
|
||
Text = "出图参数",
|
||
Dock = DockStyle.Top,
|
||
AutoSize = true,
|
||
AutoSizeMode = AutoSizeMode.GrowAndShrink
|
||
};
|
||
|
||
var label = new Label
|
||
{
|
||
AutoSize = true,
|
||
Padding = new Padding(8),
|
||
Text = "出图参数较多,请点击【生成图纸】后在弹窗中填写。"
|
||
};
|
||
|
||
group.Controls.Add(label);
|
||
return group;
|
||
}
|
||
|
||
private FlowLayoutPanel BuildActionPanel()
|
||
{
|
||
var panel = new FlowLayoutPanel
|
||
{
|
||
Dock = DockStyle.Top,
|
||
AutoSize = true
|
||
};
|
||
|
||
var btnGenerate = new Button { Text = "生成图纸", AutoSize = true };
|
||
btnGenerate.Click += (_, __) => OnGenerateDrawing();
|
||
|
||
var btnSaveAs = new Button { Text = "保存当前图纸", AutoSize = true };
|
||
btnSaveAs.Click += (_, __) => OnSaveDrawing();
|
||
|
||
var btnSettings = new Button { Text = "设置", AutoSize = true };
|
||
btnSettings.Click += (_, __) => OnOpenSettings();
|
||
|
||
var btnTestDraw = new Button { Text = "测试绘制", AutoSize = true };
|
||
btnTestDraw.Click += (_, __) => OnTestDraw();
|
||
|
||
panel.Controls.Add(btnGenerate);
|
||
panel.Controls.Add(btnSaveAs);
|
||
|
||
var btnLoadParams = new Button { Text = "读取参数", AutoSize = true };
|
||
btnLoadParams.Click += (_, __) => OnLoadParams();
|
||
panel.Controls.Add(btnLoadParams);
|
||
|
||
panel.Controls.Add(btnSettings);
|
||
// panel.Controls.Add(btnTestDraw); // 暂时屏蔽测试绘制按钮
|
||
return panel;
|
||
}
|
||
|
||
private GroupBox BuildLogGroup()
|
||
{
|
||
var group = new GroupBox
|
||
{
|
||
Text = "日志",
|
||
Dock = DockStyle.Fill
|
||
};
|
||
|
||
_txtLog = new TextBox
|
||
{
|
||
Multiline = true,
|
||
ScrollBars = ScrollBars.Vertical,
|
||
Dock = DockStyle.Fill,
|
||
Height = 140
|
||
};
|
||
|
||
group.Controls.Add(_txtLog);
|
||
return group;
|
||
}
|
||
|
||
|
||
private static double ParseScaleFactor(string raw)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(raw))
|
||
{
|
||
throw new BusinessException("请填写:出图比例 (DrawingScale)");
|
||
}
|
||
|
||
var s = (raw ?? string.Empty).Trim();
|
||
s = s.Replace(" ", "").Replace("\u3000", "").Replace(":", ":");
|
||
|
||
var parts = s.Split(':');
|
||
if (parts.Length != 2)
|
||
{
|
||
throw new BusinessException("出图比例格式错误,应为 M:N(例如 1:5 或 2:1)");
|
||
}
|
||
|
||
if (!TryParseDouble(parts[0], out var num) || num < 1e-9 ||
|
||
!TryParseDouble(parts[1], out var denom) || denom < 1e-9)
|
||
{
|
||
throw new BusinessException("出图比例数值错误,M和N必须为大于0的数字");
|
||
}
|
||
|
||
return num / denom;
|
||
}
|
||
|
||
private static bool TryParseDouble(string s, out double value)
|
||
{
|
||
value = 0;
|
||
if (string.IsNullOrWhiteSpace(s))
|
||
{
|
||
return false;
|
||
}
|
||
|
||
return double.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out value)
|
||
|| double.TryParse(s, NumberStyles.Float, CultureInfo.CurrentCulture, out value);
|
||
}
|
||
|
||
private void OnGenerateDrawing()
|
||
{
|
||
try
|
||
{
|
||
var tplParams = CollectTemplateParams();
|
||
|
||
var templateKey = TemplateKeyBuilder.Build(tplParams);
|
||
if (string.IsNullOrWhiteSpace(templateKey))
|
||
{
|
||
AppendLog("模板参数不完整,无法生成 TemplateKey。");
|
||
return;
|
||
}
|
||
|
||
var schemas = TemplateSchemaStore.Load();
|
||
var schema = (schemas?.Items ?? Enumerable.Empty<TemplateSchemaDefinition>())
|
||
.FirstOrDefault(s => string.Equals(s.TemplateKey, templateKey, StringComparison.OrdinalIgnoreCase));
|
||
if (schema == null)
|
||
{
|
||
AppendLog($"未找到该模板的参数绑定配置(TemplateKey={templateKey})。请到【设置→模板参数绑定】中新增/配置。");
|
||
return;
|
||
}
|
||
|
||
if (!TemplateDrawingRegistry.TryResolve(templateKey, out var generator))
|
||
{
|
||
AppendLog($"未找到该模板的图纸生成逻辑(TemplateKey={templateKey})。请先实现对应模板生成器。");
|
||
return;
|
||
}
|
||
|
||
// 调试日志:显示找到的生成器类型
|
||
AppendLog($"[调试] 已找到生成器: {generator.GetType().Name}, TemplateKey={templateKey}");
|
||
|
||
var catalog = ParamCatalogStore.Load();
|
||
CadParamPluging.Common.ParamBag bag;
|
||
// 若用户点击了【读取参数】,则将读取到的参数作为本次弹窗的默认值回填
|
||
using (var f = new DrawingParamsForm(catalog, schema, _lastLoadedBag))
|
||
{
|
||
var r = f.ShowDialog(this);
|
||
if (r != DialogResult.OK)
|
||
{
|
||
AppendLog("用户取消填写参数。");
|
||
return;
|
||
}
|
||
bag = f.Result;
|
||
}
|
||
|
||
// Current demo generator uses a rectangle. Size defaults are handled inside DomainFacade.DrawByParams.
|
||
// Real templates will use their own generator/parameters.
|
||
var drawingParams = new DrawingParams();
|
||
|
||
DomainFacade.ValidateParameters(tplParams, drawingParams);
|
||
|
||
var doc = AcadApp.DocumentManager.MdiActiveDocument;
|
||
if (doc == null)
|
||
{
|
||
AppendLog("未检测到活动图纸,请先打开 CAD 图纸。");
|
||
return;
|
||
}
|
||
|
||
string layoutName = LayoutManager.Current.CurrentLayout;
|
||
// 判断是否模型空间。注意:LayoutName为"Model"通常表示模型空间
|
||
bool isModelSpace = string.Equals(layoutName, "Model", StringComparison.OrdinalIgnoreCase);
|
||
|
||
AppendLog($"正在当前图纸生成: {doc.Name} ({layoutName})");
|
||
|
||
using (doc.LockDocument())
|
||
using (var ctx = new CadContext(doc))
|
||
{
|
||
// 用户要求保留模板配置文字,不执行移除操作
|
||
// var removed = TemplateDrawingService.RemoveMatchParameterAnnotations(
|
||
// ctx,
|
||
// layoutName,
|
||
// isModelSpace);
|
||
// if (removed > 0)
|
||
// {
|
||
// AppendLog($"已移除模板匹配参数标注文本: {removed} 处");
|
||
// }
|
||
|
||
var featureCategoryCount = TemplateDrawingService.ApplyFeatureCategory(
|
||
ctx,
|
||
layoutName,
|
||
isModelSpace,
|
||
bag,
|
||
msg => AppendLog(msg)); // 传递日志回调
|
||
if (featureCategoryCount > 0)
|
||
{
|
||
AppendLog($"已更新特性分类(******): {featureCategoryCount} 处");
|
||
}
|
||
|
||
var noteResult = TemplateDrawingService.ApplyNoteTemplate(
|
||
ctx,
|
||
layoutName,
|
||
isModelSpace,
|
||
schema,
|
||
bag);
|
||
if (noteResult != null)
|
||
{
|
||
if (noteResult.Applied)
|
||
{
|
||
AppendLog($"附注替换成功(目标={noteResult.TargetKind}, 占位符={noteResult.PlaceholderCountInDwg})");
|
||
}
|
||
else if (!string.IsNullOrWhiteSpace(noteResult.Message))
|
||
{
|
||
AppendLog($"附注替换跳过: {noteResult.Message}");
|
||
}
|
||
}
|
||
|
||
// 删除模板中原有的图纸图形(CAXA图层和尺寸标注,保留右上角)
|
||
var removeResult = TemplateDrawingService.RemoveTemplateOriginalDrawing(ctx);
|
||
AppendLog($"已删除原有图形: 红色外框={removeResult.OuterFrameErased}, CAXA图层={removeResult.CaxaLayerErased}, 标注={removeResult.DimensionLayerErased}, 保留右上角={removeResult.DimensionLayerKept}");
|
||
|
||
// 删除白色线框内“上半部分”的所有内容(保留右上角与下半部分附注/表格)
|
||
var upperRemoved = TemplateDrawingService.RemoveWhiteFrameUpperContent(
|
||
ctx,
|
||
keepBottomRatio: 0.50,
|
||
topRightThreshold: 0.70);
|
||
if (upperRemoved > 0)
|
||
{
|
||
AppendLog($"已清理白框上半部内容: {upperRemoved} 处");
|
||
}
|
||
|
||
var scaleFactor = ParseScaleFactor(bag.GetString("DrawingScale"));
|
||
|
||
if (removeResult.WhiteFrameExtents.HasValue && removeResult.CaxaLayerErased == 0)
|
||
{
|
||
AppendLog($"未检测到CAXA层,已切换至白框定位: {removeResult.OriginalCenter}");
|
||
}
|
||
else
|
||
{
|
||
AppendLog($"原图中心点: {removeResult.OriginalCenter}");
|
||
}
|
||
|
||
// ---------------------------------------------------------
|
||
// 智能比例建议 (Pre-calculation)
|
||
// ---------------------------------------------------------
|
||
if (removeResult.WhiteFrameExtents.HasValue)
|
||
{
|
||
var frame = removeResult.WhiteFrameExtents.Value;
|
||
var frameWidth = frame.MaxPoint.X - frame.MinPoint.X;
|
||
var frameHeight = frame.MaxPoint.Y - frame.MinPoint.Y;
|
||
|
||
// 确保 bag 中包含交付状态和工艺方法,以便 CalculateExpectedSize 正确判断示意图模式
|
||
// tplParams.ProjectType = 交付状态(如"车加工"),tplParams.DrawingType = 工艺方法(如"轧制")
|
||
if (string.IsNullOrEmpty(bag.GetString("DeliveryStatus")))
|
||
{
|
||
bag.Set("DeliveryStatus", tplParams.ProjectType ?? string.Empty);
|
||
}
|
||
if (string.IsNullOrEmpty(bag.GetString("ProcessMethod")))
|
||
{
|
||
bag.Set("ProcessMethod", tplParams.DrawingType ?? string.Empty);
|
||
}
|
||
|
||
// 1. 获取预估图形尺寸 (不含标注的纯几何尺寸 + 预留边距)
|
||
var expectedSize = generator.CalculateExpectedSize(bag, tplParams);
|
||
|
||
// 调试:输出关键参数以排查比例建议错误
|
||
var debugDelivery = bag.GetString("DeliveryStatus");
|
||
var debugProcess = bag.GetString("ProcessMethod");
|
||
var debugMinWall = bag.GetDoubleOrNull("MinWallThickness");
|
||
var debugHeight = bag.GetDoubleOrNull("Height1");
|
||
var debugOuterDia = bag.GetDoubleOrNull("OuterDiameter1");
|
||
AppendLog($"[调试] 交付状态={debugDelivery}, 工艺方法={debugProcess}");
|
||
AppendLog($"[调试] 外径D1={debugOuterDia}, 高度H1={debugHeight}, 最小壁厚={debugMinWall}, 结构={tplParams.SheetSize}");
|
||
|
||
if (expectedSize.IsValid)
|
||
{
|
||
var currentDrawW = expectedSize.Width * scaleFactor;
|
||
var currentDrawH = expectedSize.Height * scaleFactor;
|
||
|
||
bool isOverflow = currentDrawW > frameWidth || currentDrawH > frameHeight;
|
||
bool isTooSmall = currentDrawW < frameWidth * 0.3 && currentDrawH < frameHeight * 0.3;
|
||
|
||
// 调试日志
|
||
AppendLog($"[尺寸检查] 白框尺寸: {frameWidth:F0}x{frameHeight:F0}, 预期图形(原始): {expectedSize.Width:F0}x{expectedSize.Height:F0}, 当前比例: 1:{(1/scaleFactor):F1}");
|
||
AppendLog($"[尺寸检查] 预期图形(缩放后): {currentDrawW:F0}x{currentDrawH:F0}, 超出={isOverflow}, 过小={isTooSmall}");
|
||
AppendLog($"[尺寸检查] 判断阈值: 超出条件(W>{frameWidth:F0} || H>{frameHeight:F0}), 过小条件(W<{frameWidth*0.3:F0} && H<{frameHeight*0.3:F0})");
|
||
|
||
if (isOverflow || isTooSmall)
|
||
{
|
||
// 计算建议比例 (目标填充率 65%)
|
||
var ratioW = frameWidth / expectedSize.Width;
|
||
var ratioH = frameHeight / expectedSize.Height;
|
||
var targetScale = Math.Min(ratioW, ratioH) * 0.65;
|
||
|
||
// 格式化建议比例
|
||
string suggestedScaleText;
|
||
double newOneToN = 1.0 / targetScale;
|
||
|
||
// 简单的取整逻辑,优先凑整 1:1, 1:2, 1:5, 1:10 等
|
||
if (targetScale >= 0.95)
|
||
{
|
||
suggestedScaleText = "1:1";
|
||
targetScale = 1.0;
|
||
}
|
||
else
|
||
{
|
||
// 凑整到最近的 0.5 或 整数
|
||
if (newOneToN < 10)
|
||
{
|
||
newOneToN = Math.Round(newOneToN * 2) / 2.0; // 凑整 0.5
|
||
}
|
||
else
|
||
{
|
||
newOneToN = Math.Round(newOneToN); // 凑整 1
|
||
}
|
||
suggestedScaleText = $"1:{newOneToN}";
|
||
targetScale = 1.0 / newOneToN;
|
||
}
|
||
|
||
var msg = isOverflow
|
||
? $"当前比例 (1:{(1/scaleFactor):F1}) 会导致图形超出图纸边框。\n建议调整为 {suggestedScaleText}。\n\n是否应用建议比例?"
|
||
: $"当前比例 (1:{(1/scaleFactor):F1}) 会导致图形过小(占比<30%)。\n建议调整为 {suggestedScaleText}。\n\n是否应用建议比例?";
|
||
|
||
var dialogResult = MessageBox.Show(this, msg, "自动比例建议", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
|
||
|
||
if (dialogResult == DialogResult.Yes)
|
||
{
|
||
scaleFactor = targetScale;
|
||
bag.Set("DrawingScale", suggestedScaleText); // 更新参数包中的比例字符串
|
||
AppendLog($"[自动调整] 已应用建议比例: {suggestedScaleText}");
|
||
}
|
||
else
|
||
{
|
||
AppendLog($"[自动调整] 用户忽略了比例建议,保持原比例。");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
AppendLog($"绘图参数: Type={tplParams.ProjectType}, Size={tplParams.SheetSize}, Scale={bag.GetString("DrawingScale")}, Method={tplParams.DrawingType}");
|
||
|
||
// Dubug: Dump bag keys
|
||
var bagKeys = bag.GetKeys(); // Assuming GetKeys exists? If not, skip or use reflection?
|
||
// ParamBag implementation is simple dictionary usually.
|
||
// Let's just log specific keys we suspect.
|
||
AppendLog($"Bag Keys check: OD1={bag.GetDoubleOrNull("OuterDiameter1")}, H1={bag.GetDoubleOrNull("Height1")}");
|
||
|
||
// 方轴参数检查
|
||
AppendLog($"[方轴参数] Size1={bag.GetDoubleOrNull("SquareShaftSize1")}, Size2={bag.GetDoubleOrNull("SquareShaftSize2")}, Size3={bag.GetDoubleOrNull("SquareShaftSize3")}");
|
||
|
||
// 根据模板参数绘制半剖视图,居中放置于原图纸位置
|
||
// 使用特征驱动模式:根据参数存在性动态绘制对应特征
|
||
AppendLog($"[绘图] 准备调用 generator.Draw, scaleFactor={scaleFactor}");
|
||
generator.Draw(
|
||
ctx,
|
||
bag,
|
||
tplParams,
|
||
removeResult.OriginalCenter,
|
||
scaleFactor);
|
||
AppendLog($"[绘图] generator.Draw 调用完成");
|
||
|
||
// Force Zoom Extents to ensure visibility
|
||
try { doc.SendStringToExecute("._ZOOM _E ", true, false, false); } catch { }
|
||
|
||
// 更新标题栏中的比例属性
|
||
var scaleUpdated = TemplateDrawingService.UpdateScaleAttribute(
|
||
ctx,
|
||
layoutName,
|
||
isModelSpace,
|
||
scaleFactor
|
||
);
|
||
if (scaleUpdated)
|
||
{
|
||
AppendLog($"已更新标题栏比例: {(scaleFactor >= 0.9999 ? "1:1" : $"1:{(1/scaleFactor):F1}")}");
|
||
}
|
||
else
|
||
{
|
||
AppendLog("未找到标题栏中的'比例'文字,无法更新比例");
|
||
}
|
||
|
||
// 更新标题栏中的检验类别
|
||
var inspectionCategory = bag.GetString("InspectionCategory");
|
||
if (!string.IsNullOrWhiteSpace(inspectionCategory))
|
||
{
|
||
var categoryUpdated = TemplateDrawingService.UpdateInspectionCategoryAttribute(
|
||
ctx,
|
||
layoutName,
|
||
isModelSpace,
|
||
inspectionCategory
|
||
);
|
||
if (categoryUpdated)
|
||
{
|
||
AppendLog($"已更新标题栏检验类别: {inspectionCategory}");
|
||
}
|
||
else
|
||
{
|
||
AppendLog("未找到标题栏中的'检验类别'文字,无法更新检验类别");
|
||
}
|
||
}
|
||
|
||
// 添加左上角"内部资料,控制范围"标记
|
||
var labelAdded = TemplateDrawingService.AddInternalMaterialLabel(
|
||
ctx, layoutName, isModelSpace);
|
||
if (labelAdded)
|
||
{
|
||
AppendLog("已添加内部资料标记");
|
||
}
|
||
|
||
ctx.Commit();
|
||
|
||
try { doc.Editor.Regen(); } catch { }
|
||
|
||
// 保存参数到图纸内部 (Extension Dictionary / NOD)
|
||
try
|
||
{
|
||
var bagToSave = bag;
|
||
|
||
|
||
// 保存面板选项
|
||
bagToSave.Set("ProjectType", tplParams.ProjectType);
|
||
bagToSave.Set("DrawingType", tplParams.DrawingType);
|
||
bagToSave.Set("SheetSize", tplParams.SheetSize);
|
||
bagToSave.Set("SpecialCondition", tplParams.Scale); // tplParams.Scale 对应 _cbScale (特殊条件)
|
||
|
||
DictionaryHelper.SaveParams(doc.Database, bagToSave);
|
||
AppendLog("参数已保存到图纸内部数据中。");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
AppendLog($"保存内部参数失败: {ex.Message}");
|
||
Logger.Error("SaveParamsInternal", ex);
|
||
}
|
||
}
|
||
|
||
// --------- 新增:提示用户保存参数文档 ---------
|
||
using (var saveDialog = new SaveFileDialog())
|
||
{
|
||
saveDialog.Filter = "文本文件 (*.txt)|*.txt";
|
||
string docName = System.IO.Path.GetFileNameWithoutExtension(doc.Name);
|
||
saveDialog.FileName = $"{docName}_生成参数.txt";
|
||
|
||
if (saveDialog.ShowDialog() == DialogResult.OK)
|
||
{
|
||
try
|
||
{
|
||
var sb = new System.Text.StringBuilder();
|
||
sb.AppendLine("========== CAD图纸生成参数 ==========");
|
||
sb.AppendLine($"生成时间: {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
|
||
sb.AppendLine($"关联图纸: {docName}");
|
||
sb.AppendLine("------------------------------------");
|
||
|
||
// 从 catalog 中获取中文名称映射
|
||
var catalogForSave = ParamCatalogStore.Load();
|
||
|
||
// 这里的 bag 已经在上面收集完成
|
||
foreach (var item in bag.Items)
|
||
{
|
||
if (!string.IsNullOrWhiteSpace(item.Value))
|
||
{
|
||
var def = catalogForSave?.FindByKey(item.Key);
|
||
string label = def != null ? def.Label : item.Key;
|
||
sb.AppendLine($"{label}: {item.Value}");
|
||
}
|
||
}
|
||
|
||
sb.AppendLine("====================================");
|
||
|
||
System.IO.File.WriteAllText(saveDialog.FileName, sb.ToString(), System.Text.Encoding.UTF8);
|
||
AppendLog($"参数文档已保存至: {saveDialog.FileName}");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
AppendLog($"保存参数文档失败: {ex.Message}");
|
||
}
|
||
}
|
||
}
|
||
// ---------------------------------------------
|
||
|
||
AppendLog("图纸生成完成。");
|
||
}
|
||
catch (BusinessException bex)
|
||
{
|
||
AppendLog($"参数错误: {bex.Message}");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
AppendLog($"生成失败: {ex.Message}");
|
||
Logger.Error("GenerateDrawing", ex);
|
||
}
|
||
}
|
||
|
||
private void OnSaveDrawing()
|
||
{
|
||
try
|
||
{
|
||
var doc = AcadApp.DocumentManager.MdiActiveDocument;
|
||
if (doc == null)
|
||
{
|
||
AppendLog("未找到活动图纸,无法保存。");
|
||
return;
|
||
}
|
||
|
||
using (var dialog = new SaveFileDialog
|
||
{
|
||
Filter = "AutoCAD Drawing (*.dwg)|*.dwg",
|
||
FileName = "新图纸.dwg"
|
||
})
|
||
{
|
||
if (dialog.ShowDialog() == DialogResult.OK)
|
||
{
|
||
doc.Database.SaveAs(dialog.FileName, true, DwgVersion.Current, doc.Database.SecurityParameters);
|
||
AppendLog($"图纸已保存到: {dialog.FileName}");
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
AppendLog($"保存失败: {ex.Message}");
|
||
Logger.Error("SaveDrawing", ex);
|
||
}
|
||
}
|
||
|
||
private void OnOpenSettings()
|
||
{
|
||
try
|
||
{
|
||
var current = DropdownOptionsStore.Load();
|
||
using (var form = new SettingsForm(current))
|
||
{
|
||
if (form.ShowDialog() == DialogResult.OK)
|
||
{
|
||
DropdownOptionsStore.Save(form.Result);
|
||
ReloadDropdownOptions(form.Result);
|
||
AppendLog("设置已保存");
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
AppendLog($"打开设置失败: {ex.Message}");
|
||
Logger.Error("OpenSettings", ex);
|
||
}
|
||
}
|
||
|
||
private void OnLoadParams()
|
||
{
|
||
try
|
||
{
|
||
var doc = AcadApp.DocumentManager.MdiActiveDocument;
|
||
if (doc == null)
|
||
{
|
||
AppendLog("未找到活动图纸,无法读取参数。");
|
||
return;
|
||
}
|
||
|
||
ParamBag bag;
|
||
using (doc.LockDocument())
|
||
{
|
||
bag = DictionaryHelper.ReadParams(doc.Database);
|
||
}
|
||
|
||
if (bag == null || bag.Items == null || bag.Items.Count == 0)
|
||
{
|
||
AppendLog("当前图纸未找到存储的参数(或非本插件生成)。");
|
||
return;
|
||
}
|
||
|
||
|
||
|
||
// 2. 恢复下拉框选项
|
||
// ProjectType
|
||
TrySelectRequest(bag, "ProjectType", _cbProjectType);
|
||
// DrawingType
|
||
TrySelectRequest(bag, "DrawingType", _cbDrawingType);
|
||
// SheetSize
|
||
TrySelectRequest(bag, "SheetSize", _cbSheetSize);
|
||
// Scale (DrawingScale) - 注意我们下拉框叫 _cbScale,参数里可能是 DrawingScale
|
||
var scaleVal = bag.GetString("DrawingScale");
|
||
// 特殊条件/Scale 其实是一个 ComboBox,通常用于 SpecialConditions
|
||
// 但 DrawingScale 是在生成时 Parse 出来的。
|
||
// 这里的 _cbScale 绑定的是 options.SpecialConditions (e.g. "中心冲孔") 还是 "1:50"?
|
||
// 回看代码 reset _cbScale 绑定的是 options.SpecialConditions (ResetComboBoxItems(_cbScale, options.SpecialConditions))
|
||
// 而 "scale" 是指比例。
|
||
// 观察 GenerateDrawing 逻辑:
|
||
// var tplParams = CollectTemplateParams(); -> Scale = _cbScale.Text
|
||
// 这个 _cbScale 存的是“特殊条件”。
|
||
// 真正的比例是在 Bag.GetString("DrawingScale"),这是在 DrawParamsForm 里填写的。
|
||
//
|
||
// 等等!看 Readme 和 UI 逻辑,主面板只有 4 个下拉框: Project/Drawing/SheetSize/Scale
|
||
// 其中 _cbScale 有时候被用作 Scale,但 DropdownOptions 绑定的是 SpecialConditions?
|
||
// Let's check ReloadDropdownOptions:
|
||
// ResetComboBoxItems(_cbScale, options.SpecialConditions); -> 所以 _cbScale 是“特殊条件”!
|
||
// 那比例去哪了?
|
||
// Readme 说 "ComboBox / TextBox:比例(Scale)"
|
||
// 让我们确认 CollectTemplateParams: Scale = _cbScale.Text (specialCondition)
|
||
// 看来目前主面板并没有“比例”输入框?比例是在 Form 里填写的?
|
||
// 检查 OnGenerateDrawing -> new DrawingParamsForm(catalog, schema) -> 这个 Form 里填写的具体参数。
|
||
//
|
||
// 所以,OnLoadParams 只能恢复 面板上的 4 个下拉框。
|
||
// 而 Form 里的参数(如 OuterDiameter, DrawingScale)需要在点“生成”后,弹出 Form 时自动填入。
|
||
// 这需要修改 DrawingParamsForm 的构造函数或者逻辑,让它支持“默认值”从 Bag 中覆盖。
|
||
|
||
TrySelectRequest(bag, "SpecialCondition", _cbScale); // 假设存的时候 key 是 SpecialCondition?
|
||
// CollectTemplateParams 里没有把 template params 放入 bag。
|
||
// 但是!DomainFacade.ValidateParameters 可能会用到。
|
||
// 关键点:TemplateParams 只是用来匹配 TemplateKey。
|
||
// 实际生成图纸时,OnGenerateDrawing 收集了 tplParams,然后 Build TemplateKey,然后 Load Schema,然后...
|
||
// 用 Form 填写了 Bag。
|
||
// 所以“项目类型/图纸类型”等并没有被存入 Bag (除非 Form 里也有对应字段)。
|
||
// 这是一个问题:Bag 主要是 Form 的结果。面板上的选择决定了 TemplateKey。
|
||
// 我们在 SaveParamsToExtensionDictionary 时,只存了 Bag。
|
||
// 如果面板上的选项(项目类型等)没在 Bag 里,我们就无法恢复面板状态!
|
||
//
|
||
// 补救措施:必须在 SaveParams 时把 tplParams 的内容也塞进 Bag。
|
||
// 我刚刚加了 _TemplatePath/_LayoutName,但漏了 ProjectType 等。
|
||
//
|
||
// 既然现在代码还没运行,我可以再加几个 Set。
|
||
|
||
AppendLog("参数读取完成。请点击【生成图纸】继续修改详细参数。");
|
||
|
||
// 将读取到的 Bag 暂存,以便 DrawingParamsForm 打开时使用它作为默认值
|
||
_lastLoadedBag = bag;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
AppendLog($"读取参数失败: {ex.Message}");
|
||
Logger.Error("LoadParams", ex);
|
||
}
|
||
}
|
||
|
||
private ParamBag _lastLoadedBag; // 暂存读取到的参数
|
||
|
||
private void TrySelectRequest(ParamBag bag, string key, ComboBox cb)
|
||
{
|
||
var val = bag.GetString(key);
|
||
if (!string.IsNullOrEmpty(val))
|
||
{
|
||
// 尝试选中
|
||
for (int i = 0; i < cb.Items.Count; i++)
|
||
{
|
||
if (cb.Items[i] is DropdownItem item && string.Equals(item.Value, val, StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
cb.SelectedIndex = i;
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
private void ReloadDropdownOptions(DropdownOptions options)
|
||
{
|
||
if (options == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
ResetComboBoxItems(_cbProjectType, options.DeliveryStatuses);
|
||
ResetComboBoxItems(_cbDrawingType, options.ProcessMethods);
|
||
ResetComboBoxItems(_cbSheetSize, options.StructuralFeatures);
|
||
ResetComboBoxItems(_cbScale, options.SpecialConditions);
|
||
}
|
||
|
||
private TemplateParams CollectTemplateParams()
|
||
{
|
||
var specialCondition = GetComboBoxValue(_cbScale);
|
||
if (string.Equals(specialCondition, "无", StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
specialCondition = null;
|
||
}
|
||
|
||
return new TemplateParams
|
||
{
|
||
ProjectType = GetComboBoxValue(_cbProjectType),
|
||
DrawingType = GetComboBoxValue(_cbDrawingType),
|
||
SheetSize = GetComboBoxValue(_cbSheetSize),
|
||
Scale = specialCondition
|
||
};
|
||
}
|
||
|
||
private class DropdownItem
|
||
{
|
||
public string Display { get; set; }
|
||
public string Value { get; set; }
|
||
public override string ToString() => Display;
|
||
}
|
||
|
||
private static string GetComboBoxValue(ComboBox cb)
|
||
{
|
||
if (cb.SelectedItem is DropdownItem item)
|
||
{
|
||
return item.Value;
|
||
}
|
||
return cb.Text?.Trim();
|
||
}
|
||
|
||
private static ComboBox CreateComboBox(System.Collections.Generic.IEnumerable<string> items)
|
||
{
|
||
var cb = new ComboBox
|
||
{
|
||
DropDownStyle = ComboBoxStyle.DropDownList,
|
||
Width = 160,
|
||
DrawMode = DrawMode.OwnerDrawFixed
|
||
};
|
||
|
||
var toolTip = new ToolTip();
|
||
cb.DrawItem += (s, e) =>
|
||
{
|
||
if (e.Index < 0) return;
|
||
e.DrawBackground();
|
||
var text = cb.GetItemText(cb.Items[e.Index]);
|
||
TextRenderer.DrawText(e.Graphics, text, e.Font, e.Bounds, e.ForeColor, TextFormatFlags.Left | TextFormatFlags.VerticalCenter);
|
||
e.DrawFocusRectangle();
|
||
|
||
if (cb.DroppedDown && (e.State & DrawItemState.Selected) == DrawItemState.Selected)
|
||
{
|
||
int lastHovered = cb.Tag is int i ? i : -1;
|
||
if (lastHovered != e.Index)
|
||
{
|
||
cb.Tag = e.Index;
|
||
var point = cb.PointToClient(Cursor.Position);
|
||
point.Y += 20;
|
||
toolTip.Show(text, cb, point.X, point.Y);
|
||
}
|
||
}
|
||
};
|
||
cb.DropDownClosed += (s, e) =>
|
||
{
|
||
toolTip.Hide(cb);
|
||
cb.Tag = -1;
|
||
};
|
||
|
||
ResetComboBoxItems(cb, items);
|
||
return cb;
|
||
}
|
||
|
||
private static void ResetComboBoxItems(ComboBox cb, System.Collections.Generic.IEnumerable<string> items)
|
||
{
|
||
if (cb == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
var previousValue = GetComboBoxValue(cb);
|
||
var arr = (items ?? Enumerable.Empty<string>()).Where(s => !string.IsNullOrWhiteSpace(s)).Select(s => s.Trim()).ToArray();
|
||
|
||
cb.BeginUpdate();
|
||
try
|
||
{
|
||
cb.Items.Clear();
|
||
if (arr.Length > 0)
|
||
{
|
||
var dropDownItems = new System.Collections.Generic.List<DropdownItem>();
|
||
foreach (var item in arr)
|
||
{
|
||
string display = item;
|
||
if (item == "中心冲孔")
|
||
{
|
||
display = "无扩孔";
|
||
}
|
||
else if (item == "非中心冲孔" || item == "非中心冲孔有扩孔")
|
||
{
|
||
display = "有扩孔";
|
||
}
|
||
dropDownItems.Add(new DropdownItem { Display = display, Value = item });
|
||
}
|
||
cb.Items.AddRange(dropDownItems.ToArray());
|
||
|
||
var idx = dropDownItems.FindIndex(v => string.Equals(v.Value, previousValue, StringComparison.OrdinalIgnoreCase));
|
||
cb.SelectedIndex = idx >= 0 ? idx : 0;
|
||
}
|
||
}
|
||
finally
|
||
{
|
||
cb.EndUpdate();
|
||
}
|
||
}
|
||
|
||
private void OnTestDraw()
|
||
{
|
||
try
|
||
{
|
||
// ==========================================
|
||
// 测试参数构造 - 演示特征驱动模式
|
||
// ==========================================
|
||
var bag = new ParamBag();
|
||
|
||
// 锻件参数 (调整为细长型,符合参考图比例)
|
||
bag.Set("OuterDiameter1", "160");
|
||
bag.Set("OuterDiameter1TolPlus", "2");
|
||
bag.Set("OuterDiameter1TolMinus", "-2");
|
||
bag.Set("InnerDiameter2", "100");
|
||
bag.Set("InnerDiameter2TolPlus", "3");
|
||
bag.Set("InnerDiameter2TolMinus", "-3");
|
||
bag.Set("Height1", "400");
|
||
bag.Set("Height1TolPlus", "5");
|
||
bag.Set("Height1TolMinus", "-5");
|
||
|
||
// 零件参数 (车加工)
|
||
bag.Set("OuterDiameter1Prime", "150");
|
||
bag.Set("InnerDiameter2Prime", "110");
|
||
bag.Set("Height1Prime", "380");
|
||
|
||
// 可选特征参数 - 根据模板绑定动态显示
|
||
bag.Set("UnspecifiedFilletRadiusMax", "6"); // 未注圆角半径R≤6
|
||
bag.Set("MinWallThickness", "25"); // 最小壁厚T
|
||
// bag.Set("InnerRadiusMax", "8"); // 内径半径R≤8(中心冲孔时用)
|
||
|
||
var doc = AcadApp.DocumentManager.MdiActiveDocument;
|
||
if (doc == null)
|
||
{
|
||
AppendLog("未找到活动图纸。");
|
||
return;
|
||
}
|
||
|
||
using (doc.LockDocument())
|
||
using (var ctx = new CadContext(doc))
|
||
{
|
||
// 调用特征驱动绘图引擎
|
||
// 会根据bag中存在的参数自动绘制对应特征
|
||
HalfSectionDrawer.Draw(
|
||
ctx,
|
||
bag,
|
||
"车加工", // 交付状态
|
||
"环形", // 结构特征
|
||
null // 特殊条件(可选:中心冲孔/有圆头等)
|
||
);
|
||
|
||
ctx.Commit();
|
||
|
||
AppendLog($"测试绘制完成 (特征驱动模式)");
|
||
AppendLog($"已绘制特征: 外轮廓、内孔(虚线)、公差标注、未注圆角、壁厚、零件(填充)");
|
||
}
|
||
|
||
// Zoom Extents
|
||
doc.SendStringToExecute("._ZOOM _E ", true, false, false);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
AppendLog($"测试绘制失败: {ex.Message}");
|
||
Logger.Error("TestDraw", ex);
|
||
}
|
||
}
|
||
|
||
private void AppendLog(string message)
|
||
{
|
||
var time = DateTime.Now.ToString("HH:mm:ss");
|
||
_txtLog.AppendText($"[{time}] {message}{Environment.NewLine}");
|
||
}
|
||
}
|
||
}
|