Files
ShrlAlgoToolkit/ShrlAlgoToolkit.RevitAddins/ModelManager/ModelCheckViewModel.cs

670 lines
26 KiB
C#
Raw Normal View History

2024-09-22 11:05:41 +08:00
using System.Collections.ObjectModel;
using System.Windows.Data;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Architecture;
using Autodesk.Revit.DB.Electrical;
using Autodesk.Revit.DB.Mechanical;
using Autodesk.Revit.DB.Plumbing;
using Autodesk.Revit.UI;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Microsoft.Win32;
using Nice3point.Revit.Toolkit.External.Handlers;
2025-04-24 20:56:44 +08:00
using ShrlAlgoToolkit.Core.Assists;
2024-09-22 11:05:41 +08:00
2025-04-24 20:56:44 +08:00
using ShrlAlgoToolkit.RevitAddins.Assists;
using ShrlAlgoToolkit.RevitAddins.Windows;
2024-09-22 11:05:41 +08:00
// ReSharper disable PossibleMultipleEnumeration
2025-04-24 20:56:44 +08:00
namespace ShrlAlgoToolkit.RevitAddins.ModelManager;
2024-09-22 11:05:41 +08:00
public partial class ModelCheckViewModel : ObservableObject
{
private readonly CorrectReferLevelExecutes correctReferLevelExecutes;
private readonly ActionEventHandler modifyModel = new();
private readonly ActionEventHandler showElementsSectionBox = new();
2024-09-22 11:05:41 +08:00
private readonly UIApplication uiapp;
public ModelCheckViewModel(UIApplication uiapp)
{
Items = [];
var cv = CollectionViewSource.GetDefaultView(Items);
cv.GroupDescriptions.Add(new PropertyGroupDescription("ErrorMessage"));
correctReferLevelExecutes = new CorrectReferLevelExecutes(uiapp);
this.uiapp = uiapp;
FindBasePoint();
}
private bool CanExport()
{
return Items.Any();
}
private static bool CanShowElement(object obj)
{
return obj is MessageModel { IsInstance: true };
}
2024-09-22 11:05:41 +08:00
[RelayCommand]
private /*async Task*/ void CheckModel(/*CancellationToken token*/)
{
//if (Items.Any())
//{
// Items = [];
//}
Items.Clear();
var uidoc = uiapp.ActiveUIDocument;
var doc = uidoc.Document;
//await Task.Delay(TimeSpan.FromSeconds(2), token);
//if (doc.ActiveView is not View3D view3d)
//{
// view3d = doc.OfClass<View3D>().FirstOrDefault(e => FilteredElementCollector.IsViewValidForElementIteration(doc, e.Id)) as View3D;
//}
2024-12-22 10:26:12 +08:00
var elements = doc.OfParentModelElements();
2024-09-22 11:05:41 +08:00
var typeInstancesGroups = elements.GroupBy(e => e.GetTypeId());
if (IsCheckLevel)
{
var levels = doc.OfClass<Level>().OfType<Level>().OrderBy(l => l.Elevation);
var levelOutlines = CorrectReferLevelExecutes.GetRegions(levels);
foreach (var keyPair in levelOutlines)
{
//得到在标高范围内的元素
BoundingBoxIsInsideFilter insideFilter = new(keyPair.Value);
var level = keyPair.Key;
var insideCollector = new FilteredElementCollector(doc)
.WherePasses(insideFilter)
.Where(elem => elem.get_BoundingBox(doc.ActiveView) != null)
.ToList();
foreach (var elem in insideCollector)
{
var isCorrect = false;
var hasReferenceLevel = false;
foreach (Parameter param in elem.Parameters)
{
if (param.Definition.Name.Contains("标高") || param.Definition.Name.Contains("Level"))
{
hasReferenceLevel = param.StorageType == StorageType.ElementId;
if (hasReferenceLevel)
{
var value = param.AsElementId();
isCorrect = value == ElementId.InvalidElementId || param.AsElementId() == level.Id; //扶手等对象有宿主Host时为空
}
}
}
if (hasReferenceLevel && !isCorrect)
{
Items.Add(new MessageModel(elem, "参照标高有误"));
}
}
}
}
if (IsCheckName)
{
foreach (var item in typeInstancesGroups)
{
var id = item.Key;
var type = doc.GetElement(id);
var array = type.Name.Split('-');
if (array.Length >= 3)
{
var prefix = array[0].Trim();
if (
prefix
is not (
"QQ"
or "YT"
or "XL"
or "GJ"
or "CL"
or "JZ"
or "JG"
or "GP"
or "DZ"
or "TF"
or "GD"
or "TX"
or "XH"
or "ZS"
or "HB"
or "ZJ"
or "HJ"
or "CX"
or "MJ"
or "YK"
or "ZK"
or "ZT"
or "CJ"
or "XX"
or "TC"
or "NY"
or "ZB"
or "AF"
)
)
{
var errorItem = new MessageModel(type, "类型专业前缀错误");
Items.Add(errorItem);
}
}
else
{
var errorItem = new MessageModel(type, "类型名称不符合三段式");
Items.Add(errorItem);
}
}
//var familyGroups = doc.OfClass<FamilyInstance>().ToElements().Cast<FamilyInstance>().GroupBy(instance => instance.Symbol.Family);
//foreach (var group in familyGroups)
//{
// var array = group.Key.Name.Split('-');
// if (array.Length >= 3)
// {
// var prefix = array[0].Trim();
// if (prefix is not ("QQ" or "YT" or "XL" or "GJ" or "CL" or "JZ" or "JG" or "GP" or "DZ" or "TF" or "GD" or "TX" or "XH" or "ZS" or "HB" or "ZJ" or "HJ" or "CX" or "MJ" or "YK" or "ZK" or "ZT" or "CJ" or "XX" or "TC" or "NY" or "ZB" or "AF"))
// {
// var errorItem = new MessageModel(group.Key, "族名称专业前缀错误");
// Items.Add(errorItem);
// }
// }
// else
// {
// var errorItem = new MessageModel(group.Key, "族名称命名不符合三段式");
// Items.Add(errorItem);
// }
//}
}
if (IsCheckProps)
{
foreach (var group in typeInstancesGroups)
{
var typeCount = 0;
var typeId = group.Key;
var type = doc.GetElement(typeId);
foreach (Parameter param in type.Parameters)
{
var array = param.Definition.Name.Split('-');
if (array.Length >= 3)
{
var prefix = array[0].Trim();
if (prefix is ("ID" or "LC" or "ST" or "GJ" or "MF" or "AM" or "FM" or "TM" or "CM"))
{
typeCount++;
}
}
}
foreach (var elem in group)
{
var instanceCount = 0;
foreach (Parameter param in elem.Parameters)
{
var array = param.Definition.Name.Split('-');
if (array.Length >= 3)
{
var prefix = array[0].Trim();
if (prefix is ("ID" or "LC" or "ST" or "GJ" or "MF" or "AM" or "FM" or "TM" or "CM"))
{
instanceCount++;
}
}
}
if (typeCount + instanceCount < 60)
{
var errorItem = new MessageModel(elem, "规范参数不全");
Items.Add(errorItem);
}
}
}
}
if (IsCheckSlope)
{
var mepCurves = doc.OfClass<MEPCurve>().Where(e => e is not InsulationLiningBase and not FlexDuct and not FlexPipe);
foreach (var mepCurve in mepCurves)
{
var isError = false;
if (mepCurve is Pipe)
{
var param = mepCurve.get_Parameter(BuiltInParameter.RBS_PIPE_SLOPE);
isError = param.HasValue && ((param.AsDouble() < 0.25 && param.AsDouble() > 10E-6) || param.AsDouble() > Math.PI); //坡度过大或过小时,90度时AsDouble为0
}
else if (mepCurve is Duct)
{
var param = mepCurve.get_Parameter(BuiltInParameter.RBS_DUCT_SLOPE);
isError = param.HasValue && ((param.AsDouble() < 0.25 && param.AsDouble() > 10E-6) || param.AsDouble() > Math.PI); //坡度过大或过小时,90度时AsDouble为0
}
else if (mepCurve is Conduit or CableTray)
{
var p1 = mepCurve.get_Parameter(BuiltInParameter.RBS_START_OFFSET_PARAM).AsDouble();
var p2 = mepCurve.get_Parameter(BuiltInParameter.RBS_END_OFFSET_PARAM).AsDouble();
var l = mepCurve.get_Parameter(BuiltInParameter.CURVE_ELEM_LENGTH).AsDouble();
var sin = Math.Abs(p1 - p2) / l;
var radian = Math.Asin(sin);
var slope = Math.Tan(radian);
isError = slope is (> 10E-6 and < 0.25) || (slope > Math.PI && Math.Abs(radian - Math.PI / 2) > 10E-6);
}
if (isError)
{
var errorItem = new MessageModel(mepCurve, "管线坡度有误差");
Items.Add(errorItem);
}
}
}
if (IsCheckLength)
{
var mepCurves = doc.OfClass<MEPCurve>().Where(e => e is not InsulationLiningBase);
foreach (var mepCurve in mepCurves)
{
var length = mepCurve.get_Parameter(BuiltInParameter.CURVE_ELEM_LENGTH).AsDouble();
var connectors = mepCurve.GetConnectors(true);
if (length < 500 / 304.8 && connectors.Size == 2)
{
var errorItem = new MessageModel(mepCurve, "管道孤立或长度有误");
Items.Add(errorItem);
}
}
}
if (IsCheckEqual)
{
var mepCurves = doc.OfClass<MEPCurve>().Where(e => e is not InsulationLiningBase).ToList();
foreach (var c1 in mepCurves)
{
foreach (var c2 in mepCurves)
{
if (c1.Id != c2.Id)
{
2024-12-22 10:26:12 +08:00
var result = c1.GetCurve().Intersect(c2.GetCurve());
2024-09-22 11:05:41 +08:00
if (result == SetComparisonResult.Equal)
{
var errorItem = new MessageModel(c1, "管线存在重叠");
Items.Add(errorItem);
}
}
}
}
}
if (IsCheckSymbolGeometry)
{
foreach (var grouping in typeInstancesGroups)
{
var symbolId = grouping.Key;
if (doc.GetElement(symbolId) is not FamilySymbol symbol)
{
continue;
}
symbol.get_Geometry(new Options());
Options option = new() { ComputeReferences = true, DetailLevel = ViewDetailLevel.Fine };
var geometryElement = symbol.get_Geometry(option);
if (geometryElement == null)
{
continue;
}
var count = 0;
foreach (var geomObj in geometryElement)
{
if (geomObj is GeometryInstance geomInstance)
{
#if REVIT2018 || REVIT2020
if (geomInstance.Symbol is CADLinkType)
{
var errorItem = new MessageModel(symbol, "构件类型包含dwg模型");
Items.Add(errorItem);
}
#elif REVIT2025
if (doc.GetElement(geomInstance.GetSymbolGeometryId().SymbolId) is CADLinkType)
{
var errorItem = new MessageModel(symbol, "构件类型包含dwg模型");
Items.Add(errorItem);
}
#endif
}
if (geomObj is Solid)
{
count++;
}
}
if (count > 50)
{
var errorItem = new MessageModel(symbol, "构件类型几何需考虑简化超过50个实体");
Items.Add(errorItem);
}
}
}
ErrorCount = Items.Count;
ExportToExcelCommand.NotifyCanExecuteChanged();
}
[RelayCommand(CanExecute = nameof(CanExport))]
private void ExportToExcel()
{
var fileName = uiapp.ActiveUIDocument.Document.Title;
var dialog = new SaveFileDialog()
{
Title = "导出结果",
AddExtension = true,
DefaultExt = ".xlsx",
InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory),
Filter = "Excel表格(*.xlsx)|*.xlsx",
OverwritePrompt = true,
FileName = $"{fileName.Substring(0, fileName.Length - 4)}_检查结果"
};
if (dialog.ShowDialog() != true)
{
return;
}
//ExcelPackage.LicenseContext = OfficeOpenXml.LicenseContext.NonCommercial;
try
{
EPPlusHelper.WriteExcel(
dialog.FileName,
m =>
{
var groups = Items.GroupBy(e => e.ErrorMessage);
foreach (var group in groups)
{
var sheet = m.Workbook.Worksheets.Add(group.Key);
//int columns = tdd.NumberOfColumns;
//int rows = group.Count() + 1;
for (var i = -1; i < group.Count(); i++)
{
if (i == -1)
{
sheet.Cells[1, 1].Value = "元素ID";
sheet.Cells[1, 2].Value = "族类别";
sheet.Cells[1, 3].Value = "类型或实例名称";
sheet.Cells[1, 4].Value = "参照标高";
sheet.Cells[1, 5].Value = "系统";
sheet.Cells[1, 6].Value = "主体";
sheet.Cells[1, 7].Value = "房间";
}
else
{
var elem = group.ElementAt(i).Element;
var type = elem.GetType();
var host = type.GetProperty("Host");
var level = type.GetProperty("ReferenceLevel");
var mepSystem = type.GetProperty("MEPSystem");
var room = type.GetProperty("Room");
sheet.Cells[i + 2, 1].Value = elem.Id.ToString();
sheet.Cells[i + 2, 2].Value = elem.Category.Name;
sheet.Cells[i + 2, 3].Value = elem.Name;
if (level != null)
{
sheet.Cells[i + 2, 4].Value = (level.GetValue(elem, null) as Level)?.Name;
}
if (mepSystem != null)
{
sheet.Cells[i + 2, 5].Value = (mepSystem.GetValue(elem, null) as MEPSystem)?.Name;
}
if (host != null)
{
sheet.Cells[i + 2, 6].Value = (host.GetValue(elem, null) as Element)?.Name;
}
if (room != null)
{
sheet.Cells[i + 2, 7].Value = (room.GetValue(room, null) as Room)?.Name;
}
}
}
EPPlusHelper.SetTitle(sheet, 1, 1, 1, 7);
EPPlusHelper.SetStyle(sheet, 2, 1, group.Count() + 1, 7);
var range = sheet.Cells[1, 1, group.Count() + 1, 7];
range.AutoFitColumns();
}
}
);
WinDialogHelper.OpenFolderAndSelectFile(dialog.FileName);
2024-09-22 11:05:41 +08:00
}
catch (Exception ex)
{
2025-04-24 20:56:44 +08:00
LogAssist.ToLog(ex.Message);
2024-09-22 11:05:41 +08:00
}
}
private void FindBasePoint()
{
var basePoints = uiapp.ActiveUIDocument.Document.OfClass<BasePoint>().OfType<BasePoint>();
foreach (var item in basePoints)
{
//南北
var ns = Math.Round(
item.get_Parameter(BuiltInParameter.BASEPOINT_NORTHSOUTH_PARAM).AsDouble() * 0.3048,
4,
MidpointRounding.AwayFromZero
);
//东西
var ew = Math.Round(item.get_Parameter(BuiltInParameter.BASEPOINT_EASTWEST_PARAM).AsDouble() * 0.3048, 4, MidpointRounding.AwayFromZero);
//高程
var elev = Math.Round(
item.get_Parameter(BuiltInParameter.BASEPOINT_ELEVATION_PARAM).AsDouble() * 0.3048,
4,
MidpointRounding.AwayFromZero
);
if (item.IsShared)//测量点
{
SharedBasePoint = $"南北:{ns}m东西{ew}m高程{elev}m";
continue;
}
if (item.Category.GetHashCode() == -2001271)//项目基点
{
//正北角度
var angle = item.get_Parameter(BuiltInParameter.BASEPOINT_ANGLETON_PARAM).AsValueString();
ProjectBasePoint = $"南北:{ns}m东西{ew}m高程{elev}m角度{angle}";
}
}
}
2024-09-22 11:05:41 +08:00
[RelayCommand]
private void ModifyModel()
{
var errorItems = new List<MessageModel>();
modifyModel.Raise(_ =>
{
var uidoc = uiapp.ActiveUIDocument;
var doc = uidoc.Document;
doc.Invoke(
_ =>
{
#region
var errorItems1 = correctReferLevelExecutes.SetWalls();
errorItems.AddRange(errorItems1);
var errorItems2 = correctReferLevelExecutes.SetInstances();
errorItems.AddRange(errorItems2);
var errorItems3 = correctReferLevelExecutes.SetMEPCurves();
errorItems.AddRange(errorItems3);
#endregion
#region
foreach (var error in Items)
{
if (error.ErrorMessage != "管线坡度有误差")
{
continue;
}
var elem = error.Element;
if (elem is MEPCurve mep)
{
var loc = mep.Location as LocationCurve;
var line = loc!.Curve as Line;
var endPoint = line!.GetEndPoint(0);
var endPoint1 = line.GetEndPoint(1);
Line unboundLine;
Line l;
var xoy1 = endPoint.Flatten();
var xoy2 = endPoint1.Flatten();
var tan = Math.Abs(endPoint.Z - endPoint1.Z) / xoy1.DistanceTo(xoy2);
var dir = -XYZ.BasisZ;
if (endPoint.Z > endPoint1.Z) //以高点作为基准,修改低点
{
if (tan > 1)
{
unboundLine = Line.CreateUnbound(endPoint, dir);
var startPoint = unboundLine.Project(endPoint1).XYZPoint;
l = Line.CreateBound(endPoint, startPoint);
}
else
{
dir = xoy2 - xoy1;
unboundLine = Line.CreateUnbound(endPoint, dir);
var startPoint = unboundLine.Project(endPoint1).XYZPoint;
l = Line.CreateBound(endPoint, startPoint);
}
}
else
{
if (tan > 1)
{
unboundLine = Line.CreateUnbound(endPoint1, dir);
var startPoint = unboundLine.Project(endPoint).XYZPoint;
l = Line.CreateBound(startPoint, endPoint1);
}
else
{
dir = xoy2 - xoy1;
unboundLine = Line.CreateUnbound(endPoint1, dir);
var startPoint = unboundLine.Project(endPoint).XYZPoint;
l = Line.CreateBound(startPoint, endPoint1);
}
}
loc.Curve = l;
}
}
#endregion
},
"模型调整"
);
if (errorItems.Count == 0)
{
return;
}
SingletonChildWindowManager.ShowOrActivate<MessageWin, MessageViewModel>(uiapp.ActiveUIDocument, errorItems, "未解决错误");
});
}
[RelayCommand(CanExecute = nameof(CanShowElement))]
private void ShowElement(object obj)
{
if (obj is MessageModel model)
{
var uidoc = uiapp.ActiveUIDocument;
var doc = uidoc.Document;
//if (UiDocument.ActiveView.IsTemporaryHideIsolateActive())
//{
// UiDocument.ActiveView.temporary
//}
if (model.Element.IsValidObject)
{
if (model.Element.IsHidden(uidoc.ActiveView))
{
return;
}
List<ElementId> ids = [model.Element.Id];
//UiDocument.ActiveView.IsolateElementTemporary(model.ElementToMove.ViewId);
2024-09-22 11:05:41 +08:00
showElementsSectionBox.Raise(_ =>
{
if (uidoc.ActiveView is not View3D view3d)
{
view3d =
doc.OfClass<View3D>().FirstOrDefault(e => FilteredElementCollector.IsViewValidForElementIteration(doc, e.Id)) as View3D;
}
uidoc.ActiveView = view3d;
doc.Invoke(_ =>
{
if (UseSectionBox)
{
doc.InvokeSub(_ => view3d.SectionBoxElements(ids));
}
view3d.ZoomElements(uidoc, ids);
uidoc.Selection.SetElementIds(ids);
});
});
}
else
{
Items.Remove(model);
}
}
}
[ObservableProperty]
public partial int ErrorCount { get; set; }
[ObservableProperty]
public partial bool IsCheckEqual { get; set; }
[ObservableProperty]
public partial bool IsCheckLength { get; set; }
[ObservableProperty]
public partial bool IsCheckLevel { get; set; }
[ObservableProperty]
public partial bool IsCheckName { get; set; }
[ObservableProperty]
public partial bool IsCheckProps { get; set; }
[ObservableProperty]
public partial bool IsCheckSlope { get; set; }
[ObservableProperty]
public partial bool IsCheckSymbolGeometry { get; set; }
[ObservableProperty]
public partial ObservableCollection<MessageModel> Items { get; set; }
[ObservableProperty]
public partial int MyProperty { get; set; }
2024-09-22 11:05:41 +08:00
public string ProjectBasePoint { get; set; }
public string SharedBasePoint { get; set; }
[ObservableProperty]
public partial bool UseSectionBox { get; set; }
2024-09-22 11:05:41 +08:00
}
public enum ModelCheckType
{
/// <summary>
/// 命名
/// </summary>
Name,
/// <summary>
/// 坡度问题
/// </summary>
Slope,
/// <summary>
/// 参照标高
/// </summary>
ReferenceLevel,
/// <summary>
/// 属性检查
/// </summary>
Property,
/// <summary>
/// 管线重叠
/// </summary>
Equal,
/// <summary>
/// 管线过短,或孤立管线
/// </summary>
Length
}