Tekla 二次开发:ModelObject API 完全解析 - 钢结构资源网 Tekla插件 CAD工具 犀牛GH汉化 套料 Tekla 二次开发:ModelObject API 完全解析 - 钢结构资源网 Tekla插件 CAD工具 犀牛GH汉化 套料

Tekla 二次开发:ModelObject API 完全解析

TEKLA API
ModelObject
Tekla Structures 二次开发
 
核心抽象基类 · 完全 API 解析

Tekla Structures ModelObject API 完全解析

ModelObject 是 Tekla Structures Open API 中最核心的抽象基类,所有模型对象(BeamBoltArrayWeldAssembly 等)均直接或间接继承自此类。它定义了模型对象的通用行为:CRUD 操作、用户属性读写、报告属性访问、坐标系统获取、阶段管理等。

本文基于 Tekla.Structures.Model 程序集(Version=2017.0.0.0)的反编译源码,深入解析 ModelObject 的完整继承体系、构造机制、属性方法及最佳实践。

 

一、继承体系

ModelObject 位于 Tekla 模型对象层次结构的中心位置,声明为 abstract,不能直接实例化:

System.Object
  └── ModelObject (abstract)
        ├── Part (abstract) → Beam, ContourPlate, Brep, BentPlate...
        ├── BaseComponent (abstract) → Connection, Component, CustomPart...
        ├── Assembly
        └── Weld, BoltArray, RebarGroup, Load... 等共 61 种类型

ModelObject 实现了 IComparableIEquatable 接口,支持排序和相等比较;标记 [Serializable][ClassInterface(ClassInterfaceType.AutoDual)],支持 COM 互操作和序列化。内部枚举 ModelObjectEnum 定义了全部 61 种模型对象类型标识符。

关键特征:
ModelObject 声明为 abstract,不能直接实例化
• 实现 IComparableIEquatable 接口
• 标记 [Serializable][ClassInterface],支持 COM 互操作
ModelObjectEnum 定义了全部 61 种模型对象类型标识符
层级 类型 说明
Level 0 System.Object .NET 基础对象类
Level 1 ModelObject(抽象) 核心抽象基类,定义 CRUD + 属性操作
Level 2-a Part(抽象) 构件基类,派生 Beam、ContourPlate 等
Level 2-b BaseComponent(抽象) 组件基类,派生 Connection、Component 等
Level 2-c Assembly 装配体,包含多个子构件
Level 2-d Brep B-Rep 几何实体
Level 3+ 具体业务类 Beam, BoltArray, Weld, RebarGroup 等 61 种

二、构造函数

ModelObject 作为抽象基类,其自身没有公开的构造函数。子类的构造遵循以下模式:

步骤 说明
① 调用子类构造 通过具体子类创建实例:new Beam()new BoltArray()
② 基类链初始化 ObjectModelObject 构造链依次执行
③ 字段默认值 modificationTimeInfo = null(延迟加载),ObjectLabel = null
④ 标识符状态 新创建的对象 Identifier.ID = 0(无效),调用 Select()Insert() 后获得有效 ID
重要提醒:构造后的 ModelObject 仅存在于内存中,尚未写入 Tekla 模型数据库。必须显式调用 Insert() 方法才能将对象持久化到模型中。

三、属性

3.1 自身属性

属性名 类型 可读写 说明
ModificationTime DateTime? 只读 对象最后修改时间,从数据库 epoch 时间戳转换。首次访问时延迟加载
IsUpToDate bool 只读 指示对象是否为最新状态。true = 与模型数据库同步;false = 本地有未提交修改
ObjectLabel string 内部 对象标签(内部使用),通过 SetLabel(string) 设置

3.2 ModificationTime 延迟加载机制

ModificationTime 属性采用延迟加载模式。首次访问时,若内部缓存 modificationTimeInfo 为 null,则调用底层 API ExportGetObjectLastModified() 获取时间戳,通过 ConvertDateTime.FromEpoch() 转换为本地时间后缓存。后续访问直接返回缓存值,避免重复 API 调用。如果 API 调用失败,则返回 null 并设置 IsLocallyModified = true

3.3 继承属性(源自 Part / ModelObject 子类)

以下为最常用的继承属性,具体子类(BeamContourPlate 等)会在此基础上扩展更多专用属性:

属性 类型 继承自 说明
Identifier Identifier ModelObject 模型对象的唯一标识符
Name string Part 零件名称
Class string Part 零件等级
Finish string Part 表面处理代码(如 GALV)
PartNumber string Part 零件编号
AssemblyNumber string Part 构件编号
Profile Profile Part 实体的截面
Material Material Part 实体的材质
Position Position Part 控制实体在截面方向上的位置
UserPhase int ModelObject 用户定义的状态阶段

四、枚举类型

ModelObject 定义了一个重要的内部公开枚举 ModelObjectEnum,用于标识 Tekla 模型中的所有对象类型:

枚举值 数值 对应类
BEAM 1 梁/柱(Beam)
POLYBEAM 2 折线梁(PolyBeam)
CONTOURPLATE 3 等高板(ContourPlate)
BOOLEANPART 4 布尔切割件(BooleanPart)
FITTING 5 适配件(Fitting)
CUTPLANE 6 切割面(CutPlane)
WELD 8 焊缝(Weld)
ASSEMBLY 9 装配体(Assembly)
SINGLEREBAR 10 单根钢筋(SingleRebar)
REBARGROUP 11 钢筋组(RebarGroup)
BOLT_ARRAY 15 螺栓阵列(BoltArray)
CONNECTION 24 节点(Connection)
COMPONENT 25 组件(Component)
CUSTOM_PART 43 自定义零件(CustomPart)
BREP 52 B-Rep 实体(Brep)
BENT_PLATE 61 折弯板(BentPlate)

(共 61 种类型,上表仅列出最常用的 16 种。完整列表含 REBARMESH、LOAD_*、GRID、TASK 等。)

另外还存在一个私有枚举 PropertySourceEnum(SOURCE_UDA / SOURCE_REPORT / SOURCE_ALL_UDAS),用于区分属性来源,仅内部使用。

五、方法

5.1 抽象方法(必须由子类实现)

方法 返回类型 说明
Insert() bool 将当前对象插入模型数据库。成功返回 true
Select() bool 根据 Identifier 从数据库选择对象并填充属性
Modify() bool 将本地修改提交到模型数据库
Delete() bool 从模型数据库删除当前对象

5.2 实例方法

方法 返回类型 说明
GetChildren() ModelObjectEnumerator 获取当前对象的所有子对象。需 Identifier 有效
GetFatherComponent() BaseComponent 获取父组件(Connection/Component/Detail/CustomPart)。无父组件时返回 null
GetHierarchicObjects() ModelObjectEnumerator 获取关联的层级对象
GetAllUserProperties(ref Hashtable) bool 获取全部用户自定义属性(UDA),以键值对形式存入 Hashtable
GetStringUserProperties(ref Hashtable) bool 获取字符串类型的 UDA
GetDoubleUserProperties(ref Hashtable) bool 获取双精度浮点型的 UDA
GetIntegerUserProperties(ref Hashtable) bool 获取整数类型的 UDA
GetUserProperty(string, ref string) bool 按名称获取单个字符串 UDA(3 个重载:string/double/int)
SetUserProperty(string, string) bool 按名称设置单个 UDA(3 个重载:string/double/int)
GetAllReportProperties(...) bool 批量获取报告属性(支持 String/Double/Integer 三种类型)
GetReportProperty(string, ref T) bool 按名称获取单个报告属性(3 个重载)
GetDynamicStringProperty(string, ref string) bool 从数据库获取动态字符串属性(长文本自动分段拼接)
SetDynamicStringProperty(string, string) bool 设置动态字符串属性(超过512字符自动分段写入)
GetCoordinateSystem() CoordinateSystem 获取对象的局部坐标系统。返回 null 表示无效
SetPhase(Phase) bool 设置对象的施工阶段。需 Identifier 有效
GetPhase(out Phase) bool 获取对象的施工阶段信息
SetLabel(string) int 设置对象标签。传入 null 抛出 ArgumentNullException
CompareTo(object) int 实现 IComparable,按 Identifier.ID 比较
Equals(ModelObject) bool 实现 IEquatable,按 ID 判断同一模型实体

5.3 内部 / 受保护方法

方法 访问级别 说明
DeleteInstance() protected 执行实际删除操作,通过 ExportDeleteObject 调用底层 API
ToStruct(ref dotModelObject_t) internal 将 ModelObject 序列化为互操作结构体
FromStruct(ref dotModelObject_t) internal 从互操作结构体反序列化回 ModelObject
InitializeProperties(...) private static 批量初始化属性查询请求(每次最多 10 个属性)
IntGetProperties(...) private 内部属性查询入口,封装 ExportGetProperties 调用
IntSetUserProperty(...) private 内部属性设置入口,封装 ExportSetProperty 调用

六、完整代码示例

示例 1:创建 Beam 对象并插入模型

using System;
using Tekla.Structures.Model;
using Tekla.Structures.Geometry3d;

public void CreateBeamExample()
{
    // 创建一个新的 Beam 对象(继承自 ModelObject)
    Beam myBeam = new Beam();

    // 此时 myBeam.Identifier.ID == 0(无效),仅存在于内存
    Console.WriteLine($"Before Insert - ID: {myBeam.Identifier.ID}");  // 输出: 0

    // 设置必要属性
    myBeam.StartPoint = new Point(0, 0, 0);
    myBeam.EndPoint = new Point(0, 0, 5000);
    myBeam.Profile.ProfileString = "HEA400";
    myBeam.Material.MaterialString = "S235JR";
    myBeam.Class = "2";  // 构件等级

    // 调用 Insert() 将对象写入模型数据库
    bool insertSuccess = myBeam.Insert();

    if (insertSuccess)
    {
        Console.WriteLine($"Beam inserted! ID: {myBeam.Identifier.ID}");
        Console.WriteLine($"Label: {myBeam.Identifier}");
        // 插入后 Identifier.ID 变为有效值(非零正整数)
    }
    else
    {
        Console.WriteLine("Failed to insert beam. Check geometry and properties.");
        // 常见失败原因:起点终点重合、型材不存在、材料无效等
    }
}

示例 2:查询对象属性与修改

public void QueryAndModifyBeam()
{
    // 通过 Identifier 选择已有对象
    Beam beamToQuery = new Beam();
    beamToQuery.Identifier = new Identifier(123456);  // 已知的目标 ID

    if (beamToQuery.Select())  // 从数据库加载数据
    {
        // ===== 读取基本属性 =====
        Console.WriteLine($"Start: {beamToQuery.StartPoint}");
        Console.WriteLine($"End:   {beamToQuery.EndPoint}");
        Console.WriteLine($"Profile: {beamToQuery.Profile.ProfileString}");

        // ===== 读取用户自定义属性 (UDA) =====
        string commentValue = "";
        if (beamToQuery.GetUserProperty("COMMENT", ref commentValue))
        {
            Console.WriteLine($"Comment UDA: {commentValue}");
        }

        double weight = 0;
        if (beamToQuery.GetUserProperty("WEIGHT", ref weight))
        {
            Console.WriteLine($"Weight UDA: {weight} kg");
        }

        // ===== 读取报告属性 =====
        string reportName = "";
        beamToQuery.GetReportProperty("NAME", ref reportName);
        Console.WriteLine($"Report NAME: {reportName}");

        // ===== 读取修改时间和同步状态 =====
        Console.WriteLine($"Modification Time: {beamToQuery.ModificationTime}");
        Console.WriteLine($"Is Up To Date: {beamToQuery.IsUpToDate}");

        // ===== 读取施工阶段 =====
        Phase currentPhase;
        if (beamToQuery.GetPhase(out currentPhase))
        {
            Console.WriteLine($"Phase: {currentPhase.PhaseNumber} - {currentPhase.PhaseName}");
        }

        // ===== 修改属性 =====
        beamToQuery.Class = "3";  // 更改构件等级

        // 修改 UDA
        beamToQuery.SetUserProperty("COMMENT", "Modified by API");
        beamToQuery.SetUserProperty("WEIGHT", 1234.5);

        // 提交修改到数据库
        bool modifyOk = beamToQuery.Modify();
        Console.WriteLine($"Modify result: {modifyOk}");
    }
    else
    {
        Console.WriteLine("Object not found or selection failed.");
    }
}

示例 3:删除对象

public bool DeleteModelObjectByIdentifier(long objectId)
{
    try
    {
        // 使用 Beam 作为示例,任何 ModelObject 子类均可
        Beam targetBeam = new Beam();
        targetBeam.Identifier = new Identifier(objectId);

        // 先 Select 验证对象是否存在
        if (!targetBeam.Select())
        {
            Console.WriteLine($"Object with ID={objectId} does not exist.");
            return false;
        }

        // 执行删除操作
        bool deleteResult = targetBeam.Delete();

        if (deleteResult)
        {
            Console.WriteLine($"Successfully deleted object ID={objectId}");
            // 删除后 Identifier.ID 被置为 0
            Console.WriteLine($"ID after delete: {targetBeam.Identifier.ID}");
        }
        else
        {
            Console.WriteLine($"Delete failed for object ID={objectId}");
            // 可能原因:权限不足、对象被锁定、被其他对象引用等
        }

        return deleteResult;
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Error during delete: {ex.Message}");
        return false;
    }
}

// 批量删除示例:删除特定装配体内的所有构件
public void DeleteAllComponentsInAssembly(long assemblyId)
{
    Assembly asm = new Assembly();
    asm.Identifier = new Identifier(assemblyId);

    if (asm.Select())
    {
        ModelObjectEnumerator children = asm.GetChildren();
        int deletedCount = 0;

        foreach (ModelObject child in children)
        {
            if (child.Delete())
                deletedCount++;
        }

        Console.WriteLine($"Deleted {deletedCount} child objects from assembly.");
    }
}

示例 4:遍历模型对象

using System.Collections;

public void EnumerateAllBeams()
{
    Model model = new Model();

    // ===== 方式一:获取所有 Beam 类型对象 =====
    ModelObjectEnumerator beamEnumerator =
        model.GetModelObjectSelector().GetAllObjectsWithType(
            ModelObject.ModelObjectEnum.BEAM);

    ArrayList beamList = new ArrayList();

    while (beamEnumerator.MoveNext())
    {
        Beam currentBeam = beamEnumerator.Current as Beam;
        if (currentBeam != null && currentBeam.Select())
        {
            beamList.Add(currentBeam);

            // 读取每根梁的信息
            string profile = "";
            if (currentBeam.GetUserProperty("PROFILE", ref profile))
            {
                Console.WriteLine($"[{currentBeam.Identifier.ID}] " +
                    $"({currentBeam.StartPoint.X:F0}, {currentBeam.StartPoint.Y:F0}, {currentBeam.StartPoint.Z:F0}) " +
                    $"-> Profile: {profile}");
            }
        }
    }
    Console.WriteLine($"Total beams found: {beamList.Count}");

    // ===== 方式二:遍历装配体及其子对象 =====
    ModelObjectEnumerator asmEnumerator =
        model.GetModelObjectSelector().GetAllObjectsWithType(
            ModelObject.ModelObjectEnum.ASSEMBLY);

    while (asmEnumerator.MoveNext())
    {
        Assembly asm = asmEnumerator.Current as Assembly;
        if (asm != null && asm.Select())
        {
            Console.WriteLine($"\nAssembly ID: {asm.Identifier.ID}");

            // 获取装配体的所有子对象
            ModelObjectEnumerator children = asm.GetChildren();
            while (children.MoveNext())
            {
                ModelObject child = children.Current;

                // 获取子对象的父组件信息
                BaseComponent father = child.GetFatherComponent();
                string fatherInfo = father != null ?
                    father.Identifier.ToString() : "(none)";

                Console.WriteLine($"  Child [{child.GetType().Name}] " +
                    $"ID={child.Identifier.ID}, FatherComp={fatherInfo}");

                // 读取子对象的坐标系统
                CoordinateSystem coordSys = child.GetCoordinateSystem();
                if (coordSys != null)
                {
                    Console.WriteLine($"    Origin: {coordSys.Origin}");
                }
            }
        }
    }

    // ===== 方式三:按条件筛选 + 排序 =====
    ModelObjectEnumerator allObjects =
        model.GetModelObjectSelector().GetAllObjects();

    ArrayList sortedList = new ArrayList();
    while (allObjects.MoveNext())
    {
        ModelObject obj = allObjects.Current;
        sortedList.Add(obj);
    }

    // 利用 IComparable 接口进行排序
    sortedList.Sort();  // 按 Identifier.ID 升序排列

    Console.WriteLine("\n--- Sorted objects by ID ---");
    foreach (ModelObject obj in sortedList)
    {
        Console.WriteLine($"  {obj.GetType().Name,-20} ID={obj.Identifier.ID}");
    }
}

七、常见问题与注意事项

  • Identifier 必须有效:几乎所有需要与数据库交互的方法都要求 Identifier.IsValid() == true。新对象在 Insert() 之前 ID 为 0(无效)。
  • Select 后才能操作属性:通过 new Beam() 创建的空对象不包含数据库中的真实属性值。必须先调用 Select() 才能正确读取和修改属性。
  • Insert 前必须设置必要属性:不同子类有不同的前提要求。例如 Beam 需要 StartPoint、EndPoint、Profile、Material 等有效值。
  • ModificationTime 延迟加载:首次访问时会调用底层 API 获取时间戳。频繁遍历大量对象时可能产生性能开销。标记 [NonSerialized]
  • UserProperty vs ReportProperty:GetUserProperty 读取用户自定义属性(UDA),GetReportProperty 读取模板计算出的报告属性。两者是不同的数据源。
  • 动态字符串属性的 512 字符限制:SetDynamicStringPropertyGetDynamicStringProperty 在底层以 512 字符为单位分段传输,API 会自动处理。
  • Delete 后 ID 归零:删除成功后执行 Identifier.ID = 0,该对象引用已失效,不能再用于其他操作。
  • Equals 基于 Identifier 比较:两者都有有效 Identifier 时仅比较 ID;若任一方无效,则退回到 object.Equals() 引用比较。
  • CompareTo 用于集合排序:实现了 IComparable 接口,可直接对 ArrayList 调用 Sort() 按 ID 升序排列。
性能优化建议:
1. 批量操作优于单条操作 — 使用 GetAllUserProperties 一次性获取所有 UDA
2. 减少 Select 调用 — 通过模型选择器遍历得到的对象已经过 Select
3. 避免不必要的 ModificationTime 读取 — 大批量遍历时跳过此属性
4. 事务性操作 — 对于大批量插入/修改,使用 Model.CommitChanges() 进行事务管理

八、总结

  • ModelObject 是 Tekla Open API 中最核心的抽象基类,所有模型对象均继承自此类。
  • 声明为 abstract 不可直接实例化,需通过具体子类(BeamContourPlate 等)创建对象。
  • 提供了 4 个抽象 CRUD 方法(InsertSelectModifyDelete),子类必须实现。
  • 用户属性(UDA)与报告属性(Report)操作均提供 string/double/int 三种类型的读写重载。
  • GetDynamicStringProperty / SetDynamicStringProperty 支持超过 512 字符的长文本,自动分段传输。
  • 通过 GetCoordinateSystem() 获取局部坐标系统,SetPhase()/GetPhase() 管理施工阶段。
  • 实现了 IComparableIEquatable,支持按 ID 排序和相等比较。
  • ModificationTime 采用延迟加载,IsUpToDate 指示同步状态。
  • 内部通过 DelegateProxy 层与 Tekla C++ 内核通信,所有属性操作经 P/Invoke 调用。
 

本文适用于 Tekla Structures 2017 及以上版本。

 

QQ_1778130875151.png

QQ_1778130894832.png

评论 0

sitemap