
做电气设计的朋友应该都有这个体会图纸画到一百多页以后改一个端子号、统一一次部件规格动辄就要在几十张图纸里来回翻。EPLAN功能再强也不可能覆盖所有企业的自定义规则这时候“二次开发”就成了真正的救星。所谓EPLAN二次开发简单说就是利用EPLAN官方提供的API接口把那些重复的、容易出错的、按企业自身业务规则定制的操作写成程序自动完成并且可以做成自己的定制化界面让工程师点几下按钮就能搞定原本需要半天的工作。这篇文章是我自己从查阅API文档、搭建开发环境、写出第一个能运行的脚本到陆续交付了几个项目后的完整总结。适合手里有EPLAN使用基础、想往电气自动化方向走一步的电气工程师也适合刚接到EPLAN开发任务的软件工程师参考。我会尽量把开发前的选型思考、实际操作步骤、以及踩过的坑都写清楚内容偏实战不绕理论。1. 项目拆解——EPLAN二次开发到底在开发什么我刚接触EPLAN开发时犯过一个错误就是不加分辨地去网上找各种API资料结果看了一周文档也没理出头绪。后来带我的老工程师提醒了一句话先把需求想清楚再决定用哪种开发方式。所以第一步要做的不是写代码而是把项目需求拆开看它属于哪一类。1.1 先把需求拆成三类从我接触过和了解到的项目来看EPLAN二次开发的需求大致可以分为三类分类直接决定了你的技术路线也决定了修改前承接的人是谁。第一类是流程自动化。典型场景包括批量修改设备属性、按规则自动生成线号、中断点批量排序、图纸批量导出PDF、批量打印。这类需求的核心是把人工操作转成脚本调用通常不需要太多界面一个工具栏按钮加一个进度条就够了。难度在于你要理解EPLAN的菜单操作背后对应哪些API调用实际操作中不少菜单功能在API里并不是一一对应的需要自己拼装逻辑。第二类是数据集成。典型场景包括和ERP、MES、PDM系统同步部件库、读取BOM表到外部数据库、从PLC程序导入导出IO表、用外部Excel维护端子排并回填到图纸。这类需求的关键是搞清楚EPLAN的数据模型和外部数据结构之间的映射关系难点不在API本身而在你对外部系统的理解程度。比如用Excel回填端子排你得先定义清楚“哪一列对应端子上哪个属性”一旦映射错了写回图纸的数据就会串位。第三类是界面定制。典型场景是自己做一个WinForms或WPF窗口把常用的几十个操作集成到一个面板里或者在EPLAN的工具栏、右键菜单中挂载自己的功能。这类需求和前两类经常混在一起一个完整的工具往往是“界面加业务逻辑加API调用”的组合。很多企业要的其实不是一个“脚本”而是一个按钮就能分发下去的内部工具所以界面定制往往是项目里最出彩的部分。搞清楚需求属于哪一类以后你才能决定该把精力花在对象模型学习上还是花在界面框架上。如果一上来就研究界面结果需求只是批量导出PDF那就是南辕北辙了。1.2 技术选型为什么主流方案是C#EPLAN从较早版本开始就提供了完整的.NET接口官方示例代码主要用C#和VB.NET。我个人的建议是直接用C#理由有三个。第一C#在Visual Studio里的智能提示、调试体验最好。EPLAN的API对象很多类名很长没有智能提示纯靠手敲效率太低而且很容易敲错。第二社区里你能搜到的EPLAN开发资料绝大部分都是C#遇到问题更容易找到参考。VB.NET也能用不过现在新写的项目再选VB.NET的已经不多了。第三C#语法严谨处理EPLAN这种强类型的API对象时类型错误在编译期就能暴露不会拖到运行时才崩溃。至于网上有人问能不能用Python来做这里得说清楚EPLAN并没有提供官方的Python API核心的文档对象模型只能在.NET环境里完整访问。Python顶多可以做做外部数据预处理比如提前把Excel数据清洗好真正的流程控制和界面定制还是要靠C#或VB.NET。还有不少人问能不能用Python调用EPLAN的COM接口理论上可以但你会丢失大量的强类型支持代码写起来极度痛苦不建议。1.3 整体架构API、数据库和部件库是怎么配合的EPLAN二次开发的整体结构可以这样理解EPLAN本身是一个电气设计平台它把图纸、设备、端子、线号、部件库这些信息全部存进项目数据库。你写的程序通过API接口去操作这个数据库里的对象而不是直接去改数据库文件。直接改数据库是一件非常危险的事EPLAN的内部表结构很复杂而且不同版本之间可能有差异稍有不慎整个项目就损坏了。部件库是另一个重要的数据来源。我们在图纸上放置一个断路器本质上是在项目里创建了一个设备实例而这个实例会引用部件库中的一个部件定义包括型号、尺寸、图形符号都在部件库里。当你做批量属性修改或者导入导出时API操作的对象往往需要同时考虑“图纸中的实例”和“部件库中的定义”。这个关系如果没想清楚很容易出现“改了部件库所有用到这个部件的图纸全变了”之类的意外。我从实践里得到的经验是开发前先画一张简单的数据流图把“外部数据进来后经过哪些转换、最终写到EPLAN的哪个对象”标清楚。这张图不用多规范自己能看懂就行但它能帮你少走非常多弯路。我见过好几个开发同学代码写了一半才发现要修改的数据在部件库里而不在图纸上前面几十个小时基本白费。2. 环境搭建与第一个API程序2.1 开发环境准备环境搭建是很多新手一开始容易卡壳的地方。先说结论你需要一台装了EPLAN的Windows机器外加Visual Studio建议用2019或2022的Community版就够。版本匹配是个关键点。EPLAN Platform的版本差异会导致API行为不一致你开发的DLL所引用的EPLAN程序集版本要和实际安装的EPLAN版本匹配否则会出现类型不匹配或者加载失败。实际操作中我会在开发机上安装和客户一致的EPLAN版本。如果客户环境不一致最好在发布清单里写清楚最低版本要求。这里不建议同时装多个大版本很容易把注册表弄乱。安装完Visual Studio后在项目里添加引用。核心的几个程序集是EPLAN.EplApi.Application.dll——应用层入口负责连接EPLAN进程、获取项目对象。EPLAN.EplApi.DataModel.dll——数据模型包含Project、Page、Device、Placement等核心对象。EPLAN.EplApi.HEServices.dll——服务层提供各种业务服务接口。EPLAN.EplApi.Base.dll——基础类型和辅助工具。这些文件通常在EPLAN安装目录的Bin文件夹下。添加引用时建议把“复制本地”设为False避免发布时把EPLAN的DLL一起打进去否则发布到客户机器上会造成版本冲突。2.2 EPLAN对象模型先认识五个核心类EPLAN的API对象模型其实不复杂一通百通。你只需要先掌握几个核心类之间的关系就能跑通绝大多数业务流程。Project对应一个EPLAN项目文件。你所有的操作起点基本上都是拿到当前打开的Project对象。Page对应图纸页面一个项目里有若干张Page每张Page有类型、比例、尺寸等属性图纸上的图形符号和文字都放在Page上。Device对应设备实例。在EPLAN里放置一个接触器就是创建了一个Device对象它可以有一个或多个功能Function。Placement对应放置在页面上的对象可能是设备也可能是符号、文本、图框等。最后是PropertyEPLAN里几乎所有对象的属性都是通过Property对象来读写的。我用一句话概括Project是根下面挂着很多PagePage上有PlacementPlacement里面Device是主角Device的属性都要通过Property来读写。实际开发中你拿到Project以后通常通过Project.Pages拿到页面集合再通过页面上的Placements去定位对象然后通过Properties去读写具体属性。这个模型是你写所有业务逻辑的地基理解了它后面看API文档就不会觉得是看天书了。2.3 第一个程序连接正在运行的EPLAN并读取项目写第一个程序不用做太复杂的事目标就一个让外部程序能连上正在运行的EPLAN并读出当前项目的名称和页面总数。这一步跑通了你的开发环境就基本没问题了。EPLAN的API有两种常见调用方式一种是在EPLAN内部以脚本方式运行另一种是从外部程序连到EPLAN的进程。在EPLAN内部运行时你直接用Application对象就能拿到状态从外部连接时需要先挂接到正在运行的EPLAN实例。我通常是写一个WinForms程序在窗体加载时这样连接using EPLAN.EplApi.Application; using EPLAN.EplApi.DataModel; using EPLAN.EplApi.HEServices; using EPLAN.EplApi.Base; private void ConnectEplan() { // 获取当前正在运行的EPLAN实例 EplanApplication eplanApp new EplanApplication(); eplanApp.Attach(); // 挂接到正在运行的EPLAN进程 Project prj eplanApp.Project; if (prj null) { MessageBox.Show(请先在EPLAN中打开一个项目); return; } string prjName prj.ProjectLinkFilePath; int pageCount prj.Pages.Count; MessageBox.Show($当前项目{prjName}\n页面总数{pageCount}); }这段代码里最关键的是Attach方法。如果你的机器上开了多个EPLAN进程挂接的通常是当前激活的那个项目实例。所以在工具类exe开发时我会在界面上提示用户务必先打开目标项目再启动工具否则很容易连到不对的进程上。2.4 遍历图纸并统计设备数量第一步连上项目之后就能开始读数据了。下面这个例子是把当前项目里所有页面的名称和页面上的设备数量统计出来。它会用到Page和Placement的遍历这是所有后续开发里最常用的循环方式。private void CountDevicesOnPages(Project prj) { foreach (Page page in prj.Pages) { int deviceCount 0; foreach (Placement placement in page.Placements) { if (placement is Device) { deviceCount; } } Console.WriteLine($页面 {page.PageName} 有 {deviceCount} 个设备); } }初学阶段容易踩一个坑page.Placements里实际包含的是页面上所有可放置对象可能是Device也可能是Symbol、Text、图框、中断点等。所以判断类型那一步不能省。很多人统计不准就是因为把Placements全当成设备来数了。还有一个细节是EPLAN里设备的“数量”在不同语境下有不同含义。上面代码是统计放置实例数量如果你要统计的是“设备型号数量”或者“功能数量”逻辑会不一样。开发前先把统计口径定义清楚否则返工很麻烦。3. 定制化界面开发的完整思路界面定制是“从API到用户”的最后一公里也往往是项目验收时最容易被看到的成果。我自己做过的界面有三种常见形态各有优缺点这里分开讲。3.1 集成的三种形态外部程序、AddIn、内嵌选项卡第一种在EPLAN里添加自定义工具栏按钮点按钮后启动你的外部exe程序。这种方案最简单部署也容易只需要在EPLAN里手动配置一个命令行调用把外部程序的路径填进去就行。适合功能比较独立、和EPLAN交互频率不高的场景。第二种是做一个AddIn让EPLAN启动时自动加载你的程序集并在功能区里生成一个自定义选项卡或工具栏。这种方案集成度最高用户体验最好但开发和部署都更复杂。你要处理EPLAN启动时的加载逻辑、功能区的XML配置、版本兼容性等多个问题。我建议新手先不要直接做AddIn从外部程序做起把逻辑跑通后再考虑要不要提升集成度。第三种是做一个独立的WinForms或WPF窗口从工具栏按钮或菜单项打开所有操作都在窗口内完成。这是目前企业内部工具最常见的形态。窗口本身是独立exe通过API挂接到EPLAN视觉效果虽然不是嵌入式的但开发效率最高出问题也好排查。从实际项目看比较稳妥的组合是先用独立WinForms窗口把业务逻辑验证通过再花时间做AddIn集成。一上来就做AddIn调试一次要重启一次EPLAN效率非常低。3.2 做一个批量属性修改工具以一个真实需求为例项目中需要把一批接触器的品牌从A供应商换成B供应商或者批量修改设备的功能文本。手动改肯定不行几百个设备改下来不仅慢还容易漏。界面设计比较简单一个ListView用来展示和勾选设备几个文本框用于填写新的属性值再加一个“执行修改”按钮和进度条。核心代码如下private void btnUpdate_Click(object sender, EventArgs e) { // selectedDevices 是用户在ListView中勾选的设备集合 foreach (Device dev in selectedDevices) { // 修改设备属性这里的PROPERTIES常量需要根据实际需求选择 dev.Properties[PROPERTIES.PROJECT_PARTNUMBER] txtPartNumber.Text; dev.Properties[PROPERTIES.FUNCTION_FUNCTIONTEXT] txtFunctionText.Text; } MessageBox.Show(修改完成请刷新EPLAN视图); }这里要特别注意修改完属性后EPLAN视图不一定立刻刷新。很多时候你以为没改上其实改了只是界面没更新。可以在程序里调用视图刷新相关服务也可以让用户手动按F5刷新。我自己的习惯是在批量操作完成后自动触发一次刷新减少用户的困惑。另外像PROPERTIES常量这种东西每个版本都可能调整。开发时不要硬编码一个值最好通过API去读取对象现有属性做对比或者维护一张常量映射表。改版本时只需要更新映射表。3.3 界面与API交互的三个大坑界面定制看着简单但坑非常多。这里必须认真聊一下。第一个坑是线程模型。EPLAN的API不是线程安全的你在WinForms里开了一个BackgroundWorker或Task去调用API大概率会偶发崩溃。速度确实会快一点但稳定性会急剧下降。工业场景里稳定性远比那几秒的耗时重要。所以我的原则是所有API调用都在UI线程里执行长时间的批量操作可以配合进度条加DoEvents让界面不至于假死。第二个坑是事务回滚。EPLAN提供了事务机制批量修改时应该开启一个可回滚的操作栈否则用户执行一次工具后想用CtrlZ撤销结果发现一下撤掉了几百个操作体验非常糟糕。正确的做法是在批量操作前开启一个事务批量执行完一次性提交这样用户在EPLAN里只需要撤销一次就能回到操作前状态。第三个坑是对象引用失效。你之前拿到的Device对象在图纸经过一次删除、复制或重新布局后可能变成悬空引用再调用它的属性会抛异常。所以每次操作前最好重新获取对象引用不要试图把对象缓存得太久。在批量处理大量对象时我一般先收集对象的唯一标识再按需获取而不是直接保存对象引用。4. 实战案例图纸比例变化后端子模型尺寸异常的处理这个案例是我自己接到的一个真实需求搜索热度也很高可见不少人遇到过EPLAN图纸比例改变后端子模型很小怎么办。4.1 问题场景与现象分析设计人员把某张图纸从1比1改成了1比2之后发现图纸上新放置的端子、继电器这些部件模型显示变得非常小甚至小到看不清引脚接口。有人第一反应是去部件库改尺寸结果项目里其他地方用到同一个部件也跟着变大了越改越乱。这个问题的本质是图纸比例变化后已放置对象的显示尺寸没有跟着正确换算。你需要判断到底是“图纸上这个实例该显示的尺寸”出了问题还是“部件库里的原始定义”出了问题。大部分情况下都是前者。理解了这一点就不会去改部件库了。4.2 根因在“实例缩放”而不是“部件库定义”EPLAN在放置部件时图形符号和部件模型本身带有原始尺寸这个尺寸在插入时结合图纸比例进行了换算显示。正常情况你看到的端子大小是合理的但当图纸比例在后期被修改已放置对象的坐标参考没有跟着更新或者你新插入的部件沿用了旧的比例信息就会出现尺寸不匹配。从API层面看图框上的部件模型有一个缩放相关的属性。不同版本里这个属性的名称和位置可能略有不同但思路是一致的调整部件尺寸的方法是去修改每个放置实例的缩放属性而不是去改部件库的全局定义。这个思路很重要先想清楚是“局部实例”问题还是“全局定义”问题再决定改哪里。4.3 按比例自动修复模型尺寸的实现我的做法是写一个脚本遍历当前页面上所有Device类型的放置实例读取它们的缩放属性如果发现缩放值相对于当前页面比例异常就统一修正为指定值。下面是关键代码private void FixDeviceScale(Project prj) { foreach (Page page in prj.Pages) { foreach (Placement placement in page.Placements) { if (placement is Device dev) { double currentScale dev.Scale; double expectedScale CalculateExpectedScale(page); if (Math.Abs(currentScale - expectedScale) 0.01) { dev.Scale expectedScale; } } } } } private double CalculateExpectedScale(Page page) { // 根据页面比例和部件基准尺寸计算合适的缩放值 // 这里需要结合具体业务规则比如基准比例是1比1时scale为1.0 double pageScale page.PageProperty.ToString() switch { 1:1 1.0, 1:2 0.5, 2:1 2.0, _ 1.0 }; return pageScale; }注意这段代码里的CalculateExpectedScale需要根据你实际的项目规则去设计。有的项目希望端子始终显示成固定毫米尺寸有的项目希望跟随页面比例。最核心的经验是先明确业务上“正确”的尺寸是什么再去写公式。另外改完之后务必刷新页面必要时调用项目保存否则视觉上还是旧状态。我遇到过一种特殊情况同一个页面里既有1比1放的部件也有1比2放的部件还有从其他图纸复制过来的部件。处理的时候就不能简单一刀切得先识别哪些是需要修正的异常实例。这时候我会把页面上所有相同类型部件的缩放值统计出来把少数偏离大多数值的当异常处理。这种思路比写死规则更鲁棒。4.4 顺手解决中断点批量排序这个实战项目里还顺带处理了一个高频需求EPLAN中断点批量排序。断点编号乱是很多图纸的通病尤其是多页图纸经过多次修改之后断点号不连续、跳号、重复的情况非常普遍。用API处理断点的基本思路是遍历所有页面的中断点对象然后按照页面顺序和坐标位置重新赋予编号。这里有个关键点断点排序必须成对处理因为一个中断点对应一个目标中断点你不能光改起点不改成对的那个否则跨页跳转关系就乱了。我实际开发的逻辑是先把所有断点按“页面加Y坐标加X坐标”排序然后成对分配新编号最后统一写回。核心代码如下var locations new ListInterruptionPoint(); foreach (Page page in prj.Pages) { foreach (Placement placement in page.Placements) { if (placement is InterruptionPoint ip) { locations.Add(ip); } } } // 按页面和坐标排序 var sorted locations .OrderBy(ip ip.Page.PageName) .ThenBy(ip ip.Y) .ThenBy(ip ip.X) .ToList(); // 成对分配新编号这里需要根据实际业务规则决定编号方式 for (int i 0; i sorted.Count; i 2) { sorted[i].Properties[PROPERTIES.INTERRUPTIONPOINT_NUMBER] (i / 2 1).ToString(); if (i 1 sorted.Count) { sorted[i 1].Properties[PROPERTIES.INTERRUPTIONPOINT_NUMBER] (i / 2 1).ToString(); } }这里有个容易忽略的点EPLAN的中断点有“源点”和“目标点”的概念。有些项目里一个中断点号和另一个中断点号是一一对应的成对排序是基础需求。但有的项目里一个源点可能对应多个目标点这种情况下就不能简单按两两处理了需要先分析数据关系再写逻辑。5. 常见问题与排查技巧实录最后这部分是我自己在多个项目里汇总出来的经验特意整理成速查表方便你开发时对照参考。EPLAN二次开发的问题归纳起来主要是环境类、API调用类、数据同步类、性能类这几类。5.1 环境与版本类问题速查症状可能原因解决建议AddIn加载后EPLAN启动报错DLL引用了错误版本的EPLAN API核对程序集版本重新编译外部程序Attach失败EPLAN进程未启动或权限不足以管理员身份运行工具先打开EPLAN再启动工具报“组件仅用于交互式使用”在无界面环境调用API确保调用端在EPLAN主进程上下文中运行部件库EDZ导入后器件不可用EDZ文件损坏或版本不兼容用EPLAN部件管理重新导入并验证不要直接改文件网上很多人搜“EPLAN许可文件与当前版本不符怎么解决”这多半是许可证问题。这种情况应该先检查EPLAN License Manager中的许可证分配确认当前使用的模块和版本是否已经被授权而不是盲目重装。二次开发本身如果不涉及额外模块一般不需要额外许可证但如果你用到了某些特殊接口可能需要对应的授权模块。5.2 API调用与数据同步类问题开发时最容易踩的API坑是拿错对象。EPLAN API里同名或者相近的类非常多比如Device和Function、Placement和Symbol搞混之后数据读不出来而且不报错非常消耗排查时间。我的建议是写一个工具函数把某个对象的所有属性都打印出来拿到现场数据后再对照文档分析比对着API盲猜效率高得多。另一个常见问题是事务处理。没有正确使用事务或操作栈机制时批量操作一旦中间出错EPLAN可能处于半更新状态界面上能看到部分改了部分没改。解决方案是使用EPLAN提供的操作栈机制开头Begin结束Commit异常时Rollback。不要嫌麻烦这是保证数据一致性的底线。关于部件库很多人会问EDZ文件怎么导入。EDZ是EPLAN的部件库打包格式导入操作本身不难在部件管理界面里选择导入即可。但要注意EDZ导入后器件在图纸上不可用往往是因为缺了符号库或者设备连接点定义不完整这时候要先检查EDZ包本身是否完整而不是反复导入。5.3 我的排查流程和避坑心得我遇到过的最诡异的问题之一是同样的代码在测试机上完全正常部署到客户机器上就偶尔崩溃。后来发现是客户机器上的EPLAN版本有小版本差异导致API行为不一致。从那以后我养成了一个习惯在程序启动时读取EPLAN版本号并和代码里兼容的版本列表做比对不匹配就弹窗提示。这个习惯帮我少背了很多锅。另外凡是涉及批量修改部件库或者大量设备属性之前我都会强制要求先备份项目文件。EPLAN项目虽然自带恢复机制但二次开发工具一旦逻辑有bug批量改几千个对象再想恢复回来靠手动撤销根本不现实。备份这一步不能省这也是对用户负责。最后分享一个项目交付时的心得不要只交付exe和源码一定要把开发环境说明、版本兼容矩阵、已知问题列表一起写清楚。EPLAN版本一升级你的工具很可能就要跟着改这时候有一份完整的交接文档能省掉大量的沟通成本。我自己就吃过亏一个工具交付半年后EPLAN升级对方拿着旧exe来找我我花了半天才想起来这个工具依赖哪个版本接口写的。关于参数调试如果自己手头没有EPLAN环境可以建立一个内部虚拟机环境专门用来测试不同版本兼容性。正式的工业软件二次开发一定要有专门的测试环境不能拿生产项目直接试。测试花的时间永远比救火花的时间少。