CadParamPluging/UI/ParamDrawingPanel.cs

766 lines
28 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 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 Label _lblTemplateInfo;
private TextBox _txtLog;
private TextBox _txtTemplatePath;
private string _selectedFolderPath;
private string[] _cadFilePaths = new string[0];
private TemplateInfo _selectedTemplate;
private Extents3d? _selectedModelWindow;
private string _selectedSheetName;
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);
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 = 6,
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);
var btnSelect = new Button { Text = "选择路径", AutoSize = true };
btnSelect.Click += (_, __) => OnSelectTemplate();
_txtTemplatePath = new TextBox { Width = 160, ReadOnly = true };
grid.Controls.Add(btnSelect, 0, 4);
grid.Controls.Add(_txtTemplatePath, 1, 4);
var btnMatch = new Button { Text = "匹配模板", AutoSize = true };
btnMatch.Click += (_, __) => OnMatchTemplate();
_lblTemplateInfo = new Label
{
Text = "未匹配模板",
AutoSize = true,
ForeColor = Color.DarkBlue,
TextAlign = ContentAlignment.MiddleLeft
};
grid.Controls.Add(btnMatch, 0, 5);
grid.Controls.Add(_lblTemplateInfo, 1, 5);
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();
panel.Controls.Add(btnGenerate);
panel.Controls.Add(btnSaveAs);
panel.Controls.Add(btnSettings);
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 void OnMatchTemplate()
{
try
{
if (_cadFilePaths == null || _cadFilePaths.Length == 0)
{
AppendLog("请先选择路径。");
return;
}
AppendLog("开始匹配模板...");
var tplParams = CollectTemplateParams();
foreach (var cadPath in _cadFilePaths)
{
if (string.IsNullOrWhiteSpace(cadPath) || !File.Exists(cadPath))
{
continue;
}
// 1) 优先按 Layout(PaperSpace) 匹配
var layoutCandidates = TemplateLayoutAnnotationExtractor.ExtractPerLayout(cadPath);
var layoutMatch = layoutCandidates.FirstOrDefault(c => LayoutMatches(c, tplParams));
if (layoutMatch != null)
{
_selectedTemplate = new TemplateInfo
{
Name = Path.GetFileName(cadPath),
FilePath = cadPath
};
_selectedTemplate.LayoutName = layoutMatch.LayoutName;
_selectedModelWindow = null;
_selectedSheetName = layoutMatch.LayoutName;
_lblTemplateInfo.Text = $"{cadPath} | {layoutMatch.LayoutName}";
AppendLog($"匹配成功: {Path.GetFileName(cadPath)} | Layout: {layoutMatch.LayoutName}");
return;
}
// 2) Layout 未命中时,尝试按 ModelSpace 窗口候选匹配
var modelCandidates = TemplateModelSheetExtractor.ExtractCandidates(cadPath);
var modelMatch = modelCandidates.FirstOrDefault(c => ModelMatches(c, tplParams));
if (modelMatch != null)
{
_selectedTemplate = new TemplateInfo
{
Name = Path.GetFileName(cadPath),
FilePath = cadPath
};
_selectedTemplate.LayoutName = null;
_selectedModelWindow = modelMatch.Window;
_selectedSheetName = modelMatch.Name;
_lblTemplateInfo.Text = $"{cadPath} | {modelMatch.Name}";
AppendLog($"匹配成功: {Path.GetFileName(cadPath)} | Model: {modelMatch.Name}");
return;
}
}
_selectedTemplate = null;
_selectedModelWindow = null;
_selectedSheetName = null;
_lblTemplateInfo.Text = "未匹配模板";
AppendLog("未找到匹配模板。");
}
catch (BusinessException bex)
{
if (_selectedTemplate != null)
{
_selectedTemplate.LayoutName = null;
}
_selectedModelWindow = null;
_selectedSheetName = null;
_lblTemplateInfo.Text = _selectedTemplate?.FilePath ?? "未匹配图纸";
AppendLog($"图纸匹配错误: {bex.Message}");
}
catch (Exception ex)
{
if (_selectedTemplate != null)
{
_selectedTemplate.LayoutName = null;
}
_selectedModelWindow = null;
_selectedSheetName = null;
_lblTemplateInfo.Text = _selectedTemplate?.FilePath ?? "未匹配图纸";
AppendLog($"匹配失败: {ex.Message}");
Logger.Error("MatchLayout", ex);
}
}
private static bool LayoutMatches(LayoutAnnotation ann, TemplateParams tplParams)
{
if (ann == null || tplParams == null)
{
return false;
}
return ContainsIgnoreCase(ann.DeliveryStatuses, tplParams.ProjectType)
&& ContainsIgnoreCase(ann.ProcessMethods, tplParams.DrawingType)
&& ContainsIgnoreCase(ann.StructuralFeatures, tplParams.SheetSize)
&& ContainsIgnoreCase(ann.SpecialConditions, tplParams.Scale);
}
private static bool ContainsIgnoreCase(System.Collections.Generic.IEnumerable<string> values, string expected)
{
if (string.IsNullOrWhiteSpace(expected))
{
return true;
}
if (values == null)
{
return false;
}
var exp = NormalizeValue(expected);
return values.Any(v => string.Equals(NormalizeValue(v), exp, StringComparison.OrdinalIgnoreCase));
}
private static string NormalizeValue(string s)
{
if (string.IsNullOrWhiteSpace(s))
{
return string.Empty;
}
var trimmed = s.Trim();
var chars = trimmed.Where(c => !char.IsWhiteSpace(c)).ToArray();
return new string(chars);
}
private static string Preview(System.Collections.Generic.IEnumerable<string> values)
{
if (values == null)
{
return "(无)";
}
var arr = values.Where(v => !string.IsNullOrWhiteSpace(v)).Select(v => v.Trim()).ToArray();
return arr.Length == 0 ? "(无)" : string.Join("/", arr);
}
private static bool ModelMatches(ModelSheetCandidate ann, TemplateParams tplParams)
{
if (ann == null || tplParams == null)
{
return false;
}
return ContainsIgnoreCase(ann.DeliveryStatuses, tplParams.ProjectType)
&& ContainsIgnoreCase(ann.ProcessMethods, tplParams.DrawingType)
&& ContainsIgnoreCase(ann.StructuralFeatures, tplParams.SheetSize)
&& ContainsIgnoreCase(ann.SpecialConditions, tplParams.Scale);
}
private void OnSelectTemplate()
{
try
{
using (var dialog = new FolderBrowserDialog())
{
dialog.Description = "选择包含 CAD 文件的文件夹";
if (!string.IsNullOrWhiteSpace(_selectedFolderPath) && Directory.Exists(_selectedFolderPath))
{
dialog.SelectedPath = _selectedFolderPath;
}
if (dialog.ShowDialog(this) == DialogResult.OK)
{
var folderPath = dialog.SelectedPath;
if (string.IsNullOrWhiteSpace(folderPath) || !Directory.Exists(folderPath))
{
AppendLog("选择路径无效。");
return;
}
_selectedFolderPath = folderPath;
_txtTemplatePath.Text = folderPath;
_cadFilePaths = LoadCadFilesFromFolder(folderPath);
AppendLog($"已选择路径: {folderPath}");
AppendLog($"检测到 CAD 文件数量: {_cadFilePaths.Length}");
foreach (var p in _cadFilePaths.Take(10))
{
AppendLog($"- {Path.GetFileName(p)}");
}
if (_cadFilePaths.Length > 10)
{
AppendLog($"... 还有 {_cadFilePaths.Length - 10} 个文件");
}
}
}
}
catch (Exception ex)
{
AppendLog($"选择路径失败: {ex.Message}");
Logger.Error("SelectCadFolder", ex);
}
}
private static string[] LoadCadFilesFromFolder(string folderPath)
{
if (string.IsNullOrWhiteSpace(folderPath) || !Directory.Exists(folderPath))
{
return new string[0];
}
var patterns = new[] { "*.dwg", "*.dwt", "*.dxf" };
return patterns
.SelectMany(p => Directory.EnumerateFiles(folderPath, p, SearchOption.TopDirectoryOnly))
.Distinct(StringComparer.OrdinalIgnoreCase)
.OrderBy(p => p, StringComparer.OrdinalIgnoreCase)
.ToArray();
}
private void TryReplaceDropdownOptionsFromTemplate(string templatePath)
{
try
{
var result = TemplateAnnotationOptionsExtractor.Extract(templatePath);
var options = result?.Options;
if (options == null || !HasAnyOptions(options))
{
AppendLog("未从模板标注解析到参数,下拉列表不变。");
return;
}
AppendLog("已从模板标注解析到参数,询问是否替换下拉设置。");
var preview = BuildOptionsPreview(options, 5);
var confirm = MessageBox.Show(
this,
$"检测到模板参数(去重后):\n\n{preview}\n\n是否用模板参数替换当前下拉列表并保存\n特殊条件不从模板解析将保持当前设置",
"模板参数",
MessageBoxButtons.YesNo,
MessageBoxIcon.Question);
if (confirm != DialogResult.Yes)
{
AppendLog("用户取消替换下拉设置。");
return;
}
var current = DropdownOptionsStore.Load();
var updated = (current ?? DropdownOptions.CreateDefault()).Clone();
if (options.DeliveryStatuses != null && options.DeliveryStatuses.Count > 0)
{
updated.DeliveryStatuses = options.DeliveryStatuses.ToList();
}
if (options.ProcessMethods != null && options.ProcessMethods.Count > 0)
{
updated.ProcessMethods = options.ProcessMethods.ToList();
}
if (options.StructuralFeatures != null && options.StructuralFeatures.Count > 0)
{
updated.StructuralFeatures = options.StructuralFeatures.ToList();
}
updated.Normalize();
DropdownOptionsStore.Save(updated);
ReloadDropdownOptions(updated);
AppendLog("已从模板替换下拉设置并保存。");
}
catch (Exception ex)
{
AppendLog($"模板解析/替换失败: {ex.Message}");
Logger.Error("TemplateDropdownOptions", ex);
}
}
private static bool HasAnyOptions(DropdownOptions options)
{
return options != null
&& ((options.DeliveryStatuses != null && options.DeliveryStatuses.Count > 0)
|| (options.ProcessMethods != null && options.ProcessMethods.Count > 0)
|| (options.StructuralFeatures != null && options.StructuralFeatures.Count > 0));
}
private static string BuildOptionsPreview(DropdownOptions options, int maxPerField)
{
return string.Join(
Environment.NewLine,
new[]
{
$"交付状态: {PreviewValues(options?.DeliveryStatuses, maxPerField)}",
$"工艺方法: {PreviewValues(options?.ProcessMethods, maxPerField)}",
$"结构特征: {PreviewValues(options?.StructuralFeatures, maxPerField)}"
});
}
private static string PreviewValues(System.Collections.Generic.IEnumerable<string> values, int max)
{
if (values == null)
{
return "(无)";
}
var arr = values.Where(v => !string.IsNullOrWhiteSpace(v)).Select(v => v.Trim()).ToArray();
if (arr.Length == 0)
{
return "(无)";
}
if (arr.Length <= max)
{
return string.Join(", ", arr);
}
return string.Join(", ", arr.Take(max)) + $" ... (共 {arr.Length} 项)";
}
private void OnGenerateDrawing()
{
try
{
var tplParams = CollectTemplateParams();
if (_selectedTemplate == null || string.IsNullOrWhiteSpace(_selectedTemplate.FilePath))
{
AppendLog("请先选择路径并匹配模板。");
return;
}
if (string.IsNullOrWhiteSpace(_selectedTemplate.LayoutName) && !_selectedModelWindow.HasValue)
{
AppendLog("未匹配图纸,尝试自动匹配...");
OnMatchTemplate();
if (_selectedTemplate == null || (string.IsNullOrWhiteSpace(_selectedTemplate.LayoutName) && !_selectedModelWindow.HasValue))
{
return;
}
}
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;
}
var catalog = ParamCatalogStore.Load();
CadParamPluging.Common.ParamBag bag;
using (var f = new DrawingParamsForm(catalog, schema))
{
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 = TemplateDrawingService.CreateDocumentFromTemplate(_selectedTemplate);
if (_selectedModelWindow.HasValue)
{
TemplateDrawingService.KeepOnlyModelWindow(doc, _selectedModelWindow.Value);
}
else
{
TemplateDrawingService.KeepOnlyLayout(doc, _selectedTemplate.LayoutName);
}
AppendLog($"已生成图纸窗口,并仅保留图纸: {_selectedSheetName ?? _selectedTemplate.LayoutName}");
using (doc.LockDocument())
using (var ctx = new CadContext(doc))
{
var removed = TemplateDrawingService.RemoveMatchParameterAnnotations(
ctx,
_selectedTemplate.LayoutName,
_selectedModelWindow.HasValue);
if (removed > 0)
{
AppendLog($"已移除模板匹配参数标注文本: {removed} 处");
}
var noteResult = TemplateDrawingService.ApplyNoteTemplate(
ctx,
_selectedTemplate.LayoutName,
_selectedModelWindow.HasValue,
schema,
bag);
if (noteResult != null)
{
if (noteResult.Applied)
{
AppendLog($"附注替换成功(目标={noteResult.TargetKind}, 占位符={noteResult.PlaceholderCountInDwg}");
}
else if (!string.IsNullOrWhiteSpace(noteResult.Message))
{
AppendLog($"附注替换跳过: {noteResult.Message}");
}
}
var cadService = new CadDrawingService(ctx);
DomainFacade.DrawByParams(_selectedTemplate, drawingParams, cadService);
ctx.Commit();
}
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 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 = _cbScale.Text?.Trim();
if (string.Equals(specialCondition, "无", StringComparison.OrdinalIgnoreCase))
{
specialCondition = null;
}
return new TemplateParams
{
ProjectType = _cbProjectType.Text?.Trim(),
DrawingType = _cbDrawingType.Text?.Trim(),
SheetSize = _cbSheetSize.Text?.Trim(),
Scale = specialCondition
};
}
private static ComboBox CreateComboBox(System.Collections.Generic.IEnumerable<string> items)
{
var cb = new ComboBox
{
DropDownStyle = ComboBoxStyle.DropDownList,
Width = 160
};
var arr = (items ?? Enumerable.Empty<string>()).Where(s => !string.IsNullOrWhiteSpace(s)).Select(s => s.Trim()).ToArray();
if (arr.Length > 0)
{
cb.Items.AddRange(arr);
}
if (cb.Items.Count > 0)
{
cb.SelectedIndex = 0;
}
return cb;
}
private static void ResetComboBoxItems(ComboBox cb, System.Collections.Generic.IEnumerable<string> items)
{
if (cb == null)
{
return;
}
var previous = cb.Text;
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)
{
cb.Items.AddRange(arr);
var idx = Array.FindIndex(arr, v => string.Equals(v, previous, StringComparison.OrdinalIgnoreCase));
cb.SelectedIndex = idx >= 0 ? idx : 0;
}
}
finally
{
cb.EndUpdate();
}
}
private void AppendLog(string message)
{
var time = DateTime.Now.ToString("HH:mm:ss");
_txtLog.AppendText($"[{time}] {message}{Environment.NewLine}");
}
}
}