538 lines
19 KiB
C#
538 lines
19 KiB
C#
|
||
|
||
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 ex)
|
||
{
|
||
System.Windows.MessageBox.Show("列名不存在或不匹配,请检查表头是否存在换行或多余文字。", ex.Message);
|
||
}
|
||
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 ex)
|
||
{
|
||
System.Windows.MessageBox.Show("列名不存在或不匹配,请检查表头是否存在换行或多余文字。", ex.Message);
|
||
}
|
||
finally
|
||
{
|
||
AppDomain.CurrentDomain.AssemblyResolve -= CurrentDomainOnAssemblyResolve;
|
||
}
|
||
}
|
||
}
|