///| IEC 61131-3 梯形图 (Ladder Diagram, LD) 数据结构与高级编译器
///|
/// 触点类型
pub(all) enum Contact {
NormallyOpen(String) // 常开触点 --| |-- (为 TRUE 时导通)
NormallyClosed(String) // 常闭触点 --|/|-- (为 FALSE 时导通)
DirectNO(PlcAddress) // 直接物理地址常开触点
DirectNC(PlcAddress) // 直接物理地址常闭触点
RisingEdgeContact(String) // 上升沿触点 --|P|--
FallingEdgeContact(String) // 下降沿触点 --|N|--
CompareContact(String, BinaryOp, Int) // 比较触点 (如 Counter.CV >= 10)
} derive(Debug, Eq)
///|
/// 输出线圈类型
pub(all) enum Coil {
Normal(String) // 普通输出线圈 --( )--
Set(String) // 置位线圈 --(S)-- (导通时置 TRUE,保持)
Reset(String) // 复位线圈 --(R)-- (导通时置 FALSE,保持)
DirectCoil(PlcAddress) // 直接物理地址线圈
} derive(Debug, Eq)
///|
/// 梯形图逻辑节点 (支持任意深度的串并联布尔网络)
pub(all) enum LadderNode {
Leaf(Contact)
Series(Array[LadderNode]) // 串联逻辑 (AND)
Parallel(Array[LadderNode]) // 并联逻辑 (OR)
} derive(Debug, Eq)
///|
/// 梯级 (Rung):从左母线流经触点逻辑网络,驱动末端线圈
pub(all) struct Rung {
id : Int
comment : String
condition : LadderNode
coils : Array[Coil]
} derive(Debug)
///|
/// 创建梯级
pub fn Rung::new(
id : Int,
comment : String,
condition : LadderNode,
coils : Array[Coil],
) -> Rung {
{ id, comment, condition, coils, }
}
///|
/// 完整梯形图程序
pub(all) struct LadderDiagram {
name : String
rungs : Array[Rung]
} derive(Debug)
///|
/// 创建梯形图
pub fn LadderDiagram::new(name : String) -> LadderDiagram {
{ name, rungs: [], }
}
///|
/// 添加梯级
pub fn LadderDiagram::add_rung(self : LadderDiagram, rung : Rung) -> Unit {
self.rungs.push(rung)
}
// ------------------- 核心编译引擎:LD -> AST / ST -------------------
///|
/// 将 LadderNode 编译为布尔表达式 (Expr)
pub fn ladder_node_to_expr(node : LadderNode) -> Expr {
match node {
LadderNode::Leaf(contact) =>
match contact {
Contact::NormallyOpen(v) => Expr::Variable(v)
Contact::NormallyClosed(v) =>
Expr::Unary(UnaryOp::Not, Expr::Variable(v))
Contact::DirectNO(addr) => Expr::DirectAddress(addr)
Contact::DirectNC(addr) =>
Expr::Unary(UnaryOp::Not, Expr::DirectAddress(addr))
Contact::RisingEdgeContact(v) => Expr::FieldAccess(v, "Q")
Contact::FallingEdgeContact(v) => Expr::FieldAccess(v, "Q")
Contact::CompareContact(v, op, target) =>
Expr::Binary(
Expr::Variable(v),
op,
Expr::Literal(PlcValue::Int(target)),
)
}
LadderNode::Series(nodes) => {
if nodes.length() == 0 {
return Expr::Literal(PlcValue::Bool(true))
}
let mut expr = ladder_node_to_expr(nodes[0])
let mut i = 1
while i < nodes.length() {
let next_expr = ladder_node_to_expr(nodes[i])
expr = Expr::Binary(expr, BinaryOp::And, next_expr)
i = i + 1
}
expr
}
LadderNode::Parallel(nodes) => {
if nodes.length() == 0 {
return Expr::Literal(PlcValue::Bool(false))
}
let mut expr = ladder_node_to_expr(nodes[0])
let mut i = 1
while i < nodes.length() {
let next_expr = ladder_node_to_expr(nodes[i])
expr = Expr::Binary(expr, BinaryOp::Or, next_expr)
i = i + 1
}
expr
}
}
}
///|
/// 将单个梯级编译为对应的 ST 语句数组 (Stmt)
pub fn rung_to_stmts(rung : Rung) -> Array[Stmt] {
let stmts = []
let cond_expr = ladder_node_to_expr(rung.condition)
for coil in rung.coils {
match coil {
Coil::Normal(var_name) => stmts.push(Stmt::Assign(var_name, cond_expr))
Coil::DirectCoil(addr) => stmts.push(Stmt::AssignAddress(addr, cond_expr))
Coil::Set(var_name) => {
let then_part = [
Stmt::Assign(var_name, Expr::Literal(PlcValue::Bool(true))),
]
stmts.push(Stmt::If(cond_expr, then_part, [], []))
}
Coil::Reset(var_name) => {
let then_part = [
Stmt::Assign(var_name, Expr::Literal(PlcValue::Bool(false))),
]
stmts.push(Stmt::If(cond_expr, then_part, [], []))
}
}
}
stmts
}
///|
/// 将整套梯形图编译为 ST 抽象语法树 (Program)
pub fn LadderDiagram::to_program(self : LadderDiagram) -> Program {
let stmts = []
for rung in self.rungs {
let r_stmts = rung_to_stmts(rung)
for s in r_stmts {
stmts.push(s)
}
}
{ name: self.name, variables: [], statements: stmts, }
}