智能体驱动的 PLC 编程
在之前的文章中,我们描述了如何教LLM读写施耐德电气的Control Expert XEF文件——M340 PLC背后的XML格式。这种方法之所以有效,是因为XEF文件是文本。导出、编辑、导入。
但对于我们运行在施耐德M241(TM241CE40R)上的混凝土工厂项目,我们转向了EcoStruxure Machine Expert——一个完全不同的工具,基于CODESYS 3.5。而Machine Expert的项目文件是二进制的。默认情况下没有文本导出。没有XSD模式。没有明显的入口。
问题是:我们能否给LLM与Control Expert相同的Machine Expert访问级别?不只是读取代码,而是修改它、构建它,最终下载到实时PLC——全部从Linux终端,通过SSH,通过AI代理?
答案是肯定的,但通往那里的道路比预期的更有趣。
1、三种方法,并行测试
我们首先研究了所有可能的远程控制Machine Expert的方法。三个并行研究方向:
方法1:脚本化运行中的GUI。 Machine Expert有一个"Scripting Immediate"面板——一个IronPython 2.7 REPL,完全访问CODESYS脚本API。我们能从外部向它馈送命令吗?
我们发现Machine Expert(LogicBuilder.exe)不暴露COM对象、命名管道或命令行转发。--runscript标志启动一个新实例,而不是发送到正在运行的实例。在运行中的IDE中执行脚本的唯一方法是通过GUI的>>>提示。
使用Windows UI Automation(具有Interactive登录类型的计划任务 + AutomationElement来查找>>>输入字段),我们设法以编程方式粘贴和执行execfile()命令。脆弱,但它确实工作了。
方法2:无头运行。 Machine Expert附带LogicBuilderShell.exe——一个为CI/CD设计的无头变体。它接受--noUI --runscript,应该非常适合自动化。
它拒绝启动。虚拟机没有激活施耐德电气许可证(以演示模式运行),而演示模式对话框需要GUI会话。Shell变体崩溃并显示:
System.InvalidOperationException: Showing a modal dialog box or form when the
application is not running in UserInteractive mode is not a valid operation.
没有付费许可证就是死胡同。
方法3:在Machine Expert内部运行HTTP服务器。 如果我们在IDE的IronPython环境中启动一个Web服务器呢?脚本引擎可以访问Python 2.7的BaseHTTPServer,而CODESYS明确支持通过system.execute_on_primary_thread()使用后台线程。
这是获胜者。
2、架构
解决方案是一个在Machine Expert的IronPython引擎中运行的单个Python 2.7脚本。当通过Scripting Immediate面板执行时,它在后台线程上启动一个HTTP服务器:
[Linux / Claude Code] --HTTP--> [Windows VM: Machine Expert + Bridge on port 8090]
|
v (IronPython scripting API)
[CODESYS engine]
|
v (Modbus TCP via port proxy)
[PLC TM241CE40R]
桥接器暴露一个REST API:
GET /api/status - 项目路径、脏标志、应用程序名称
GET /api/tree - 列出所有对象(POU、GVL、类型)
GET /api/st/read?name=X - 读取任何POU的声明 + 实现
POST /api/st/write - 修改任何POU的ST代码
POST /api/build - 编译应用程序
POST /api/xml/import - 导入PLCopen XML
POST /api/xml/export - 将对象导出为PLCopen XML
POST /api/project/save - 保存项目
POST /api/shutdown - 停止桥接器
每个触及项目的操作都通过system.execute_on_primary_thread()编组到主线程,而HTTP服务器在守护线程上运行。这是CODESYS明确支持的,但在其文档中标记为"仅限专家"。
3、编码陷阱
第一个版本立即对简单操作有效——/api/ping、/api/status、/api/build都返回了干净的JSON。但当我们试图从POU读取ST代码时,响应为空。
Machine Expert控制台中的错误说明了问题:
UnicodeDecodeError: 'unknown' codec can't decode byte 0xB3 at position 8673
CODESYS返回的.NET System.String对象包含来自Windows代码页的字符(在这种情况下,来自代码注释中kg/m³单位的³)。Python 2.7的json模块试图将它们解码为UTF-8并失败了。
修复是一个递归清理器,强制每个字符串通过unicode()并使用xmlcharrefreplace回退:
def _sanitize(obj):
if isinstance(obj, dict):
return {_sanitize(k): _sanitize(v) for k, v in obj.items()}
if isinstance(obj, (list, tuple)):
return [_sanitize(item) for item in obj]
if isinstance(obj, bool) or isinstance(obj, (int, float)) or obj is None:
return obj
try:
s = unicode(obj)
return s.encode("ascii", "xmlcharrefreplace").decode("ascii")
except:
return u"<encoding error>"
此后,每个POU——包括那些带有罗马尼亚语变音符号和工程单位符号的——都干净地序列化为JSON。
4、我们能用它做什么
随着桥接器的运行,Linux上的AI代理对Machine Expert项目拥有完整的编程访问权限。以下是典型工作流的样子:
读取所有PLC代码:
$ curl -s http://<vm-ip>:8090/api/tree?recursive=true | python3 -m json.tool
{
"objects": [
{"name": "Continuous_Dosing_Control", "has_declaration": true, "has_implementation": true},
{"name": "FB_BeltScale", "has_declaration": true, "has_implementation": true},
{"name": "GVL_Dosing", "has_declaration": true},
...
],
"count": 55
}
$ curl -s "http://<vm-ip>:8090/api/st/read?name=Continuous_Dosing_Control"
{
"name": "Continuous_Dosing_Control",
"declaration": "PROGRAM Continuous_Dosing_Control\nVAR\n max_component: REAL; ...",
"implementation": "(* Recipe calculations *)\nrecipe_density := aggregate1_target + ..."
}
修改代码并构建:
$ curl -X POST -d '{"name":"MAIN","implementation":"PRG_ModbusComm();\nPRG_Control();"}' \
http://<vm-ip>:8090/api/st/write
{"name": "MAIN", "updated": ["implementation"]}
$ curl -X POST http://<vm-ip>:8090/api/build
{"result": "build_complete", "message_count": 0, "messages": []}
导出PLCopen XML用于版本控制:
$ curl -X POST -d '{"name":"Application","path":"C:\\export.xml","recursive":true}' \
http://<vm-ip>:8090/api/xml/export
{"result": "exported", "path": "C:\\export.xml"}
5、实际使用:分析和修改配料算法
桥接器立即证明了它的价值。我们需要理解和修改混凝土工厂的连续配料算法——一个PLC程序,它根据配方目标计算皮带速度,带有VFD校正因子和自动数量跟踪。
使用桥接器,AI代理:
读取所有12个POU和6个GVL来自实时项目,理解从配方输入到速度计算再到VFD输出的完整数据流。
识别了一个设计问题:速度计算没有考虑不同的皮带容量。一条在100%时输送72,000 kg/h的皮带和一根在100%时输送19,000 kg/h的水泥螺旋,如果给定相同的百分比设定点,会产生错误的比例。
提出并验证了修复:在计算速度比率之前,将配方目标按校准值归一化。七行结构化文本,独立于工厂操作员准备的电子表格计算进行了验证。
在操作员在Machine Expert中应用更改后读回代码,在下载到PLC之前确认修改是正确的。
所有这些都是通过SSH进行的,AI代理在通过WireGuard VPN连接到工厂网络的Linux笔记本电脑上运行。PLC通过SCADA计算机上的端口代理访问,Machine Expert在KVM虚拟机中运行。
6、引导:唯一的手动步骤
桥接器需要一个手动操作:打开Machine Expert的Scripting Immediate面板并输入:
execfile(r"C:\Users\Adi\me_bridge_server.py")
之后,一切都是远程的。只要Machine Expert打开,桥接器就会保持。如果Machine Expert重新启动,需要重新输入命令——或通过UI Automation使用计划任务自动化。
我们研究了通过Windows UI Automation自动化此步骤(通过其AutomationId查找>>>输入字段,设置焦点,通过剪贴板粘贴,发送Enter)。它有效,但很脆弱且需要交互式桌面会话。目前,手动引导是可以接受的——这是每个Machine Expert会话的一次性操作。
7、CODESYS脚本API
桥接器之所以有效,是因为CODESYS附带了全面的IronPython脚本API。注入脚本命名空间的关键对象:
projects— 打开、保存、关闭项目。projects.primary返回活动项目。system— 消息存储、线程、UI控制。system.execute_on_primary_thread()是线程安全操作的关键方法。online— PLC连接、登录、下载、启动/停止。online.create_online_application()提供对实时变量读写的访问。
API通过安装目录中的.pyi存根文件记录:
LogicBuilder/ScriptLib/Stubs/scriptengine/
├── ScriptProject.pyi # 项目操作、XML导入/导出
├── ScriptApplication.pyi # 构建、重建、清理、引导应用程序
├── ScriptOnline.pyi # PLC连接、下载、变量访问
├── ScriptTextualObject.pyi # 读取/写入ST代码
├── ScriptTreeObject.pyi # 导航项目树
└── ScriptSystem.pyi # 线程、消息、UI
这些存根是Machine Expert等同于Control Expert的XSD模式——使编程访问成为可能的未记录关键。
8、下一步
桥接器开启了我们仍在探索的可能性:
- 自动化回归测试:修改POU、构建、检查错误、恢复——全部在脚本中。
- 实时变量监控:
ScriptOnlineApplication.read_value()方法可以实时读取PLC变量,实现AI辅助调试。 - 持续部署:修改代码、构建、下载到PLC、验证——工业自动化的CI/CD管道。
- 多项目管理:相同的桥接器模式应该适用于任何基于CODESYS 3.5的IDE(ABB Automation Builder、WAGO e!COCKPIT等)——未经测试,但脚本API是共享的。
根本见解与我们Control Expert工作的见解相同:工业自动化工具比其供应商宣传的具有更多的编程访问权限。你只需要查看安装目录。
原文链接: Building an HTTP Bridge Inside Machine Expert for LLM-Driven PLC Programming
汇智网翻译整理,转载请标明出处