
近日,全网都在说Gemini3 Pro这个模型多么厉害,作为关注工控和AI的公众号,自然不会错过这波体验。

从AI排行耪来看,目前已经冲到榜首:

各项评分数据也不错:

我们这里更加关心的是:Gemini 3 Pro的PLC编程能力如何?有没有比之前更优呢?
但是,在国内连Google都连不上,估计很多朋友都被这个难住了,当然我也不例外,大家如果有好的免费稳定资源欢迎留言区分享下。

加了翻墙的VPN,虽然可以登录,但是还是限制地区。

我翻了下面这个区域,看了是无法直接用了。

当然,看到提示也可以用Vertex AI来测试:

提示说不支持中文输入,好吧!我后面还是换成下面这种方式,即通过openrouter来测试下这款AI的编程能力:

要展示“专家级别”的 ST (Structured Text) 编程技能,关键不在于写出晦涩难懂的代码,而在于展示架构设计能力 (Architecture)、抽象思维 (Abstraction)、代码复用性 (Reusability) 以及对现代 IEC 61131-3 (OOP 扩展) 特性的熟练运用。
以下示例将展示如何在 CODESYS V3 或 Beckhoff TwinCAT 3 环境下,使用 面向对象编程 (OOP) 的方式构建一个通用的设备控制框架。
相比于传统的“面条式代码”,这段代码展示了接口(Interface)、继承(Inheritance)、属性(Property)和方法(Method)的使用。
构建一个通用的气缸/执行器控制系统。
专家视角:使用接口实现“多态”。主程序不需要知道这是什么气缸,只需要知道它能 MoveWork 和 MoveHome。
INTERFACE I_Actuator
(* 暴露给外部调用的公共方法和属性 *)
METHOD
MoveWork : BOOL
END_METHOD
METHOD
MoveHome : BOOL
END_METHOD
METHOD
ResetError : BOOL
END_METHOD
PROPERTY ErrorID : UDINT END_PROPERTY
PROPERTY IsBusy : BOOL END_PROPERTY
PROPERTY IsAtWork : BOOL END_PROPERTY
PROPERTY IsAtHome : BOOL END_PROPERTY专家视角:利用 ABSTRACT 和 EXTENDS。这是最核心的部分,处理了定时器、状态切换、报错逻辑。子类无需重复编写这些通用逻辑。
FUNCTION_BLOCK ABSTRACT FB_ActuatorBase IMPLEMENTS I_Actuator
VAR
(* 状态机枚举 *)
_eState : (IDLE, MOVING_WORK, MOVING_HOME, ERROR) := IDLE;
(* 内部变量,使用下划线前缀区分 *)
_fbTimeoutTimer : TON;
_tTimeoutLimit : TIME := T#5S;
_nErrorID : UDINT := 0;
END_VAR
(* === 接口属性实现 (只读) === *)
PROPERTY ErrorID : UDINT
GET
ErrorID := _nErrorID;
END_GET
END_PROPERTY
PROPERTY IsBusy : BOOL
GET
IsBusy := (_eState = MOVING_WORK) OR (_eState = MOVING_HOME);
END_GET
END_PROPERTY
(* === 抽象方法 (强制子类必须实现具体的硬件读写) === *)
(* 子类用来设置物理输出 *)
METHOD PROTECTED ABSTRACT SetOutputs
VAR_INPUT
bWorkCmd : BOOL;
bHomeCmd : BOOL;
END_VAR
END_METHOD
(* 子类用来读取物理反馈 *)
METHOD PROTECTED ABSTRACT ReadInputs
END_METHOD
(* === 核心逻辑周期调用方法 === *)
METHOD PUBLIC Execute
(* 读取子类映射的传感器状态 *)
ReadInputs();
(* 状态机逻辑 *)
CASE _eState OF
IDLE:
_fbTimeoutTimer(IN := FALSE);
MOVING_WORK:
SetOutputs(TRUE, FALSE);
_fbTimeoutTimer(IN := TRUE, PT := _tTimeoutLimit);
(* 使用 THIS^ 访问通过接口/虚方法获取的状态 *)
IF THIS^.IsAtWork THEN
_eState := IDLE;
ELSIF _fbTimeoutTimer.Q THEN
_nErrorID := 101;
(* 例如: 动作超时 *)
_eState := ERROR;
END_IF
MOVING_HOME:
SetOutputs(FALSE, TRUE);
_fbTimeoutTimer(IN := TRUE, PT := _tTimeoutLimit);
IF THIS^.IsAtHome THEN
_eState := IDLE;
ELSIF _fbTimeoutTimer.Q THEN
_nErrorID := 102;
_eState := ERROR;
END_IF
ERROR:
SetOutputs(FALSE, FALSE); (* 安全保护 *)
END_CASE
END_METHOD
(* === 接口方法实现 === *)
METHOD MoveWork : BOOL
IF _eState <> ERROR THEN
_eState := MOVING_WORK;
MoveWork := TRUE;
END_IF
END_METHOD
METHOD MoveHome : BOOL
IF _eState <> ERROR THEN
_eState := MOVING_HOME;
MoveHome := TRUE;
END_IF
END_METHOD
METHOD ResetError : BOOL
_nErrorID := 0;
_eState := IDLE;
ResetError := TRUE;
END_METHOD专家视角:具体的业务逻辑非常干净。开发者只需关注 IO 映射,无需关心超时计时器怎么写,因为基类已经处理了。
FUNCTION_BLOCK FB_PneumaticCylinder EXTENDS FB_ActuatorBase
VAR_INPUT
bSensorWork : BOOL; (* 物理输入 *)
bSensorHome : BOOL;
END_VAR
VAR_OUTPUT
bValveWork : BOOL; (* 物理输出 *)
bValveHome : BOOL;
END_VAR
(* === 实现基类的抽象属性 === *)
PROPERTY IsAtWork : BOOL
GET
IsAtWork := bSensorWork;
END_GET
END_PROPERTY
PROPERTY IsAtHome : BOOL
GET
IsAtHome := bSensorHome;
END_GET
END_PROPERTY
(* === 实现基类的抽象方法 === *)
METHOD PROTECTED SetOutputs
VAR_INPUT
bWorkCmd : BOOL;
bHomeCmd : BOOL;
END_VAR
(* 具体的硬件逻辑,例如这里是双电控 *)
bValveWork := bWorkCmd;
bValveHome := bHomeCmd;
END_METHOD
METHOD PROTECTED ReadInputs
(* 如果需要复杂的滤波或防抖,在这里写 *)
;
END_METHOD专家视角:主程序极度简洁。使用数组或接口列表可以轻松批量管理 100 个气缸。
PROGRAM MAIN
VAR
(* 实例化具体的气缸 *)
fbCylinderA : FB_PneumaticCylinder;
(* 定义测试变量 *)
bUserBtnExtend : BOOL;
bUserBtnRetract : BOOL;
bUserBtnReset : BOOL;
(* 接口指针,用于演示多态性 *)
iMyActuator : I_Actuator;
END_VAR
(* 1. 硬件 IO 映射 (模拟) *)
fbCylinderA.bSensorWork:= HwInputs.I0_0;
fbCylinderA.bSensorHome:= HwInputs.I0_1;
(* 2. 逻辑控制 - 通过接口操作,实现解耦 *)
iMyActuator := fbCylinderA; (* 将具体实例赋值给接口 *)
(* 此时,我们只通过 iMyActuator 操作,不关心具体是双电控还是单电控 *)
IF bUserBtnExtend THEN
iMyActuator.MoveWork();
END_IF
IF bUserBtnRetract THEN
iMyActuator.MoveHome();
END_IF
IF bUserBtnReset THEN
iMyActuator.ResetError();
END_IF
(* 3. 周期性执行核心逻辑 *)
(* 注意:必须调用实例的 Execute 方法来驱动内部状态机 *)
fbCylinderA.Execute();
(* 4. 输出映射 *)
HwOutputs.Q0_0:= fbCylinderA.bValveWork;
HwOutputs.Q0_1:= fbCylinderA.bValveHome;I_Actuator 接口控制设备。如果未来你需要把气缸换成伺服电机推杆,只需新建一个 FB_ServoPusher 实现该接口,主程序的控制逻辑(按钮、状态流转)一行代码都不用改。_eState, _fbTimeoutTimer 等变量被隐藏在基类内部,外部无法随意修改,保证了系统的健壮性。只能通过 MoveWork() 等方法与其交互。FB_ActuatorBase 一个地方,全厂所有气缸的逻辑都会自动更新。IF..THEN..ELSE 嵌套,状态机清晰,逻辑分层明确(硬件层 -> 驱动层 -> 业务层)。好了,至于代码到底如何,交给各位PLC的同仁们去评判吧。有任何想法欢迎留言讨论哦!