using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
namespace ScaffoldMaterialStatistics
{
public class ScaffoldStatsCommands
{
// 定义材料类型枚举
private enum MaterialType
{
HorizontalBar, // 横杆
VerticalBar, // 立杆
DiagonalBrace, // 水平斜拉杆
SteelTread, // 钢踏板
SteelSection, // 型钢
WallConnector, // 连墙件
Fastener // 扣件
}
// 定义材料信息结构
private struct MaterialInfo
{
public MaterialType Type;
public string Name;
public double Length; // 单位:毫米
public int Quantity;
}
// 预编译正则表达式以提高性能
private static readonly Regex _lengthRegex = new Regex(
@"(\d+\.?\d*)\s*(mm|米|m)|^(\d+\.?\d*)$",
RegexOptions.Compiled | RegexOptions.IgnoreCase
);
private static readonly Regex _diagonalBraceRegex = new Regex(
@"水平斜拉杆",
RegexOptions.Compiled | RegexOptions.IgnoreCase
);
[CommandMethod("MCS", CommandFlags.Modal)]
public void ScaffoldMaterialStatistics()
{
Document doc = Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
try
{
// 提示用户选择对象
PromptSelectionOptions opts = new PromptSelectionOptions();
opts.MessageForAdding = "请框选外架材料: ";
PromptSelectionResult selRes = ed.GetSelection(opts);
if (selRes.Status != PromptStatus.OK)
{
ed.WriteMessage("\n操作已取消。");
return;
}
using (Transaction tr = db.TransactionManager.StartTransaction())
{
// 获取选择集
SelectionSet selSet = selRes.Value;
// 统计材料
List<MaterialInfo> materials = CountMaterials(selSet, tr, ed);
// 检查是否有统计数据
if (materials.Count == 0)
{
ed.WriteMessage("\n未找到可统计的材料!");
return;
}
// 创建统计表格
CreateStatisticsTable(doc, materials, ed);
tr.Commit();
}
ed.WriteMessage("\n外架材料统计完成!");
}
catch (System.Exception ex)
{
ed.WriteMessage($"\n错误: {ex.Message}\n{ex.StackTrace}");
}
}
private List<MaterialInfo> CountMaterials(SelectionSet selSet, Transaction tr, Editor ed)
{
// 使用字典直接存储MaterialInfo结构
Dictionary<string, MaterialInfo> materialDict = new Dictionary<string, MaterialInfo>();
int unrecognizedCount = 0;
// 遍历选择集中的每个对象ID
foreach (SelectedObject selectedObj in selSet)
{
if (selectedObj == null) continue;
Entity entity = tr.GetObject(selectedObj.ObjectId, OpenMode.ForRead) as Entity;
string layerName = entity.Layer.ToUpper(); // 统一转为大写便于比较
MaterialInfo? info = null;
string entityInfo = $"图层: {layerName}, ";
if (entity is BlockReference blockRef)
{
// 获取块名
BlockTableRecord btr = tr.GetObject(blockRef.BlockTableRecord, OpenMode.ForRead) as BlockTableRecord;
string blockName = btr.Name.ToUpper(); // 统一转为大写
entityInfo += $"块名: {blockName}";
// 解析块名和图层名并获取材料信息
info = ParseBlockAndLayerName(blockName, layerName);
}
else
{
// 非块对象
entityInfo += $"实体类型: {entity.GetType().Name}";
info = ParseLayerName(layerName);
}
if (info.HasValue)
{
MaterialInfo material = info.Value;
// 应用乘数规则
if (material.Type == MaterialType.SteelTread && !material.Name.Contains("单片"))
{
// 非单片钢踏板数量×3
material.Quantity = 3;
}
else if (material.Type == MaterialType.Fastener && material.Name == "900mm扣件")
{
// 900mm扣件数量×3
material.Quantity = 3;
}
else
{
material.Quantity = 1;
}
// 创建唯一标识键
string key = $"{material.Type}_{material.Name}_{material.Length:F1}";
// 更新材料数量
if (materialDict.TryGetValue(key, out MaterialInfo existing))
{
existing.Quantity += material.Quantity;
materialDict[key] = existing;
}
else
{
materialDict[key] = material;
}
entityInfo += $" -> 识别为: {material.Name} [数量: {material.Quantity}]";
}
else
{
unrecognizedCount++;
entityInfo += " -> 未识别";
}
// 输出调试信息
ed.WriteMessage($"\n{entityInfo}");
}
// 输出未识别对象数量
if (unrecognizedCount > 0)
{
ed.WriteMessage($"\n警告: 有 {unrecognizedCount} 个对象未被识别");
}
return materialDict.Values.ToList();
}
// 主要解析函数(块和图层)
private MaterialInfo? ParseBlockAndLayerName(string blockName, string layerName)
{
// 1. 处理扣件 (包含900mm扣件识别)
if (blockName.Contains("扣件") || layerName.Contains("扣件"))
{
return new MaterialInfo
{
Type = MaterialType.Fastener,
Name = (blockName.Contains("900") || layerName.Contains("900")) ?
"900mm扣件" : "标准扣件",
Length = 0
};
}
// 2. 处理钢踏板 (包含单片识别)
if (blockName.Contains("钢踏板") || layerName.Contains("钢踏板"))
{
double length = ExtractLengthFromString(blockName) > 0
? ExtractLengthFromString(blockName)
: ExtractLengthFromString(layerName);
return new MaterialInfo
{
Type = MaterialType.SteelTread,
Name = (blockName.Contains("单片") || layerName.Contains("单片")) ?
"单片钢踏板" : "钢踏板",
Length = length
};
}
// 3. 增强型钢识别
if (IsSteelSection(blockName, layerName))
{
double length = ExtractSteelLength(blockName, layerName);
string name = "型钢";
if (blockName.Contains("工字钢连梁") || layerName.Contains("工字钢连梁"))
name = "工字钢连梁";
else if (blockName.Contains("悬挑") || layerName.Contains("悬挑"))
name = "悬挑型钢";
else if (blockName.Contains("90") || layerName.Contains("90"))
name = "90度型钢";
else if (blockName.Contains("右45") || layerName.Contains("右45"))
name = "右45°角型钢";
else if (blockName.Contains("左45") || layerName.Contains("左45"))
name = "左45°角型钢";
return new MaterialInfo
{
Type = MaterialType.SteelSection,
Name = name,
Length = length
};
}
// 4. 其他材料类型
if (blockName.Contains("横杆") || layerName.Contains("横杆"))
{
double length = ExtractLengthFromString(blockName) > 0
? ExtractLengthFromString(blockName)
: ExtractLengthFromString(layerName);
return new MaterialInfo
{
Type = MaterialType.HorizontalBar,
Name = "横杆",
Length = length
};
}
if (_diagonalBraceRegex.IsMatch(blockName) || _diagonalBraceRegex.IsMatch(layerName))
{
double length = ExtractLengthFromString(blockName) > 0
? ExtractLengthFromString(blockName)
: ExtractLengthFromString(layerName);
return new MaterialInfo
{
Type = MaterialType.DiagonalBrace,
Name = "水平斜拉杆",
Length = length
};
}
if (blockName.Contains("立杆") || layerName.Contains("立杆"))
{
return new MaterialInfo
{
Type = MaterialType.VerticalBar,
Name = "立杆",
Length = 0
};
}
if (blockName.Contains("连墙件") || layerName.Contains("连墙件"))
{
double length = ExtractWallConnectorLength(blockName, layerName);
return new MaterialInfo
{
Type = MaterialType.WallConnector,
Name = "连墙件",
Length = length
};
}
// 未识别
return null;
}
// 图层解析(独立对象)
private MaterialInfo? ParseLayerName(string layerName)
{
// 1. 钢踏板
if (layerName.Contains("钢踏板"))
{
return new MaterialInfo
{
Type = MaterialType.SteelTread,
Name = layerName.Contains("单片") ? "单片钢踏板" : "钢踏板",
Length = ExtractLengthFromString(layerName)
};
}
// 2. 型钢
if (IsSteelSection("", layerName))
{
double length = ExtractSteelLength("", layerName);
string name = "型钢";
if (layerName.Contains("工字钢连梁")) name = "工字钢连梁";
else if (layerName.Contains("悬挑")) name = "悬挑型钢";
else if (layerName.Contains("90")) name = "90度型钢";
else if (layerName.Contains("右45")) name = "右45°角型钢";
else if (layerName.Contains("左45")) name = "左45°角型钢";
return new MaterialInfo
{
Type = MaterialType.SteelSection,
Name = name,
Length = length
};
}
// 3. 连墙件
if (layerName.Contains("连墙件"))
{
return new MaterialInfo
{
Type = MaterialType.WallConnector,
Name = "连墙件",
Length = ExtractWallConnectorLength("", layerName)
};
}
// 4. 扣件
if (layerName.Contains("扣件"))
{
return new MaterialInfo
{
Type = MaterialType.Fastener,
Name = layerName.Contains("900") ? "900mm扣件" : "标准扣件",
Length = 0
};
}
return null;
}
private double ExtractLengthFromString(string input)
{
if (string.IsNullOrEmpty(input)) return 0;
var match = _lengthRegex.Match(input);
if (!match.Success) return 0;
// 处理带单位的数字(修复分组索引错误)
if (match.Groups[1].Success && double.TryParse(match.Groups[1].Value, out double result))
{
string unit = match.Groups[2].Value.ToLower();
// 使用传统switch语句替代switch表达式
switch (unit)
{
case "米":
case "m":
return result * 1000; // 米转毫米
default:
return result;
}
}
// 处理纯数字(分组索引应为3)
if (match.Groups[3].Success && double.TryParse(match.Groups[3].Value, out result))
return result;
return 0;
}
// 增强型钢识别(替换or模式)
private bool IsSteelSection(string blockName, string layerName)
{
string combined = $"{blockName} {layerName}".ToUpper();
return combined.Contains("型钢") ||
combined.Contains("工字钢") ||
combined.Contains("XCG") ||
combined.Contains("悬挑") ||
combined.Contains("90度") ||
combined.Contains("90°") ||
combined.Contains("45度") ||
combined.Contains("45°") ||
combined.Contains("角型钢");
}
// 从字符串中提取长度信息
private double ExtractLengthFromString(string input)
{
if (string.IsNullOrEmpty(input)) return 0;
var match = _lengthRegex.Match(input);
if (!match.Success) return 0;
// 处理带单位的数字
if (double.TryParse(match.Groups[1].Value, out double result))
{
string unit = match.Groups[2].Value.ToLower();
return unit switch
{
"米" or "m" => result * 1000, // 米转毫米
_ => result
};
}
// 处理纯数字
if (double.TryParse(match.Groups[3].Value, out result))
return result;
return 0;
}
// 提取型钢长度(优先考虑图层名)
private double ExtractSteelLength(string blockName, string layerName)
{
// 优先从图层名提取
double length = ExtractLengthFromString(layerName);
if (length > 0) return length;
// 然后从块名提取
return ExtractLengthFromString(blockName);
}
// 提取连墙件长度
private double ExtractWallConnectorLength(string blockName, string layerName)
{
// 优先从图层名提取
double length = ExtractLengthFromString(layerName);
if (length > 0) return length;
// 然后从块名提取
return ExtractLengthFromString(blockName);
}
// 创建统计表格
private void CreateStatisticsTable(Document doc, List<MaterialInfo> materials, Editor ed)
{
Database db = doc.Database;
// 获取插入点
PromptPointResult ppr = ed.GetPoint("\n请指定统计表插入点: ");
if (ppr.Status != PromptStatus.OK)
{
ed.WriteMessage("\n未指定插入点,取消创建表格。");
return;
}
Point3d insertPoint = ppr.Value;
using (Transaction tr = db.TransactionManager.StartTransaction())
{
// 创建表格
Table table = new Table();
table.SetDatabaseDefaults(db);
table.Position = insertPoint;
// 设置表格尺寸
table.NumRows = materials.Count + 3; // 标题+表头+数据行+说明行
table.NumColumns = 4;
// 设置列宽
table.SetColumnWidth(0, 10); // 序号
table.SetColumnWidth(1, 25); // 材料名称
table.SetColumnWidth(2, 25); // 杆件长度
table.SetColumnWidth(3, 15); // 数量
// 设置标题行
table.Cells[0, 0].Value = "外架材料统计表";
table.Cells[0, 0].Alignment = CellAlignment.MiddleCenter;
table.Cells[0, 0].TextHeight = 3.0;
// 合并标题行单元格
table.MergeCells(CellRange.Create(table, 0, 0, 0, 3));
// 设置表头
string[] headers = { "序号", "材料名称", "杆件长度(mm)", "数量" };
for (int i = 0; i < headers.Length; i++)
{
table.Cells[1, i].Value = headers[i];
table.Cells[1, i].Alignment = CellAlignment.MiddleCenter;
table.Cells[1, i].TextHeight = 2.5;
}
// 填充数据
int row = 2;
int index = 1;
foreach (var material in materials.OrderBy(m => m.Type).ThenBy(m => m.Length))
{
table.Cells[row, 0].Value = index++.ToString(); // 序号
table.Cells[row, 1].Value = material.Name; // 材料名称
table.Cells[row, 2].Value = material.Length > 0 ?
material.Length.ToString("F0") : ""; // 杆件长度(取整)
table.Cells[row, 3].Value = material.Quantity; // 数量
// 设置数据单元格样式
for (int j = 0; j < 4; j++)
{
table.Cells[row, j].Alignment = CellAlignment.MiddleCenter;
table.Cells[row, j].TextHeight = 2.0;
}
row++;
}
// 添加说明行
table.Cells[row, 0].Value = "说明:①本表中钢踏板除单钢踏板外其余钢踏板已乘3计算数量;本表中钢踏板按一步一设计算数量。②连墙件按楼层计算数量;扣件按每根连墙件4个扣件计算数量;③本表中横杆计算数量含外侧两道护腰横杆内侧未涉及在内;本表中材料用量为预估算用量;具体实际用量按现场实际施工用量为准。";
table.Cells[row, 0].Alignment = CellAlignment.MiddleLeft;
table.Cells[row, 0].TextHeight = 1.2;
// 合并说明行单元格
table.MergeCells(CellRange.Create(table, row, 0, row, 3));
// 将表格添加到当前空间
BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
BlockTableRecord btr = tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord;
btr.AppendEntity(table);
tr.AddNewlyCreatedDBObject(table, true);
tr.Commit();
}
}
}
}
CS0111类型ScaffoldStatsCommands”已定义了一个名为“ExtractLengthFromString”的具有相同参数类型的成员