Files
SzmediTools/Szmedi.RvKits/ModelManager/ModelReviewViewModel.cs
2025-09-19 09:18:09 +08:00

1231 lines
51 KiB
C#
Raw Permalink 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.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Electrical;
using Autodesk.Revit.DB.Mechanical;
using Autodesk.Revit.DB.Plumbing;
using Autodesk.Revit.UI;
using Microsoft.Win32;
using Spire.Doc.Documents;
using Word = Spire.Doc;
namespace Szmedi.RvKits.ModelManager
{
public partial class ModelReviewViewModel : ObservableValidator
{
[ObservableProperty]
private bool isCivil = true;
[ObservableProperty]
private bool belongFloor = true;
[ObservableProperty]
private bool szComponent = true;
[ObservableProperty]
private bool listEveryErrors = true;
[ObservableProperty]
[StringLength(3, MinimumLength = 3)]
[NotifyCanExecuteChangedFor(nameof(ReviewModelCommand), nameof(ReviewModelsCommand))]
private string number = "001";
[ObservableProperty]
private string outputFolder = string.IsNullOrEmpty(Properties.Settings.Default.ReviewOutputFolder) ? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), $"审核报告{DateTime.Now:yyyy-MM-dd}") : Properties.Settings.Default.ReviewOutputFolder;
[ObservableProperty]
[StringLength(3, MinimumLength = 4)]
private string reviewCom = "深圳市市政设计研究院有限公司";
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(ReviewModelCommand), nameof(ReviewModelsCommand))]
[StringLength(20, MinimumLength = 2)]
private string reviewer;
[ObservableProperty]
[StringLength(100, MinimumLength = 1)]
private string reviewerComments = "请按照详细意见进行修改";
private readonly string templateFile = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Templates", "ModelReviewTemplate.docx");
private readonly UIApplication uiapp;
private string disc;
public ModelReviewViewModel(UIApplication uiapp)
{
this.uiapp = uiapp;
Reviewer = uiapp.Application.Username;
}
/// <summary>
/// 填写表单
/// </summary>
/// <param name="docx"></param>
/// <param name="reviewItems"></param>
private void FillReviewItems(Word.Document docx, List<ReviewItem> reviewItems)
{
var section = docx.Sections[0];
var table = section.Tables[1];
//复制表行,扣除原有行
for (var i = 0; i < reviewItems.Count - 1; i++)
{
var row = table.Rows[2].Clone();
table.Rows.Insert(3, row);
}
var rowIndex = 2;
var detail = new StringBuilder();
var detailtitles = new List<string>();
// 遍历表格中的行
for (var i = 0; i < reviewItems.Count; i++)
{
var item = reviewItems[i];
// 获取当前行
var row = table.Rows[rowIndex];
//第一列
var p1 = row.Cells[0].FirstParagraph;
p1 ??= row.Cells[0].AddParagraph();
p1.Text = $"{i + 1}";
//第二列
var p2 = row.Cells[1].FirstParagraph;
p2 ??= row.Cells[1].AddParagraph();
p2.Text = item.ModelFileName;
//第三列
var p3 = row.Cells[2].FirstParagraph;
p3 ??= row.Cells[2].AddParagraph();
p3.Text = item.Comment;
rowIndex++;
detailtitles.Add(item.Comment);
detail.Append($"{item}").AppendLine();
//detail.Append(item.DetailComment.ToString());
}
docx.Replace("${DetailComments}", $"{detail}", true, true);
foreach (var title in detailtitles)
{
var selection = docx.FindAllString(title, false, true).Last();
if (selection != null)
{
var titleRange = selection.GetAsOneRange();
titleRange.CharacterFormat.Bold = true;
titleRange.CharacterFormat.TextColor = System.Drawing.Color.Red;
titleRange.CharacterFormat.FontSize = 14;
}
}
var infos = docx.FindAllString("{族名:类型名/实例名:元素Id}", false, true);
if (infos != null)
{
foreach (var info in infos)
{
var textrange = info.GetAsOneRange();
textrange.CharacterFormat.FontSize = 10;
}
}
}
private static void InsertTextWatermark(Word.Document docx)
{
var section = docx.Sections[0];
var txtWatermark = new Word.TextWatermark
{
Text = "SZMEDI",
FontSize = 100,
Color = System.Drawing.Color.Gray,
Layout = WatermarkLayout.Diagonal,
};
section.Document.Watermark = txtWatermark;
}
/// <summary>
/// 管线附件
/// </summary>
/// <returns></returns>
private List<ReviewItem> ReviewAccessoryConnection(Document doc)
{
var instances = doc.OfType<FamilyInstance>()
.Where(ins => ins.Category.ToBuiltInCategory() == BuiltInCategory.OST_MechanicalEquipment);
return [];
}
/// <summary>
/// 审核项目,通用检查项并保存
/// </summary>
/// <param name="doc"></param>
/// <param name="message"></param>
/// <returns></returns>
private async Task<bool> ReviewDocumentAndSaveDocx(Document doc, StringBuilder message)
{
var project = string.Empty;
var engineering = string.Empty;
var discipline = string.Empty;
var phase = string.Empty;
var modelCom = string.Empty;
var path = doc.PathName;
var rvtfileName = Path.GetFileNameWithoutExtension(path);
var array = rvtfileName.Split('-', '/', '_').Select(s => s.Trim()).ToArray();
if (array.Length < 3)
{
message.AppendLine($"文件:{rvtfileName};命名不符合拆分规则:线路-工程(站场等)-专业-工程阶段-其他");
}
Word.Document docx = new();
docx.LoadFromFile(templateFile);
//添加水印
InsertTextWatermark(docx);
var lineRegex = new Regex(@"^\d+号线[\u4e00-\u9fff]*");
var phaseRegex = new Regex(@"^(设计|施工)阶段$");
var engineerRegex = new Regex(@"^[\u4e00-\u9fff]*(车辆段|站|区间|停车场|主变电|变电所)$");
var disciplineRegex = new Regex(@"^(通风|建筑|结构|轨道|机电|土建|围护|给排水|([\u4e00-\u9fff]电)|综合|通信|信号|管线|装修|场布|场地)");
foreach (var item in array)
{
if (lineRegex.IsMatch(item) && string.IsNullOrEmpty(project))
{
project = item;
continue;
}
if (phaseRegex.IsMatch(item) && string.IsNullOrEmpty(phase))
{
phase = item;
continue;
}
if (engineerRegex.IsMatch(item) && string.IsNullOrEmpty(engineering))
{
engineering = item;
continue;
}
if (disciplineRegex.IsMatch(item) && string.IsNullOrEmpty(discipline))
{
discipline = item;
}
}
disc = discipline;
//从文件名未能获得阶段时
if (string.IsNullOrEmpty(phase))
{
var phaseParam = doc.ProjectInformation.GetParameters("建设说明-建设阶段").FirstOrDefault();
if (phaseParam != null && !string.IsNullOrEmpty(phaseParam.AsString()))
{
phase = phaseParam.AsString();
}
}
//从构件读取,创建单位
var elems = doc.OfParentModelCollector();
if (elems == null)
{
return false;
}
foreach (var elem in elems)
{
var param = elem.GetParameters("CM-100-创建单位").FirstOrDefault();
if (param != null && !string.IsNullOrEmpty(param.AsString()))
{
modelCom = param.AsString();
break;
}
}
docx.Replace("${Project}", project, true, true);
docx.Replace("${Engineering}", engineering, true, true);
docx.Replace("${Discipline}", discipline.Trim('专', '业', '模', '型'), true, true);
docx.Replace("${Phase}", phase, true, true);
docx.Replace("${ModelCom}", modelCom, true, true);
docx.Replace("${ReviewCom}", ReviewCom, true, true);
docx.Replace("${Time}", DateTime.Now.ToString("yyyy-MM-dd"), true, true);
docx.Replace("${Year}", DateTime.Now.ToString("yyyy"), true, true);
docx.Replace("${Number}", Number, true, true);
docx.Replace("${Reviewer}", Reviewer, true, true);
docx.Replace("${ReviewComments}", ReviewerComments, true, true);
var items = new List<ReviewItem>();
items.AddRange(ReviewLocation(doc));
items.AddRange(ReviewName(doc));
items.AddRange(ReviewProperties(doc));
if (!IsCivil)
{
items.AddRange(ReviewFacilityConnection(doc));
items.AddRange(ReviewMEPCurveConnection(doc));
//items.AddRange(ReviewInstanceColor(doc));
items.AddRange(ReviewMEPCurveSlope(doc));
}
//var li = items.SelectMany(i => i.ReviewElements);
//if (li.Count() > 1000 && ListEveryErrors)
//{
// var result = MessageBox.Show("所有不符合要求的元素条目合计超过1000条数量过多写入word可能时间较长\r\n请考虑取消逐项列举是否继续导出", "提示", MessageBoxButton.YesNo, MessageBoxImage.Question);
// if (result == MessageBoxResult.No)
// {
// ListEveryErrors = false;
// items.ForEach(item => item.ListErrors = false);
// //foreach (var item in items)
// //{
// //}
// //return false;
// }
//}
//return false;
FillReviewItems(docx, items);
try
{
await Task.Run(
() =>
{
var docxFileName = Path.Combine($"{OutputFolder}", $"{rvtfileName}.docx");
docx.SaveToFile(docxFileName, Word.FileFormat.Docx);
});
return true;
}
catch (IOException ex)
{
MessageBox.Show(ex.Message);
return false;
}
}
private bool IsConnectToSystem(FamilyInstance ins)
{
bool hasConnectToSystem = false;
if (ins.MEPModel == null || ins.MEPModel.ConnectorManager == null)
{
return true;
}
if (ins.get_BoundingBox(null) != null &&
ins.MEPModel.ConnectorManager != null)
{
var cons = ins.GetConnectors(false)
.OfType<Connector>()
.Where(
con => con.ConnectorType == ConnectorType.End &&
con.Domain != Domain.DomainElectrical &&
con.ConnectorType == ConnectorType.Surface);
if (cons.Count() == 0)
{
return true;
}
foreach (Connector con in cons)
{
try
{
if (con.IsConnected)
{
hasConnectToSystem = true;
break;
}
}
catch (Exception)
{
//hasConnectToSystem = true;
}
}
}
return hasConnectToSystem;
}
/// <summary>
/// 设备连接
/// </summary>
/// <returns></returns>
private List<ReviewItem> ReviewFacilityConnection(Document doc)
{
var facilityReviews = new List<ReviewItem>();
var errorFacilities = doc.OfParentModelCollector().OfType<FamilyInstance>()
.Where(ins => !IsConnectToSystem(ins));
ReviewItem facilityError = new(ListEveryErrors) { Comment = "设备、管件、附件未连接到管线路由", Document = doc };
facilityError.ReviewElements.AddRange(errorFacilities);
if (facilityError.ReviewElements.Count > 0)
{
facilityReviews.Add(facilityError);
}
return facilityReviews;
}
/// <summary>
/// 管线坡度
/// </summary>
/// <param name="doc"></param>
/// <returns></returns>
private List<ReviewItem> ReviewMEPCurveSlope(Document doc)
{
List<ReviewItem> slopeReviews = [];
var mepCurves = doc.QueryElementsByType<MEPCurve>().Where(e => e is not InsulationLiningBase and not FlexDuct and not FlexPipe);
ReviewItem slopeError = new(ListEveryErrors) { Comment = "管线坡度有误", Document = doc };
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.0025 && param.AsDouble() > 10E-6) || param.AsDouble() > Math.PI); //坡度过大或过小时
}
else if (mepCurve is Duct)
{
var param = mepCurve.get_Parameter(BuiltInParameter.RBS_DUCT_SLOPE);
isError = param.HasValue && ((param.AsDouble() < 0.0025 && param.AsDouble() > 10E-6) || param.AsDouble() > Math.PI); //坡度过大或过小时
}
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-8 and < 0.25) || (slope > Math.PI && Math.Abs(radian - Math.PI / 2) > 10E-8);
}
if (isError)
{
slopeError.ReviewElements.Add(mepCurve);
}
}
if (slopeError.ReviewElements.Count > 0)
{
slopeReviews.Add(slopeError);
}
return slopeReviews;
}
/// <summary>
/// 颜色
/// </summary>
/// <param name="doc"></param>
/// <returns></returns>
private List<ReviewItem> ReviewInstanceColor(Document doc)
{
return [];
}
/// <summary>
/// 模型定位
/// </summary>
/// <param name="doc"></param>
/// <returns></returns>
private List<ReviewItem> ReviewLocation(Document doc)
{
var locationReviews = new List<ReviewItem>();
ReviewItem surveyPointError = new(ListEveryErrors) { Comment = "测量点坐标有误", Document = doc };
ReviewItem projectBasePointError = new(ListEveryErrors) { Comment = "项目基点坐标有误", Document = doc };
var basePoints = doc.QueryElementsByType<BasePoint>().OfType<BasePoint>().Where(p => p.get_BoundingBox(null) != null);
foreach (var point in basePoints)
{
//南北
var ns = Math.Round(
point.get_Parameter(BuiltInParameter.BASEPOINT_NORTHSOUTH_PARAM).AsDouble() * 304.8,
0,
MidpointRounding.AwayFromZero
);
//东西
var ew = Math.Round(
point.get_Parameter(BuiltInParameter.BASEPOINT_EASTWEST_PARAM).AsDouble() * 304.8,
0,
MidpointRounding.AwayFromZero
);
//建筑零标高的绝对高程
var height = Math.Round(
point.get_Parameter(BuiltInParameter.BASEPOINT_ELEVATION_PARAM).AsDouble() * 304.8,
0,
MidpointRounding.AwayFromZero
);
//测量点
if (point.IsShared)
{
if (ns != 0.0 && ew != 0.0)
{
surveyPointError.ReviewElements.Add(point);
}
}
else
{
//小数点的位数要准确mm级单位下南北的坐标位数10位24、25开头东西方向坐标位数9位。48、49开头
var nsStr = ((long)Math.Floor(ns)).ToString();
var ewStr = ((long)Math.Floor(ew)).ToString();
var nsCount = nsStr.Length;
var ewCount = ewStr.Length;
if ((nsCount != 10) || !(nsStr.StartsWith("24") || nsStr.StartsWith("25")))
{
projectBasePointError.ReviewElements.Add(point);
}
//存在超出深圳市范围的轨道交通站点
if (ewCount != 9 ||
!(ewStr.StartsWith("48") ||
ewStr.StartsWith("49") ||
ewStr.StartsWith("50") ||
ewStr.StartsWith("51") ||
ewStr.StartsWith("52")))
{
if (!projectBasePointError.ReviewElements.Contains(point))
{
projectBasePointError.ReviewElements.Add(point);
}
}
}
}
if (surveyPointError.ReviewElements.Count > 0)
{
locationReviews.Add(surveyPointError);
}
if (projectBasePointError.ReviewElements.Count > 0)
{
locationReviews.Add(projectBasePointError);
}
return locationReviews;
}
/// <summary>
/// 使用包围盒筛选器查找与目标管道可能重叠的管道。
/// </summary>
/// <param name="doc">Revit文档</param>
/// <param name="mepCurve">要检查的目标管道</param>
/// <returns>可能重叠的管道ID列表</returns>
public IList<Element> FindOverlapsWithBoundingBox(MEPCurve mepCurve)
{
var doc = mepCurve.Document;
// 获取目标管道的包围盒
BoundingBoxXYZ bb = mepCurve.get_BoundingBox(null);
if (bb == null)
{
return [];
}
// 创建一个轮廓线作为过滤器
Outline outline = new Outline(bb.Min, bb.Max);
// 创建包围盒相交过滤器
var bbFilter = new BoundingBoxIsInsideFilter(outline, 100 / 304.8);
// 排除目标管道自身
ElementId targetId = mepCurve.Id;
ExclusionFilter exclusionFilter = new ExclusionFilter(new List<ElementId> { targetId });
// 筛选出所有与目标管道包围盒相交的管道
FilteredElementCollector collector = new FilteredElementCollector(doc)
.OfClass(typeof(MEPCurve))
.WherePasses(bbFilter)
.WherePasses(exclusionFilter);
return collector.ToElements();
}
/// <summary>
/// 管线连接
/// </summary>
/// <returns></returns>
private List<ReviewItem> ReviewMEPCurveConnection(Document doc)
{
var mepCurveConnectionReviews = new List<ReviewItem>(2);
var mepCurves = doc.OfType<MEPCurve>()
.Where(e => e is not InsulationLiningBase && (e is not Pipe pipe || pipe.Diameter > 50 / 304.8))
.ToList();
ReviewItem singleMEPCurveError = new(ListEveryErrors) { Comment = "存在未连接的孤立管线", Document = doc };
ReviewItem equalMEPCurveError = new(ListEveryErrors) { Comment = "机电管线存在重叠", Document = doc };
foreach (var c1 in mepCurves)
{
//var length = c1.get_Parameter(BuiltInParameter.CURVE_ELEM_LENGTH).AsDouble();
var connectors = c1.GetConnectors(true);
if (connectors.Size == 2)
{
singleMEPCurveError.ReviewElements.Add(c1);
}
var loc = c1.GetLocCurve();
var others = FindOverlapsWithBoundingBox(c1);
foreach (var c2 in others)
{
var result = loc.Intersect(c2.GetLocCurve());
if (result == SetComparisonResult.Equal)
{
equalMEPCurveError.ReviewElements.Add(c1);
break;
}
}
}
//return mepCurveConnectionReviews;
if (singleMEPCurveError.ReviewElements.Count > 0)
{
mepCurveConnectionReviews.Add(singleMEPCurveError);
}
if (equalMEPCurveError.ReviewElements.Count > 0)
{
mepCurveConnectionReviews.Add(equalMEPCurveError);
}
return mepCurveConnectionReviews;
}
/// <summary>
/// 审核模型
/// </summary>
[RelayCommand(CanExecute = nameof(CanCheck))]
private async Task ReviewModel()
{
if (uiapp.ActiveUIDocument == null)
{
return;
}
if (!Directory.Exists(OutputFolder))
{
Directory.CreateDirectory(OutputFolder);
}
var message = new StringBuilder(1024);
var doc = uiapp.ActiveUIDocument.Document;
var path = doc.PathName;
if (doc.IsFamilyDocument)
{
MessageBox.Show("当前文件不是项目文件!", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
if (string.IsNullOrEmpty(path))
{
MessageBox.Show("当前文件未保存,请先保存文件!", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
var rvtfileName = Path.GetFileNameWithoutExtension(path);
var docxFileName = Path.Combine($"{OutputFolder}", $"{rvtfileName}.docx");
var result = await ReviewDocumentAndSaveDocx(doc, message);
if (result)
{
// var dialogResult = message.Length > 0
//? MessageBox.Show($"{message}\n是否打开输出的文件夹", "审核完成", MessageBoxButton.OK, MessageBoxImage.Information)
//: MessageBox.Show("是否打开输出的文件夹?", "审核完成", MessageBoxButton.YesNo, MessageBoxImage.Information);
// if (dialogResult == MessageBoxResult.Yes)
// {
// WinDialogAssists.OpenFolderAndSelectFile(docxFileName);
// //Process.Start(new ProcessStartInfo(OutputFolder) { UseShellExecute = true });
// }
OpenTargetFolder(message, docxFileName);
}
}
/// <summary>
/// 打开目标文件夹
/// </summary>
/// <param name="message"></param>
private static void OpenTargetFolder(StringBuilder message, string fileOrFolderPath)
{
var result = message.Length > 0
? MessageBox.Show($"{message}\n是否打开输出的文件夹", "审核完成", MessageBoxButton.OK, MessageBoxImage.Information)
: MessageBox.Show("是否打开输出的文件夹?", "审核完成", MessageBoxButton.YesNo, MessageBoxImage.Information);
if (result == MessageBoxResult.Yes)
{
if (File.Exists(fileOrFolderPath))
{
WinDialogAssists.OpenFolderAndSelectFile(fileOrFolderPath);
}
else
{
Process.Start(new ProcessStartInfo(fileOrFolderPath));
}
}
}
/// <summary>
/// 多选审核模型
/// </summary>
[RelayCommand(CanExecute = nameof(CanCheck))]
private async Task ReviewModels()
{
OpenFileDialog dialog =
new()
{
Filter = "Revit项目文件(*.rvt)|*.rvt",
Multiselect = true,
Title = "选择审核文件",
};
StringBuilder message = new(1024);
if (dialog.ShowDialog() == true)
{
if (!Directory.Exists(OutputFolder))
{
Directory.CreateDirectory(OutputFolder);
}
foreach (var path in dialog.FileNames)
{
Document doc;
var fileName = Path.GetFileNameWithoutExtension(path);
try
{
doc = uiapp.Application.OpenDocumentFile(path);
}
catch (Autodesk.Revit.Exceptions.FileAccessException)
{
message.AppendLine($"模型文件:{fileName} 被保存为更新的版本,无法打开");
continue;
}
catch (Autodesk.Revit.Exceptions.CorruptModelException)
{
message.AppendLine($"模型文件:{fileName},打开文档失败或文档损坏");
continue;
}
catch (Exception ex)
{
message.AppendLine($"模型文件:{fileName}{ex.Message}");
continue;
}
if (doc.IsFamilyDocument)
{
message.AppendLine($"模型文件:{fileName} 不是项目文件");
doc.Close(false);
continue;
}
await ReviewDocumentAndSaveDocx(doc, message);
doc.Close(false);
}
OpenTargetFolder(message, OutputFolder);
}
}
/// <summary>
/// 构件命名
/// </summary>
/// <param name="doc"></param>
/// <returns></returns>
private List<ReviewItem> ReviewName(Document doc)
{
var nameReviews = new List<ReviewItem>();
ReviewItem prefixError = new(ListEveryErrors)
{
Comment = "专业缩写前缀有误",
Document = doc
};
ReviewItem segmentError = new(ListEveryErrors)
{
Comment = "类型名称不符合三段式",
Document = doc
};
var elements = doc.OfParentModelCollector();
var typeInstancesGroups =
elements.GroupBy(e => e.GetTypeId()).ToList();
foreach (var item in typeInstancesGroups)
{
var id = item.Key;
if (id == null || id == ElementId.InvalidElementId)
{
continue;
}
var type = doc.GetElement(id);
//去掉没有类型的实例,如地形等
if (type == null)
{
continue;
}
var array = type.Name.Split('-');
var i = disc.Contains("场布") || disc.Contains("场地布置") ? 2 : 3;
if (array.Length >= i)
{
var prefix = array[0];
if (
prefix
is "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"
or "CD"//场布
)
{
continue;
}
prefixError.ReviewElements.Add(type);
//prefixError.Append($"{type.Name}");
}
else
{
segmentError.ReviewElements.Add(type);
}
}
if (prefixError.ReviewElements.Count > 0)
{
nameReviews.Add(prefixError);
}
if (segmentError.ReviewElements.Count > 0)
{
nameReviews.Add(segmentError);
}
return nameReviews;
}
/// <summary>
/// 属性条目
/// </summary>
/// <param name="doc"></param>
/// <returns></returns>
private List<ReviewItem> ReviewProperties(Document doc)
{
var propertiesReviews = new List<ReviewItem>();
ReviewItem paramCountError = new(ListEveryErrors) { Comment = "构件信息条目不全或重复添加", Document = doc };
var detail = "模型权属单位、创建单位、创建人员及联系方式、校审单位等信息,部分或全部未填写。";
ReviewItem paramFillError = new(ListEveryErrors)
{
Comment = "要求的构件信息未填写",
Document = doc,
DetailNote = detail,
};
ReviewItem belongFloorpParamFillError = default;
if (BelongFloor)
{
belongFloorpParamFillError = new(ListEveryErrors)
{
Comment = "“所属楼层”信息未填写",
Document = doc,
DetailNote = "所属楼层信息未填写",
};
}
ReviewItem szComponentParamFillError = default;
if (SzComponent)
{
szComponentParamFillError = new(ListEveryErrors)
{
Comment = "“深圳构件标识”信息未填写",
Document = doc,
DetailNote = "构件的深圳构件标识信息未填写",
};
}
ReviewItem projectInfoCountError = new(ListEveryErrors) { Comment = "要求的项目信息条目不全或重复添加", Document = doc };
ReviewItem projectInfoFillError = new(ListEveryErrors) { Comment = "要求的项目信息未填写", Document = doc };
//ReviewItem floorError = new() { Comment = "信息丢失或未填写", doc = doc };
var elements = doc.OfParentModelCollector();
var typeInstancesGroups = elements
.GroupBy(e => e.GetTypeId())
.ToList();
foreach (var group in typeInstancesGroups)
{
var paramCount = 0;
var typeId = group.Key;
if (typeId == null || typeId == ElementId.InvalidElementId)
{
continue;
}
var type = doc.GetElement(typeId);
var typeParams = type.Parameters
.OfType<Parameter>()
.Distinct(new PropertyComparer<Parameter>(p => p.Definition.Name));
//类型参数个数
foreach (var param in typeParams)
{
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"))
{
paramCount++;
}
}
}
////是否有类型参数
//var floorParam = type.GetParameters("所属楼层").FirstOrDefault();
//var belongCom = type.GetParameters("AM-200-模型权属单位").FirstOrDefault();
//var assetCom = type.GetParameters("AM-200-资产权属单位").FirstOrDefault();
//var creatorCom = type.GetParameters("CM-100-创建单位").FirstOrDefault();
//var creator = type.GetParameters("CM-100-创建人员及联系方式").FirstOrDefault();
//var reviewComment = type.GetParameters("CM-100-校审单位").FirstOrDefault();
//var hasSetValues = string.IsNullOrEmpty(floorParam?.AsString()) ||
// string.IsNullOrEmpty(belongCom?.AsString()) ||
// string.IsNullOrEmpty(assetCom?.AsString()) ||
// string.IsNullOrEmpty(creatorCom?.AsString()) ||
// string.IsNullOrEmpty(creator?.AsString()) ||
// string.IsNullOrEmpty(reviewComment?.AsString());
//实例参数个数
foreach (var elem in group)
{
//var floorParam = elem.GetParameters("所属楼层").FirstOrDefault();
//var belongCom = elem.GetParameters("AM-200-模型权属单位").FirstOrDefault();
//var assetCom = elem.GetParameters("AM-200-资产权属单位").FirstOrDefault();
//var creatorCom = elem.GetParameters("CM-100-创建单位").FirstOrDefault();
//var creator = elem.GetParameters("CM-100-创建人员及联系方式").FirstOrDefault();
//var reviewComment = elem.GetParameters("CM-100-校审单位").FirstOrDefault();
//var hasSetValues = string.IsNullOrEmpty(floorParam?.AsString()) ||
// string.IsNullOrEmpty(belongCom?.AsString()) ||
// string.IsNullOrEmpty(assetCom?.AsString()) ||
// string.IsNullOrEmpty(creatorCom?.AsString()) ||
// string.IsNullOrEmpty(creator?.AsString()) ||
// string.IsNullOrEmpty(reviewComment?.AsString());
var hasSetValues =
HasSetValue(elem, "AM-200-模型权属单位") &&
HasSetValue(elem, "CM-100-创建单位") &&
HasSetValue(elem, "CM-100-创建人员及联系方式") &&
HasSetValue(elem, "CM-100-校审单位");
if (!hasSetValues)
{
paramFillError.ReviewElements.Add(elem);
}
//所属楼层
if (BelongFloor)
{
hasSetValues = HasSetValue(elem, "所属楼层");
if (!hasSetValues)
{
belongFloorpParamFillError.ReviewElements.Add(elem);
}
}
//深圳构件标识
if (SzComponent)
{
hasSetValues = HasSetValue(elem, "深圳构件标识");
if (!hasSetValues)
{
szComponentParamFillError.ReviewElements.Add(elem);
}
}
}
var instanceParams = group.FirstOrDefault().Parameters
.OfType<Parameter>()
.Distinct(new PropertyComparer<Parameter>(p => p.Definition.Name));
foreach (var param in instanceParams)
{
var array = param.Definition.Name.Split('-');
if (array.Length >= 3)
{
var prefix = array[0].Trim();
//TC-是技术参数
if (prefix is ("ID" or "LC" or "ST" or "GJ" or "MF" or "AM" or "FM" or "TM" or "CM"))
{
paramCount++;
}
}
}
//参数数量有误
if (paramCount < 60)
{
paramCountError.ReviewElements.Add(type);
}
}
//项目信息有误
var projectParamCoutHasError = false;
var projectParamCount = 0;
var projectParams = doc.ProjectInformation.Parameters.OfType<Parameter>().Distinct(new PropertyComparer<Parameter>(p => p.Definition.Name));
foreach (var param in projectParams)
{
if (param.Id.IntegerValue > 0)
{
var name = param.Definition.Name;
if (name.StartsWith("项目标识") ||
name.StartsWith("坐标定位") ||
name.StartsWith("项目内部定位") ||
name.StartsWith("建筑类别或等级") ||
name.StartsWith("建设说明") || name.StartsWith("系统分类"))
{
projectParamCount++;
}
}
}
if (IsCivil)
{
if (projectParamCount < 17)
{
projectInfoCountError.DetailNote = "请根据《轨道交通工程BIM模型建模标准》添加项目标识、项目定位、建设说明、建筑类别或等级等信息条目格式为属性组-属性名称。";
projectParamCoutHasError = true;
//projectInfoCountError.ReviewElements.Add(doc.ProjectInformation);
}
}
else
{
if (projectParamCount < 6)
{
projectInfoCountError.DetailNote = "请根据《轨道交通工程BIM模型建模标准》添加项目标识、系统分类等信息条目格式为属性组-属性名称。";
projectParamCoutHasError = true;
}
}
var projectName = doc.ProjectInformation.GetParameters("项目标识-项目名称").FirstOrDefault()?.AsString();
var projectInfoFillHasError = string.IsNullOrEmpty(projectName);
//项目参数信息未填写
if (IsCivil)
{
var stationName = doc.ProjectInformation.GetParameters("项目标识-站点名称").FirstOrDefault()?.AsString();
var engineerName = doc.ProjectInformation.GetParameters("项目标识-工程名称").FirstOrDefault()?.AsString();
var startMileage = doc.ProjectInformation.GetParameters("坐标定位-起点里程").FirstOrDefault()?.AsString();
var centerMileage = doc.ProjectInformation.GetParameters("坐标定位-站台中心里程").FirstOrDefault()?.AsString();
var location = doc.ProjectInformation.GetParameters("建设说明-建设地点").FirstOrDefault()?.AsString();
var phase = doc.ProjectInformation.GetParameters("建设说明-建设阶段").FirstOrDefault()?.AsString();
var archiLevel = doc.ProjectInformation.GetParameters("建筑类别或等级-建筑等级").FirstOrDefault()?.AsString();
var seismicGrade = doc.ProjectInformation.GetParameters("建筑类别或等级-抗震等级").FirstOrDefault()?.AsString();
var seismicIntensity = doc.ProjectInformation.GetParameters("建筑类别或等级-抗震设防烈度").FirstOrDefault()?.AsString();
projectInfoFillHasError = projectInfoFillHasError && string.IsNullOrEmpty(stationName) ||
string.IsNullOrEmpty(engineerName) ||
string.IsNullOrEmpty(startMileage) ||
string.IsNullOrEmpty(centerMileage) ||
string.IsNullOrEmpty(location) ||
string.IsNullOrEmpty(phase) ||
string.IsNullOrEmpty(archiLevel) ||
string.IsNullOrEmpty(seismicGrade) ||
string.IsNullOrEmpty(seismicIntensity);
projectInfoFillError.DetailNote = "项目名称、站点名称、工程名称、起点里程、站台中心里程、建设地点、建设阶段、建筑等级、抗震等级、抗震设防烈度等项目信息未填写。";
}
else
{
var systemName = doc.ProjectInformation.GetParameters("系统分类-系统分类名称").FirstOrDefault()?.AsString();
var systemCode = doc.ProjectInformation.GetParameters("系统分类-系统分类代号").FirstOrDefault()?.AsString();
projectInfoFillHasError = projectInfoFillHasError && string.IsNullOrEmpty(systemName) || string.IsNullOrEmpty(systemCode);
projectInfoFillError.DetailNote = $"项目名称、系统分类名称、系统分类代号等部分项目信息未填写";
}
if (projectParamCoutHasError)
{
propertiesReviews.Add(projectInfoCountError);
}
if (projectInfoFillHasError)
{
propertiesReviews.Add(projectInfoFillError);
}
if (paramCountError.ReviewElements.Count > 0)
{
propertiesReviews.Add(paramCountError);
}
if (paramFillError.ReviewElements.Count > 0)
{
propertiesReviews.Add(paramFillError);
}
if (BelongFloor && belongFloorpParamFillError.ReviewElements.Count > 0)
{
propertiesReviews.Add(belongFloorpParamFillError);
}
if (SzComponent && szComponentParamFillError.ReviewElements.Count > 0)
{
propertiesReviews.Add(szComponentParamFillError);
}
//if (floorError.ReviewElements.Count > 0)
//{
// propertiesReviews.Add(floorError);
//}
return propertiesReviews;
}
/// <summary>
/// 是否存在已填值的该参数
/// </summary>
/// <param name="elem"></param>
/// <param name="paramName"></param>
private static bool HasSetValue(Element elem, string paramName)
{
var parameters = elem.GetParameters(paramName);
if (parameters.Count == 0)
{
return false;
}
else if (parameters.Count == 1)
{
return !string.IsNullOrEmpty(parameters.FirstOrDefault().AsString()) && !string.IsNullOrWhiteSpace(parameters.FirstOrDefault().AsString());
}
else
{
foreach (var param in parameters)
{
if (!string.IsNullOrEmpty(param.AsString()) && !string.IsNullOrWhiteSpace(param.AsString()))
{
return true;
}
}
}
return false;
}
/// <summary>
/// 选择输出目录
/// </summary>
/// <param name="window"></param>
[RelayCommand]
private void SelectOutputFolder(Window window)
{
FolderBrowserDialog dialog = new();
if (dialog.ShowDialog(window))
{
OutputFolder = dialog.SelectedPath;
}
}
private bool CanCheck => !string.IsNullOrEmpty(Reviewer) &&
!string.IsNullOrEmpty(OutputFolder) &&
!string.IsNullOrEmpty(Number);
}
class ReviewItem(bool listErrors)
{
public override string ToString()
{
var sb = new StringBuilder();
sb.Append($"{Comment}");
sb.AppendLine(ReviewElements.Count > 0 ? "{族名:类型名或实例名:类型或实例Id}" : "");
if (!string.IsNullOrEmpty(DetailNote)/* && ReviewElements.Count > 50*/)
{
sb.AppendLine(DetailNote);
//return sb.ToString();
}
if (ReviewElements.Count > 1000 && !ListErrors)
{
var group = ReviewElements.GroupBy(e => e.GetTypeId())
.Select(g => g.FirstOrDefault().Name)
.Aggregate((n1, n2) => $"{n1};{n2}");
sb.Replace("{族名:类型名或实例名:类型或实例Id}", string.Empty);
sb.AppendLine($"类型名称:{group}");
sb.Append("注:问题构件数量超过1000个未全部列举请自行检查。");
return sb.ToString();
}
foreach (var element in ReviewElements)
{
var doc = element.Document;
sb.Append('{');
if (element is ElementType elementType)
{
var familyName = elementType.FamilyName;
sb.Append(familyName);
if (!string.IsNullOrEmpty(familyName))
{
sb.Append(':');
}
var typeName = element.Name;
if (!string.IsNullOrEmpty(typeName) && typeName != familyName)
{
sb.Append(typeName).Append(':');
}
sb.Append(element.Id.IntegerValue);
}
else
{
if (element.GetTypeId() != ElementId.InvalidElementId)
{
var type = doc.GetElement(element.GetTypeId());
if (type is ElementType elemType)
{
var familyName = elemType.FamilyName;
sb.Append(familyName);
if (!string.IsNullOrEmpty(familyName))
{
sb.Append(':');
}
var typeName = element.Name;
sb.Append(typeName);
if (!string.IsNullOrEmpty(typeName))
{
sb.Append(':');
}
var elemName = element.Name;
if (elemName != typeName && !string.IsNullOrEmpty(elemName))
{
sb.Append(element.Name).Append(':');
}
sb.Append(element.Id.IntegerValue);
}
}
else
{
var elemName = element.Name;
sb.Append(element.Name);
if (!string.IsNullOrEmpty(elemName))
{
sb.Append(':');
}
sb.Append(element.Id.IntegerValue);
}
//sb.Append($"{element.Name}");
//if (!string.IsNullOrEmpty(element.Name))
//{
// sb.Append('');
//}
//sb.Append($"{element.Id}");
}
sb.Append("};");
}
sb.AppendLine();
return sb.ToString();
}
public ReviewCategory Category { get; set; }
public string Comment { get; set; }
public Document Document { get; set; }
public string ModelFileName => Path.GetFileNameWithoutExtension(Document.Title);
public int Number { get; set; }
/// <summary>
/// 复审意见
/// </summary>
public string ReexamineComment { get; set; }
/// <summary>
/// 意见回复
/// </summary>
public string ReplyToComment { get; set; }
/// <summary>
/// 详细意见备注
/// </summary>
public string DetailNote { get; set; }
/// <summary>
/// 元素、元素类型、族等
/// </summary>
public List<Element> ReviewElements { get; set; } = [];
public bool ListErrors { get; set; } = listErrors;
}
enum ReviewCategory
{
None = 0,
Standard = 1,
Accuracy = 2,
Optional = 3,
}
public class PropertyComparer<T> : IEqualityComparer<T>
{
private readonly Func<T, object> _propertySelector;
public PropertyComparer(Func<T, object> propertySelector)
{
_propertySelector = propertySelector;
}
public bool Equals(T x, T y)
{
return _propertySelector(x).Equals(_propertySelector(y));
}
public int GetHashCode(T obj)
{
return _propertySelector(obj).GetHashCode();
}
}
}