从0开始——CAD与Tekla开发入门

背景

因工作需要,想尝试一下从autoCAD读取图纸数据,并直接在Tekla Structures建模。之前有使用过VS2010,用vb.net和c#.net开发过一点程序,这次想继续用VS2010进行开发。由于将要从0开始学习CAD和Tekla两款软件的二次开发,等完成后,对其他人也有些参考价值,所以本文将记录下整个的摸索过程。
目前的大致思路是:
- 通过CAD插件读取数据
- 储存原始数据
- 原始数据经过计算后,生成模型参数数据
- 通过Tekla插件将模型绘制出来


尝试过程

2016.08.23

第1步目标:外部程序调用Tekla的API,在模型里绘制简单图形(已实现)

目前已实现,稍后补上。

第2步目标:外部程序调用autoCAD的API,在图纸上绘制简单图形(已实现)

经过搜索,发现CAD二次开发主分为:VBA、Lisp、ObjectARX和.Net。从我的开发经历看,.Net应该是最满足项目需求的——操作体验与保密,所以首先尝试.Net的方案。
我用的是autoCAD2014,找到这篇文章AutoCAD .NET API二次开发学习指南
去到autoCAD官网的资料区,里面有各种文档和安装程序,可惜没有找到中文网页。
之前搜索ObjectARX时,有下载2014版的SDK,是在新浪博客找到的,链接也是官网的。
从资料区看,autoCAD2014支持VS2010和VS2012,看来运气还不错。
安装完ObjectARX SDK,确切地说是解压了之后,在VS2010里并没有出现ObjectARX的模板。于是按照学习指南的步骤,又安装了.NET Wizards,程序是直接在官网下载的。在安装.NET Wizards之前,我还安装了ObjectARX Wizards,同样是在官网找到的。ObjectARX Wizards安装了之后,在VS2010里C++下出现了AutoCAD的模板,安装.NET Wizards之后,才在C#下出AutoCAD的模板。
C#下的新模板
新建了一个工程,里面已经添加了autoCAD的一些引用,直接编译,没有报错。
这里写图片描述
生成的是.dll文件,不能直接运行,我又另外新建了一个普通工程TestModel,想添加引用,看能不能编译通过。因为dll的插件里是空的,我想起了之前在github下载的一个cad 插件工程,当时已经编译通过生成了dll文件。于是,我用它来测试引用,顺便看看能不能操作autoCAD。
我先是只添加了dll的引用和using声明,编译通过,但运行时报错:”未能加载文件或程序集“Acdbmgd.dll”或它的某一个依赖项。找不到指定的模块。“。添加对Acdbmgd.dll等几个文件的引用,还是报错。
这里写图片描述
还是看文档吧,下载了AutoCAD 2014 .Net Training,里面有个PPT,英文的。看了几页,惊呆了!居然是用netload加载dll到autoCAD!试了一下,顺利地运行了一个画直线的命令,果然是打开的方式不对。可是,这种用法,真的不符合我的预期啊!
这里写图片描述
但我不死心,继续找。终于找到《外部.NET程序与AutoCAD交互》,在TestModel中添加了“AutoCAD 2014 Type Library”引用,但添加”using Autodesk.AutoCAD.Interop;“时提示错误。搜索“”,找到一个帖子,以下一个回答。

晕,还没有人回答你噢,我来说一下吧,首先第一步你要添加两个COM引用:
AutoCAD 2010 Type Library //我的机子里装的是CAD2010版
AutoCAD/ObiectDBX Common 18.0 Type Library
把这两个引用的命名空间引进来
using Autodesk.AutoCAD.Interop;
using Autodesk.AutoCAD.Interop.Common;
//就可以写代码了
AcadApplication CadApp; //定义一个CAD应用程序对像
AcadDocument CadDoc; //定义一个CAD文档
AcadModelSpace CadSpace; //这是CAD的模型空间
//然后就先一个CAD应用程序
CadApp = new AcadApplication();
CadApp.Visible = true; //如果你想把CAD显示到前台来,就设为true
CadDoc = CadApp.ActiveDocument; //获取CAD当前活动的文档
CadSpace = (AcadModelSpace)CadDoc.ModelSpace; //获得命名空间
double[] c = new double[6]; //我随便定义一个要画线的点数组,记得点这个数组里是按XYZ坐标顺序存放的噢
c[0] = 34; c[1] = 98; c[2] = 67; c[3] = 956; c[4] = 655; c[5] = 322; //随便输的
CadSpace.Add3DPoly(c); //我这里画的是三维多段线,你要画其它的线型的话,到找一下对应的方法即可。
别忘了记得给分噢!

但还是提示错误,我用Everything搜索“Autodesk.AutoCAD.Interop.dll”,在ObjectARX SDK里找到了这个文件,有32位、64位两种,还看到了Autodesk.AutoCAD.Interop.Common.dll,我引用了64位的这两个文件,错误消除了。直接编译运行,成功打开了一个新autoCAD进程,画出了一条线。
这里写图片描述
这里写图片描述
这里写图片描述

在《外部.NET程序与AutoCAD交互》找到调用已打开autoCAD的代码,修改后如下,也成功地在autoCAD里画了一条线。后来发现,只添加两个dll,不添加COM引用,也是可以的。

        private void button2_Click(object sender, EventArgs e)
        {
            const string progID = "AutoCAD.Application.19.1"; 
            AcadApplication acApp = null; 
            try 
            { 
                acApp = (AcadApplication)Marshal.GetActiveObject(progID); 
            }catch{
                try { 
                    Type acType = Type.GetTypeFromProgID(progID); 
                    acApp = (AcadApplication)Activator.CreateInstance(acType, true); 
                } catch { 
                    MessageBox.Show("Cannot create object of type \"" + progID + "\""); 
                } 
            }
            if (acApp != null){ 
                // By the time this is reached AutoCAD is fully  
                // functional and can be interacted with through code   
                acApp.Visible = true;
                AcadDocument CadDoc;  //定义一个CAD文档
                AcadModelSpace CadSpace;  //这是CAD的模型空间
                //然后就先一个CAD应用程序
                acApp.Visible = true;  //如果你想把CAD显示到前台来,就设为true
                CadDoc = acApp.ActiveDocument;  //获取CAD当前活动的文档
                CadSpace = (AcadModelSpace)CadDoc.ModelSpace; //获得命名空间
                double[] c = new double[6];  //我随便定义一个要画线的点数组,记得点这个数组里是按XYZ坐标顺序存放的噢
                c[0] = 34; c[1] = 98; c[2] = 67; c[3] = 956; c[4] = 655; c[5] = 322;  //随便输的
                CadSpace.Add3DPoly(c);
                MessageBox.Show("hello");
            }
        }

现在能启动autoCAD了,后面就是想办法把插件dll加载进去。

第3步目标:外部程序调用autoCAD .Net API(未实现)

第2步完成的,其实是通过COM方式操作的autoCAD。《外部.NET程序与AutoCAD交互》虽然写了怎样加载.Net程序集来调用.Net API,但一直出现应用程序加载失败。看来事情并不是那么简单。COM方式操作autoCAD难以得到返回值,而我的目标是读取数据。
看来这条路暂时不通,调整目标:用acad2014.lsp自动加载dll插件,通过插件与外部程序通讯。

C:\Program Files\Autodesk\AutoCAD 2014\Support\acad2014.lsp
(COMMAND "NetLoad" "D:\\test.dll")

2016.08.24

第4步目标:用.Net插件在图纸上画常用图形、标注等(部分实现)

先看《AutoCAD 2014 .Net Training》里的教程,把里面的示例工程都认真看了一遍。从1到8,每个示例添加一点功能,发现它把常用的功能都演示了下:画圆、画块、database、Jigs、PointMonitor、添加右键、添加窗体。可惜没说Ribbon,我找到AutoCAD中程序创建Ribbon界面执行AutoCAD命令,本想直接添加到示例8,但引用老是有问题。然后,我用Wizard新建了个工程,引用全部选中,把代码复制进去,又添加System.Xaml引用和using Autodesk.Windows;终于编译通过,创建出了一个空白Ribbon Tab。

using System;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.EditorInput;

using Autodesk.Windows;

        private const string MY_TAB_ID = "MY_TAB_ID";

        [CommandMethod("addMyRibbon")]
        public void createRibbon()
        {
            Autodesk.Windows.RibbonControl ribCntrl =
                      Autodesk.AutoCAD.Ribbon.RibbonServices.RibbonPaletteSet.RibbonControl;
            //can also be Autodesk.Windows.ComponentManager.Ribbon;     

            //add the tab
            RibbonTab ribTab = new RibbonTab();
            ribTab.Title = "My custom tab";
            ribTab.Id = MY_TAB_ID;
            ribCntrl.Tabs.Add(ribTab);

            //create and add both panels
            addPanel1(ribTab);
            addPanel2(ribTab);

            //set as active tab
            ribTab.IsActive = true;
        }

        private void addPanel2(RibbonTab ribTab)
        {
        }

        private void addPanel1(RibbonTab ribTab)
        {
            //throw new NotImplementedException();
        }

这里写图片描述
这里写图片描述
这里写图片描述
当我想继续添加Panel和Button时,又出现引用问题了,有一个类的声明老找不到。

2016.08.25

折腾了半天,发现是自定义的。。。补上之后,终于添加上按钮了。

//添加按钮的代码
        private void addPanel2(RibbonTab ribTab)
        {
            //create the panel source
            RibbonPanelSource ribPanelSource = new RibbonPanelSource();
            ribPanelSource.Title = "Edit Registry";

            //create the panel
            RibbonPanel ribPanel = new RibbonPanel();
            ribPanel.Source = ribPanelSource;
            ribTab.Panels.Add(ribPanel);

            //create button1
            RibbonButton ribButtonDrawCircle = new RibbonButton();
            ribButtonDrawCircle.Text = "My Draw Circle";
            ribButtonDrawCircle.ShowText = true;
            //pay attention to the SPACE after the command name
            ribButtonDrawCircle.CommandParameter = "DrawCircle ";
            ribButtonDrawCircle.CommandHandler = new AdskCommandHandler();
            ribPanelSource.Items.Add(ribButtonDrawCircle);
        }

        private void addPanel1(RibbonTab ribTab)
        {
            //throw new NotImplementedException();
        }
        [CommandMethod("DrawCircle")]
        public void DrawCircle()
        {
            //画个圆,实现在此略去,这不是这篇blog的重点。
            Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
            PromptPointOptions getPointOptions = new PromptPointOptions("Pick Center Point : ");
            PromptPointResult getPointResult = ed.GetPoint(getPointOptions);
            if ((getPointResult.Status == PromptStatus.OK))
            {
                PromptDistanceOptions getRadiusOptions = new PromptDistanceOptions("Pick Radius : ");
                getRadiusOptions.BasePoint = getPointResult.Value;
                getRadiusOptions.UseBasePoint = true;
                PromptDoubleResult getRadiusResult = ed.GetDistance(getRadiusOptions);
                if ((getRadiusResult.Status == PromptStatus.OK))
                {
                    Database dwg = ed.Document.Database;
                    Transaction trans = dwg.TransactionManager.StartTransaction();
                    try
                    {
                        Circle circle = new Circle(getPointResult.Value, Vector3d.ZAxis, getRadiusResult.Value);
                        BlockTableRecord btr = (BlockTableRecord)trans.GetObject(dwg.CurrentSpaceId, OpenMode.ForWrite);
                        btr.AppendEntity(circle);
                        trans.AddNewlyCreatedDBObject(circle, true);
                        trans.Commit();
                    }
                    catch (System.Exception ex)
                    {
                        ed.WriteMessage("problem due to " + ex.Message);
                    }
                    finally
                    {
                        trans.Dispose();
                    }
                }
            }
        }
//以下是响应按钮的自定义类
    class AdskCommandHandler : System.Windows.Input.ICommand
    {
        public bool CanExecute(object parameter)
        {
            return true;
        }

        public event EventHandler CanExecuteChanged;

        public void Execute(object parameter)
        {
            //is from Ribbon Button
            RibbonButton ribBtn = parameter as RibbonButton;
            if (ribBtn != null)
            {
                //execute the command 
                Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.SendStringToExecute((string)ribBtn.CommandParameter, true, false, true);
            }
        }
    }

这里写图片描述
这一部分的工程代码到此处可下载

第5步目标:把ribbon、窗体、右键整合到同一个插件工程(已实现)

2016.08.26
把Ribbon、palette、右键的代码整合到一起之后,可以通过命令来加载它们了。但为了方便,我想让CAD启动时,自动加载。

        void IExtensionApplication.Initialize()
        {
            AddContextMenu();
            createRibbon(); 
            palette();
         }

        void IExtensionApplication.Terminate()
        {
            RemoveContextMenu();            
        }

按理说,上面的代码就能让CAD在启动时加载右键、Ribbon、窗体了,但Dll加载却出问题了。逐一测试后,发现是Ribbon加载有问题。在《 打开cad如何自动加载ribbon菜单》找到了答案,原来是要等待Ribbon启动。原样复制代码,却提示“No overload for ‘ComponentManager_ItemInitialized’ matches delegate ‘System.EventHandler’”。还好最后找到另一篇《获取Ribbon 选项卡(Tab)被点击的消息》,添加了一个“< RibbonItemEventArgs >”,错误消除。

        void IExtensionApplication.Initialize()
        {
            AddContextMenu();
            Autodesk.Windows.ComponentManager.ItemInitialized += new EventHandler<RibbonItemEventArgs>(ComponentManager_ItemInitialized); 
            palette();
         }

        void IExtensionApplication.Terminate()
        {
            RemoveContextMenu();
        }
        void ComponentManager_ItemInitialized(object sender, RibbonItemEventArgs e)
        {
            if (Autodesk.Windows.ComponentManager.Ribbon != null)
            {
                createRibbon();
                Autodesk.Windows.ComponentManager.ItemInitialized -= new EventHandler<RibbonItemEventArgs>(ComponentManager_ItemInitialized);
            }
        }

这里写图片描述
自动加载使用的是注册表法,为了不让CAD提示警告,还把dll放在了CAD安装目录下。

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Autodesk\AutoCAD\R19.1\ACAD-D001\Applications\CadDataCapture]
"LOADCTRLS"=dword:0000000e
"LOADER"="C:\\Program Files\\Autodesk\\AutoCAD 2014\\CadDataCapture.dll"
"DESCRIPTION"="AutoCAD Data Capture"
"MANAGED"=dword:00000001

[HKEY_LOCAL_MACHINE\SOFTWARE\Autodesk\AutoCAD\R19.1\ACAD-D001\Applications\CadDataCapture\Commands]
"palette"="CadDataCapture.resources.dll#palette"

[HKEY_LOCAL_MACHINE\SOFTWARE\Autodesk\AutoCAD\R19.1\ACAD-D001\Applications\CadDataCapture\Groups]
"CadDataCapture_CMDS"="CadDataCapture_CMDS"

但还有一个问题,就是palette加载之后,重启CAD,会有两个palette。这个小问题就不纠结了,这个目标算是完成了。
这部分的工程代码可在此处下载

暂告一段落

后面的开发研究就不具备普遍性了,所以文章就暂告一段落,一些工程代码随后上传。

  • 8
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值