Revit 插件产品架构梳理
一、前言
BIM:Building Information Modeling 建筑信息模型,就是将建筑的相关信息附着于模型中,以管理该建筑在设计、算量、施工、运维全生命周期的情况。创建模型的主要主流软件有Autodesk(欧特克)的Revit、Bentley的microstation、达索的CATIA(曾被我国在80、90年代用于制造战斗机,比较高端)。我所在的企业是从事BIM软件的,隶属于一家上市企业。
我在两个部门工作过。第一个部门是算量软件部门,专门制作基于Revit的算量插件(桌面端)。第二个部门是BIM数据平台部门,专门为大型企业制作BIM施工管控平台(Web及移动App端)。刚研究生毕业即进入公司,在两年整的工作中主要负责WPF(DevExpress)、识别算法、网络通信编程(Remoting、WebService、WCF、Socket)以及Web开发。
架构的改造也是我工作的一部分,站在公司老司机的肩膀上还是很爽的。那么,我就从桌面端软件开始吧!
二、架构简图
制作Revit插件必须要引用2个dll:RevitAPI和RevitAPIUI,每年根据Autodesk发布Revit产品进行迭代,支持C#、Managed C++及VB编程。

该解决方案共含有15个C#项目,红色模块都需要引用Revit的dll,灰色部分不用引用。编译完之后,在C:\ProgramData\Autodesk\Revit\Addins\201X添加相应的.addin文件,并指向Product.App的位置即可,然后运行Revit的软件即可使用插件。
<?xml version="1.0" encoding="utf-8"?>
<RevitAddIns>
<AddIn Type="Application">
<Name>Product</Name>
<Assembly>X:Path\Product.App.dll</Assembly>
<ClientId>a21827a2--445b-a625-8d9f4a52f218</ClientId>
<FullClassName>Product.App.ExternalApplication</FullClassName>
<VendorId>BIMproduct</VendorId>
<VendorDescription>BIMproduct www.product.com</VendorDescription>
</AddIn>
</RevitAddIns>
三、架构解读
1. App201X
该项目是Revit插件的入口,主要由ExternalApplication与ExternalCommand组成。需要引用其它的项目。该项目的ExternalApplication需要实现一个Revit接口:IExternalApplication。
ExternalApplication在OnStartup函数中可以做一些初始化工作,比如初始化依赖反转容器、创建Ribbon按钮、初始化载入一些第三方dll、产品的授权检测等等。
namespace Product.App
{
public class ExternalApplication:IExternalApplication
{ public Result OnStartup(UIControlledApplication application)
{
try
{
#if !REVIT2014
CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("zh-CN");
#endif
Thread.CurrentThread.CurrentCulture = new CultureInfo("zh-CN");
Thread.CurrentThread.CurrentUICulture = new CultureInfo("zh-CN"); // Refer to: https://stackoverflow.com/questions/4041197/how-to-set-and-change-the-culture-in-wpf
// 使用“System.Windows.Documents.TextElement”以和QS或其他插件中的“FrameworkElement”区别
FrameworkElement.LanguageProperty.OverrideMetadata(
typeof(System.Windows.Documents.TextElement),
new FrameworkPropertyMetadata(
XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag)));
}
catch (Exception ex)
{
if (ex is System.ArgumentException)
{
// 有可能“System.Windows.Documents.TextElement”已被注册过. Do nothing
}
else
{
return Result.Succeeded;
}
} try
{
//Initialize
}
catch (Exception ex)
{
return Result.Succeeded;
}
return Result.Succeeded;
} public Result OnShutdown(UIControlledApplication application)
{
try
{
//shoutdown
}
catch (Exception)
{
// do nothing...
}
return Result.Succeeded;
}
}
}
ExternalCommand主要负责插件上的按钮,以“关于”按钮为例:
[Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
[Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)]
public class AboutCommand : ExternalCommandBase
{
protected override Result RunImpls(ExternalCommandData commandData, ref string message, ElementSet elements)
{
AboutWnd aboutWnd = new AboutWnd();
// 出现异常时,关闭窗体
try
{
aboutWnd.ShowDialog();
}
finally
{
aboutWnd.Close();
}
return Result.Succeeded;
} public override bool IsCommandAvailable(UIApplication applicationData, CategorySet selectedCategories)
{
return true;
} public override bool IsProductLicenseValid()
{
return true;
}
}
可以看到该命令并没有直接实现IExternalCommand, IExternalCommandAvailability两个接口。而是再封装一个抽象基类,让抽象基类实现Revit的这俩个接口。然后相关的Command再继承这个抽象基类。为什么这么做是因为抽象基类中还可以做一些检查授权等等的事情。
namespace Product.App
{
public abstract class ExternalCommandBase:IExternalCommand, IExternalCommandAvailability
{
protected static bool _allowMultiRun = false;
protected static bool _isRunning = false; /// <summary>
///
/// </summary>
/// <param name="commandData"></param>
/// <param name="message"></param>
/// <param name="elements"></param>
/// <returns></returns>
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
if(!_allowMultiRun && _isRunning)
{
return Result.Cancelled;
} if (!IsProductLicenseValid())
{
Container.Resolve<IMessageBox>().ShowInfo("授权失败,无法使用!");
return Result.Cancelled;
} _isRunning = true; try
{
string msg = $"run command '{this.GetType().Name}'";
Container.Resolve<ILog>().Info(msg);
var res = RunImpls(commandData, ref message, elements);
_isRunning = false;
return res;
}
catch (Autodesk.Revit.Exceptions.OperationCanceledException)
{
_isRunning = false;
// generally, it's select cancel exception
return Result.Cancelled;
}
catch (Exception ex)
{
_isRunning = false;
if (ex.Message== "The active view is non-graphical and does not support capture of the focus for pick operations.")
{
Container.Resolve<IMessageBox>().ShowWarning("当前焦点不在视图区域,请将焦点停留在某个视图中。");
return Result.Failed;
}
string errorMsg = $"exception in command '{this.GetType().Name}': {ex}";
Container.Resolve<ILog>().Error(errorMsg, ex);
message = ex.Message;
return Result.Failed;
}
} protected string GetProductName()
{
return Container.Resolve<IApplication>().ProductLicenseName;
} /// <summary>
/// Implements this method to provide implementation for Execute
/// </summary>
/// <param name="commandData"></param>
/// <param name="message"></param>
/// <param name="elements"></param>
/// <returns></returns>
protected abstract Result RunImpls(ExternalCommandData commandData, ref string message, ElementSet elements); public virtual bool IsCommandAvailable(UIApplication applicationData, CategorySet selectedCategories)
{
return false;
} public virtual bool IsProductLicenseValid()
{
#if DEBUG
return true;
#else
return LicenseService.IsRegistered(GetProductName(), Container.Resolve<IApplication>().LicenseServerURL);
#endif
}
}
}
2. Common、Common.RevitExt201X
Common项目主要定义基本的数据接口和定义依赖反转容器,注意该项目中并没有引用Revit的dll,说明其中的数据定义是不依赖于Revit的。
与Revit有关的数据定义都放在Common.RevitExt201X,比如自定义的DockablePanel接口(继承IDockablePaneProvider)。
3. UI、UI.Common、UI.RevitExt201X
UI项目主要用于制作点击命令后产生的界面UI,该项目采用MVVM框架进行界面实现,文件夹梳理可如下:

UI.Common主要是用于制作可复用的自定义控件、进度条、常用Converter;
UI.RevitExt201X主要是用于控制DockablePanel和控制Revit构件显示效果,其依赖于Revit。
4. Resource、Unity4Net、Report201X
Resource项目用于多语言版本;
Unity4Net项目用于对Windows的一些操作进行封装;
Report201X项目用于输出报表。报表因为其有一定的业务逻辑,并不是一个单纯的UI界面汇总,所以单独做成一个project会比较好。
5. RevitAPIUtil201X
该项目用于对rvt中的构件进行直接的API操作,但不含有任何的业务逻辑。比如获取构件的若干面,获取长度等等。
6. Core201X
该项目用于核心业务逻辑。当用户在UI界面中点击某按钮,其执行的核心在该项目中。
7. Foundation、Foundation.RevitExt201X
Foundation项目用于进行基本的常数定义,全局定义,授权检查配置等等。
Foundation.RevitExt201X与App201X息息相关,App201X中的ExternalApplication的OnStartup中的相关初始化逻辑比较多,将核心的部分抽出放在该项目中。
8. DBManager
该项目用于对数据库进行操作,Revit插件如果只用自带的revit数据缓存是不能满足现实需求的,需要使用小型数据库,推荐使用SqlLite数据库。
9. ProjectManager
因为算量软件的第一步是工程设置,之后计算的正确性都需要正确的工程设置,所以将工程设置单独抽出而不是放在Core201X中。
Revit 插件产品架构梳理的更多相关文章
- 【.NET架构】BIM软件架构01:Revit插件产品架构梳理
一.前言 BIM:Building Information Modeling 建筑信息模型,就是将建筑的相关信息附着于模型中,以管理该建筑在设计.算量.施工.运维全生命周期的情况.创建模 ...
- ODI学习笔记2--ODI产品架构
ODI学习笔记2--ODI产品架构 ODI产品架构: ODI提供了以下几种管理工具:Designer 用于定义数据转换逻辑,这是最常用的开发工具,大部分的开发任务,包括data store的定义,in ...
- MVC 4 插件化架构简单实现实例篇
ASP.NET MVC 4 插件化架构简单实现-实例篇 先回顾一下上篇决定的做法: 1.定义程序集搜索目录(临时目录). 2.将要使用的各种程序集(插件)复制到该目录. 3.加载临时目录中的程序集 ...
- [API 开发管理] EOLINKER 升级为多产品架构, AMS V4.5 版本常见问题汇总
自AMS4.5开始,eoLinker 全面升级为多产品架构,部分操作方式较以前有较大改变,本文针对改进部分做重点说明. 在说明之前,我们先通过以下的图文看看AMSV4.5更新了哪些内容: Q:我可以创 ...
- 智表(ZCELL)插件产品选型说明书,市场主流插件对比,帮您选型
智表(ZCELL)插件产品选型说明书,市场主流插件对比,帮您选型. 说明书下载:地址 一. 我们为什么需要智表插件产品 客户早已养成EXCEL中的操作习惯,BS架构下,普通的网页交互,与客户习惯 ...
- 基于ASP.NET MVC 4/5 Razor的模块化/插件式架构实现
概述 在日常开发中, 我们经常谈起模块化/插件化架构,这样可既可以提高开效率,又可以实现良好的扩展性,尤其对于产品化的系统有更好的实用性. 架构 我们采用的是MVC5(本文中介绍的方法对于MVC4也是 ...
- ASP.NET MVC 4 插件化架构简单实现-思路篇
用过和做过插件的都会了解插件的好处,园子里也有很多和讨论,但大都只些简单的加载程序集什么的,这里主要讨论的就是使用 ASP.NET MVC 4 来实现每个插件都可以完全从主站点剥离出来,即使只是一个插 ...
- C#实现插件式架构的方法
插件式架构,一种全新的.开放性的.高扩展性的架构体系.插件式架构设计近年来非常流行,基于插件的设计好处很多,把扩展功能从框架中剥离出来,降低了框架的复杂度,让框架更容易实现.扩展功能与框架以一种很松的 ...
- ASP.NET MVC 4 插件化架构简单实现-实例篇
先回顾一下上篇决定的做法: 1.定义程序集搜索目录(临时目录). 2.将要使用的各种程序集(插件)复制到该目录. 3.加载临时目录中的程序集. 4.定义模板引擎的搜索路径. 5.在模板引擎的查找页面方 ...
随机推荐
- Appium环境搭建——安卓模拟器(AVD)调试 1-创建模拟器失败点的总结
一.先安装好Android SDK 系统变量—>新建,变量名:ANDROID_HOME,变量值:SDK的安装路径 系统变量—>path—>点击编辑,变量值添加:;%ANDROID_H ...
- 上手d3js
0---什么是d3js: d3js是一个开源的,基于对svg操作的数据可视化框架,简单的说他提供了数据提供了一系列的数据可视化工具,可以用他很方便的创造出关于svg的动画:svg动画具有可伸缩,不失真 ...
- Actifio如何保护和管理Oracle-带外篇
引言 本文提供CDS带外环境下相关配置,保护和恢复Oracle的所需步骤. 目的是提供Oracle数据库配置前的详细说明,Actifio环境下发现和配置Oracle数据库,执行还原和恢复,以及配置Or ...
- 【转载】【原创】华硕F8TR笔记本更换主板及喇叭教程
转载地址:http://blog.sina.com.cn/s/blog_6241aaed0102w4e6.html [原创]华硕F8TR笔记本更换主板及喇叭教程 华硕AUSU F8TR笔记本 ...
- Yii2 设计模式——设计模式简介
我们首先来思考一个问题:作为工程师,我们的价值是什么? 笔者认为是——解决用户问题. 我们的任何知识和技能,如果不能解决特定的问题,那么就是无用的屠龙之术:我们的任何经验,如果不能对解决新的问题有用, ...
- docker mysql8 注意
1. mysql8 出了有段时间了,但公司项目的django还不支持mysql8的默认加密方式. 连接时报错 Error : The server requested authentication m ...
- HTTP协议初步解析
一.什么是HTTP协议 HTTP是hypertext transfer protocol(超文本传输协议)的简写,它是TCP/IP协议的一个应用层协议,定义了Web客户端向Web服务器请求Web页面的 ...
- Unity外包团队:U3D与UE我选哪个好?请别再问这种问题了!
原本预先决定的两家VR游戏公司采访,思熊和星为棋,并没有发现什么共性之初.结果在采访之后却意外发现,两家的经历有着非常相似的地方.他们都是来自于开发游戏所用的引擎的原开发商,比如思熊的主力来自Epic ...
- Azure CosmosDB (12) 创建Cosmos DB并执行查询语句
<Windows Azure Platform 系列文章目录> The SQL API supports the following aggregate functions. SUM an ...
- php正则表达式提取img alt/title标签并替换
有时我们需要对富文本编辑器中的img标签进行必要的处理以满足网站自身的需要,比如:根据站点关键词对页面内img的alt标签设定关键词,以下为提取并替换alt/title标签内容的正则: $title ...