Files
SzmediTools/Szmedi.RvKits/InfoManager/EAMTools/EAMCodingViewModel.cs

538 lines
19 KiB
C#
Raw Normal View History

2025-09-16 16:06:41 +08:00

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;
}
}
}