Files
SzmediTools/Szmedi.RvKits/InfoManager/EAMTools/EAMCodingViewModel.cs
2025-12-23 21:37:02 +08:00

538 lines
19 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.Collections;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Windows;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using EPPlus.Core.Extensions;
using Microsoft.Win32;
using Nice3point.Revit.Toolkit.External.Handlers;
using OfficeOpenXml;
using Szmedi.RvKits.Assists;
namespace Szmedi.RvKits.InfoManager.EAMTools;
public partial class EAMCodingViewModel : ObservableObject
{
public EAMCodingViewModel(UIDocument uidoc)
{
this.uidoc = uidoc;
Collection = GetCategoriesFromDocument();
//Instances = (from familyInstance in ins let facility = new InstanceFacility(familyInstance) select facility).ToList();
}
private readonly ActionEventHandler showFacilitySectionBox = new();
private readonly ActionEventHandler writeCodeByMappedInstances = new();
private readonly ActionEventHandler writeCodeInstances = new();
private readonly UIDocument uidoc;
[ObservableProperty]
private Dictionary<string, List<FamilyInstance>> collection;
[ObservableProperty]
private bool isFuzzyMatch;
/// <summary>
/// 是否经过楼层映射
/// </summary>
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(WriteCodeToInstancesMappedCommand))]
[NotifyCanExecuteChangedFor(nameof(ExportExcelCommand))]
private bool isMapper;
/// <summary>
/// 是否经过比对匹配
/// </summary>
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(WriteCodeToInstancesMappedCommand))]
[NotifyCanExecuteChangedFor(nameof(ExportExcelCommand))]
private bool isComparison;
[ObservableProperty]
private List<EAMFacility> facilities = [];
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(MapModelEAMCodeCommand), nameof(ExportComponentsCommand))]
private List<InstanceFacility> instances = [];
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(MapModelEAMCodeCommand))]
private string excelPath;
[ObservableProperty]
private List<StoryCodeMapper> storyCodeMappers = [];
[ObservableProperty]
private string searchText;
/// <summary>
/// 楼层映射
/// </summary>
[RelayCommand]
private void ApplyMappers()
{
foreach (StoryCodeMapper mapper in StoryCodeMappers)
{
foreach (EAMFacility eamFacility in Facilities)
{
if (eamFacility.EAMCode.Contains(mapper.Story))
{
eamFacility.EAMCode = eamFacility.EAMCode.Replace(mapper.Story, mapper.Code);
}
}
}
IsMapper = true;
}
private static Assembly CurrentDomainOnAssemblyResolve(object sender, ResolveEventArgs args)
{
if (!args.Name.Contains("ComponentModel.Annotations"))
{
return null;
}
string assemblyFile = Path.Combine(GlobalVariables.DirAssembly, "System.ComponentModel.Annotations.dll");
return File.Exists(assemblyFile) ? Assembly.LoadFrom(assemblyFile) : null;
}
/// <summary>
/// 获取重命名的条目
/// </summary>
/// <param name="items"></param>
[RelayCommand]
private void GetInstances(IList items)
{
if (items.Count == 0)
{
Instances = null!;
return;
}
List<InstanceFacility> instanceFacilities = [];
foreach (object item in items)
{
if (item is KeyValuePair<string, List<FamilyInstance>> categoryItem)
{
//var familyType = new FilteredElementCollector(uidoc.doc)
// .QueryElementsByType(typeof(FamilyInstance))
// .Cast<FamilyInstance>()
// .Where(
// f => f.Category.Id.IntegerValue == (int)categoryItem.Key
// );
foreach (FamilyInstance element in categoryItem.Value)
{
InstanceFacility instanceFacility = new(element);
instanceFacilities.Add(instanceFacility);
}
}
}
Instances = instanceFacilities;
}
[RelayCommand]
private void OpenEAMFile()
{
OpenFileDialog dialog =
new()
{
CheckFileExists = true,
Filter = WinDialogAssists.CreateFilter("Excel文件", "xlsx"),
Multiselect = false,
InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
};
if (dialog.ShowDialog() != true)
{
return;
}
StoryCodeMappers.Clear();
ExcelPath = dialog.FileName;
FileInfo fi = new(ExcelPath);
using ExcelPackage package = new(fi);
ExcelWorksheet worksheet = package.Workbook.Worksheets[1];
////获取worksheet的行数
//int rows = worksheet.Dimension.End.Row;
////获取worksheet的列数
//int cols = worksheet.Dimension.End.Column;
worksheet.TrimLastEmptyRows();
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainOnAssemblyResolve;
try
{
Facilities = package.ToList<EAMFacility>(1, configuration => configuration.SkipCastingErrors());
foreach (EAMFacility eamFacility in Facilities)
{
eamFacility.EAMCode = $"EAM-{eamFacility.Story}-{eamFacility.FunctionLocation}";
}
IEnumerable<IGrouping<string, EAMFacility>> groups = Facilities.GroupBy(x => x.Story);
foreach (IGrouping<string, EAMFacility> group in groups)
{
if (group.Key == null)
{
continue;
}
StoryCodeMappers.Add(new() { Story = group.Key, });
}
IsMapper = false;
IsComparison = false;
Instances.ForEach(ins => ins.IsMapped = false);
}
catch (EPPlus.Core.Extensions.Exceptions.ExcelValidationException)
{
System.Windows.MessageBox.Show("列名不存在或不匹配,请检查表头是否存在换行。");
}
finally
{
AppDomain.CurrentDomain.AssemblyResolve -= CurrentDomainOnAssemblyResolve;
}
}
private bool CanWrite()
{
return Facilities.Any(f => f.IsMapped == true) && IsMapper && IsComparison;
}
private bool CanMap()
{
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
return ExcelPath != null && Instances.Any();
}
[RelayCommand]
private void ShowFacility(object obj)
{
if (obj is not IFacility model)
{
return;
}
Document doc = uidoc.Document;
if (!model.Instance.IsValidObject)
{
return;
}
if (model.Instance.IsHidden(uidoc.ActiveView))
{
return;
}
List<ElementId> ids = [model.Instance.Id];
showFacilitySectionBox.Raise(_ =>
{
if (uidoc.ActiveView is not View3D view3d)
{
view3d = doc.QueryElementsByType<View3D>().FirstElement() as View3D;
}
uidoc.ActiveView = view3d;
doc.Invoke(
_ =>
{
view3d.CreateSectionBox(ids, true);
BoundingBoxXYZ box = model.Instance.get_BoundingBox(uidoc.ActiveGraphicalView);
UIView uiView = uidoc.GetOpenUIViews().FirstOrDefault(v => v.ViewId == uidoc.ActiveGraphicalView.Id);
XYZ extendXyz = new(1, 1, 1);
uiView.ZoomAndCenterRectangle(box.Min - extendXyz, box.Max + extendXyz);
uidoc.Selection.SetElementIds(ids);
},
"定位构件"
);
});
}
[RelayCommand(CanExecute = nameof(CanWrite))]
private void WriteCodeToInstancesMapped()
{
IEnumerable<EAMFacility> mapFacilities = Facilities.Where(f => f.IsMapped == true);
writeCodeByMappedInstances.Raise(_ =>
{
uidoc.Document.Invoke(
_ =>
{
foreach (EAMFacility facility in mapFacilities)
{
facility.Instance.get_Parameter(BuiltInParameter.DOOR_NUMBER).Set(facility.EAMCode);
}
},
"EAM编码写入"
);
});
}
private bool CanExport()
{
return IsComparison && IsMapper;
}
[RelayCommand(CanExecute = nameof(CanExport))]
private void ExportExcel()
{
SaveFileDialog dialog =
new()
{
Filter = WinDialogAssists.CreateFilter("Excel文件", "xlsx"),
InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
};
if (dialog.ShowDialog() != true)
{
return;
}
string excel = dialog.FileName;
FileInfo fi = new(excel);
//using ExcelPackage package = new(fi);
//ExcelPackage excelPackage = Instances.ToWorksheet("< 1950")
// .WithConfiguration(configuration => configuration.WithColumnConfiguration(x => x.AutoFit()))
// .WithColumn(x => x.FirstName, "First Name")
// .WithColumn(x => x.LastName, "Last Name")
// .WithColumn(x => x.YearBorn, "Year of Birth")
// .WithTitle("< 1950")
// .NextWorksheet(post50, "> 1950")
// .WithColumn(x => x.LastName, "Last Name")
// .WithColumn(x => x.YearBorn, "Year of Birth")
// .WithTitle("> 1950")
// .ToExcelPackage();
ExcelPackage package = Facilities
.ToWorksheet("EAM编码匹配表")
.WithConfiguration(cfg => cfg.WithColumnConfiguration(c => c.AutoFit()))
.NextWorksheet(Instances, "族实例匹配表")
.WithConfiguration(cfg => cfg.WithColumnConfiguration(c => c.AutoFit()))
.ToExcelPackage();
package.SaveAs(fi);
MessageBoxResult result = MessageBox.Show("是否打开Excel表", "提示", MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result == MessageBoxResult.Yes)
{
Process.Start(excel);
}
}
[RelayCommand(CanExecute = nameof(CanMap))]
private void MapModelEAMCode()
{
foreach (InstanceFacility instanceFacility in Instances)
{
//标记已经写入EAM编码或类型名设备编号一致
IEnumerable<EAMFacility> mapEAMFacilities = Facilities.Where(
eamFacility =>
IsFuzzyMatch
? instanceFacility.Instance.Name.Contains(eamFacility.FacilityNumber)
|| eamFacility.EAMCode == instanceFacility.Number
|| instanceFacility.Instance
.GetParameters("ID-100-编号")
.FirstOrDefault()
?.AsString()
.Contains(eamFacility.FacilityNumber) == true
: instanceFacility.Instance.Name == eamFacility.FacilityNumber
|| eamFacility.EAMCode == instanceFacility.Number
|| instanceFacility.Instance.GetParameters("ID-100-编号").FirstOrDefault()?.AsString()
== eamFacility.FacilityNumber
);
if (mapEAMFacilities.Count() > 1)
{
instanceFacility.IsMapped = null;
instanceFacility.MapErrorMessage = "族类型名称与设备编号\n没有一一对应";
}
if (mapEAMFacilities.Count() == 1)
{
instanceFacility.IsMapped = true;
instanceFacility.EAMCode = mapEAMFacilities.FirstOrDefault().EAMCode;
}
}
foreach (EAMFacility eamFacility in Facilities)
{
IEnumerable<InstanceFacility> mapInstanceFacilities = Instances.Where(
instanceFacility =>
IsFuzzyMatch
? instanceFacility.Instance.Name.Contains(eamFacility.FacilityNumber)
|| eamFacility.EAMCode == instanceFacility.Number
: instanceFacility.Instance.Name == eamFacility.FacilityNumber || eamFacility.EAMCode == instanceFacility.Number
);
if (mapInstanceFacilities.Count() > 1)
{
eamFacility.IsMapped = null;
eamFacility.MapErrorMessage = "设备编号与族类型名称\n没有一一对应";
}
if (mapInstanceFacilities.Count() == 1)
{
eamFacility.IsMapped = true;
eamFacility.Instance = mapInstanceFacilities.FirstOrDefault().Instance;
}
}
IsComparison = true;
}
public Dictionary<string, List<FamilyInstance>> GetCategoriesFromDocument()
{
Document doc = uidoc.Document;
//var view3D = doc.QueryElementsByType<View3D>().Cast<View3D>().FirstOrDefault(v => !v.IsTemplate);
//Dictionary<BuiltInCategory, string> items = [];
Dictionary<string, List<FamilyInstance>> dict = new FilteredElementCollector(doc)
.OfClass(typeof(FamilyInstance))
.Where(i =>
i.Category.AllowsBoundParameters
&& !Enum.GetName(typeof(BuiltInCategory), i.Category.Id.IntegerValue)!.Contains("Fitting")
&& i.Category.CategoryType == CategoryType.Model
//风道末端
&& i.Category.Id.IntegerValue != -2008013)
.Cast<FamilyInstance>()
.GroupBy(i => i.Category.Id)
.ToDictionary(g => Category.GetCategory(doc, g.Key).Name, g => g.ToList());
return dict;
//var doc = uidoc.doc;
//var view3D = doc.QueryElementsByType<View3D>().Cast<View3D>().FirstOrDefault(v => !v.IsTemplate);
//var ins = view3D
// .OfCollector()
// .QueryElementsByType(typeof(FamilyInstance))
// .Cast<FamilyInstance>()
// .Where(
// i =>
// i.StructuralType == StructuralType.NonStructural
// && i.Category.CategoryType == CategoryType.Model
// && !Enum.GetName(typeof(BuiltInCategory), i.Category.Id.IntegerValue)!.Contains("Accessory")
// && !Enum.GetName(typeof(BuiltInCategory), i.Category.Id.IntegerValue)!.Contains("Fitting")
// //风道末端
// && i.Category.Id.IntegerValue != -2008013
// )
// .ToList();
//foreach (var ZoomRBGroup in groupBy)
//{
// items.Add(Category.GetCategory(doc, ZoomRBGroup.Key).Name(BuiltInCategory)ZoomRBGroup.Key.IntegerValue, );
//}
//return items.OrderBy(kv => kv.Value).ToDictionary(kv => kv.Key, kv => kv.Value);
}
private bool CanExportComponent()
{
return Instances != null && Instances.Any();
}
[RelayCommand(CanExecute = nameof(CanExportComponent))]
private void ExportComponents()
{
SaveFileDialog dialog =
new()
{
Filter = WinDialogAssists.CreateFilter("Excel文件", "xlsx"),
InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
};
if (dialog.ShowDialog() != true)
{
return;
}
string excel = dialog.FileName;
FileInfo fi = new(excel);
//using ExcelPackage package = new(fi);
//ExcelPackage excelPackage = Instances.ToWorksheet("< 1950")
// .WithConfiguration(configuration => configuration.WithColumnConfiguration(x => x.AutoFit()))
// .WithColumn(x => x.FirstName, "First Name")
// .WithColumn(x => x.LastName, "Last Name")
// .WithColumn(x => x.YearBorn, "Year of Birth")
// .WithTitle("< 1950")
// .NextWorksheet(post50, "> 1950")
// .WithColumn(x => x.LastName, "Last Name")
// .WithColumn(x => x.YearBorn, "Year of Birth")
// .WithTitle("> 1950")
// .ToExcelPackage();
ExcelPackage package = Facilities
.ToWorksheet("族实例匹配表")
.WithConfiguration(cfg => cfg.WithColumnConfiguration(c => c.AutoFit()))
.ToExcelPackage();
package.SaveAs(fi);
}
[RelayCommand]
private void QuickWriteEAMToInstances()
{
OpenFileDialog dialog =
new()
{
CheckFileExists = true,
Filter = WinDialogAssists.CreateFilter("Excel文件", "xlsx"),
Multiselect = false,
InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
};
if (dialog.ShowDialog() != true)
{
return;
}
StoryCodeMappers.Clear();
FileInfo fi = new(dialog.FileName);
using ExcelPackage package = new(fi);
ExcelWorksheet worksheet = package.Workbook.Worksheets[1];
worksheet.TrimLastEmptyRows();
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainOnAssemblyResolve;
try
{
List<WriteCodeItem> items = package.ToList<WriteCodeItem>(1, configuration => configuration.SkipCastingErrors());
StringBuilder sb = new();
writeCodeInstances.Raise(_ =>
{
uidoc.Document.Invoke(
_ =>
{
foreach (WriteCodeItem item in items)
{
if (int.TryParse(item.Id, out int result))
{
ElementId id = new(result);
Element element = uidoc.Document.GetElement(id);
try
{
element.get_Parameter(BuiltInParameter.DOOR_NUMBER).Set(item.EAMCode);
}
catch (Exception e)
{
sb.AppendLine($"异常:{e.Message} - 元素ID{item.Id}EAM编码{item.EAMCode}");
}
}
}
},
"批量写入EAM编码"
);
});
if (sb.Length > 0)
{
LogAssists.WriteTxtFile("EAM编码写入失败", sb.ToString());
}
}
catch (EPPlus.Core.Extensions.Exceptions.ExcelValidationException)
{
System.Windows.MessageBox.Show("列名不存在或不匹配,请检查表头是否存在换行。");
}
finally
{
AppDomain.CurrentDomain.AssemblyResolve -= CurrentDomainOnAssemblyResolve;
}
}
}