///| PLC 运行时执行引擎与扫描周期调度器 (Scan Cycle Scheduler)
///|
/// 运行时执行环境
pub(all) struct PlcRuntime {
mem : PlcMemory
fb_instances : Map[String, FbInstance]
mut program : Program?
// 扫描周期状态统计
mut cycle_count : Int64
mut current_time_ms : Int64
mut scan_time_us : Int64
mut watchdog_ms : Int64
mut is_running : Bool
} derive(Debug)
///|
/// 创建 PLC 运行时
pub fn PlcRuntime::new() -> PlcRuntime {
{
mem: PlcMemory::new(),
fb_instances: {},
program: None,
cycle_count: 0L,
current_time_ms: 0L,
scan_time_us: 0L,
watchdog_ms: 500L, // 默认 500ms 看门狗超时上限
is_running: true,
}
}
///|
/// 加载程序 (POU) 并初始化变量
pub fn PlcRuntime::load_program(self : PlcRuntime, prog : Program) -> Unit {
self.program = Some(prog)
// 初始化变量声明
for v in prog.variables {
match v.located_at {
Some(addr) => self.mem.bind_variable(v.name, addr)
None => ()
}
match v.init_val {
Some(val) => self.mem.set_var(v.name, val)
None =>
// 默认零值
if v.var_type == "BOOL" {
self.mem.set_var(v.name, PlcValue::Bool(false))
} else if v.var_type == "INT" {
self.mem.set_var(v.name, PlcValue::Int(0))
} else if v.var_type == "REAL" {
self.mem.set_var(v.name, PlcValue::Real(0.0))
} else if v.var_type == "TIME" {
self.mem.set_var(v.name, PlcValue::Time(0L))
} else if v.var_type == "TON" {
self.fb_instances[v.name] = FbInstance::Ton(TonTimer::new())
} else if v.var_type == "TOF" {
self.fb_instances[v.name] = FbInstance::Tof(TofTimer::new())
} else if v.var_type == "TP" {
self.fb_instances[v.name] = FbInstance::Tp(TpTimer::new())
} else if v.var_type == "TONR" {
self.fb_instances[v.name] = FbInstance::Tonr(TonrTimer::new())
} else if v.var_type == "CTU" {
self.fb_instances[v.name] = FbInstance::Ctu(CtuCounter::new())
} else if v.var_type == "CTUD" {
self.fb_instances[v.name] = FbInstance::Ctud(CtudCounter::new())
} else if v.var_type == "SR" {
self.fb_instances[v.name] = FbInstance::Sr(SrFlipFlop::new())
} else if v.var_type == "RS" {
self.fb_instances[v.name] = FbInstance::Rs(RsFlipFlop::new())
} else if v.var_type == "R_TRIG" {
self.fb_instances[v.name] = FbInstance::Rtrig(RTrig::new())
} else if v.var_type == "F_TRIG" {
self.fb_instances[v.name] = FbInstance::Ftrig(FTrig::new())
}
}
}
}
// ------------------- 表达式求值器 -------------------
///|
/// 计算表达式的值
pub fn PlcRuntime::eval_expr(self : PlcRuntime, expr : Expr) -> PlcValue {
match expr {
Expr::Literal(v) => v
Expr::Variable(name) => self.mem.get_var(name)
Expr::DirectAddress(addr) => self.mem.get_address_val(addr)
Expr::FieldAccess(inst_name, field) =>
match self.fb_instances.get(inst_name) {
Some(fb) => fb.get_field(field)
None => self.mem.get_var("\{inst_name}.\{field}")
}
Expr::Unary(op, sub_expr) => {
let v = self.eval_expr(sub_expr)
match op {
UnaryOp::Not => PlcValue::Bool(!v.is_true())
UnaryOp::Neg =>
match v {
PlcValue::Int(i) => PlcValue::Int(-i)
PlcValue::Real(r) => PlcValue::Real(-r)
_ => PlcValue::Null
}
}
}
Expr::Binary(left, op, right) => {
let lv = self.eval_expr(left)
if op == BinaryOp::And && !lv.is_true() {
return PlcValue::Bool(false)
}
if op == BinaryOp::Or && lv.is_true() {
return PlcValue::Bool(true)
}
let rv = self.eval_expr(right)
match op {
BinaryOp::Add => lv.add(rv)
BinaryOp::Sub => lv.sub(rv)
BinaryOp::Mul => lv.mul(rv)
BinaryOp::Div => lv.div(rv)
BinaryOp::Mod => lv.modulo(rv)
BinaryOp::Eq => PlcValue::Bool(lv.eq(rv))
BinaryOp::Neq => PlcValue::Bool(!lv.eq(rv))
BinaryOp::Lt => PlcValue::Bool(lv.lt(rv))
BinaryOp::Lte => PlcValue::Bool(lv.lte(rv))
BinaryOp::Gt => PlcValue::Bool(!lv.lte(rv))
BinaryOp::Gte => PlcValue::Bool(!lv.lt(rv))
BinaryOp::And => PlcValue::Bool(lv.is_true() && rv.is_true())
BinaryOp::Or => PlcValue::Bool(lv.is_true() || rv.is_true())
BinaryOp::Xor => PlcValue::Bool(lv.is_true() != rv.is_true())
}
}
}
}
// ------------------- 语句执行器 -------------------
///|
/// 执行单个语句
pub fn PlcRuntime::execute_statement(
self : PlcRuntime,
stmt : Stmt,
delta_ms : Int64,
) -> Unit {
match stmt {
Stmt::Assign(var_name, expr) => {
let val = self.eval_expr(expr)
self.mem.set_var(var_name, val)
}
Stmt::AssignAddress(addr, expr) => {
let val = self.eval_expr(expr)
self.mem.set_address_val(addr, val)
}
Stmt::AssignField(inst_name, field, expr) => {
let val = self.eval_expr(expr)
self.mem.set_var("\{inst_name}.\{field}", val)
}
Stmt::If(cond, then_stmts, elsifs, else_stmts) => {
if self.eval_expr(cond).is_true() {
self.execute_statement_list(then_stmts, delta_ms)
return
}
for pair in elsifs {
let (e_cond, e_stmts) = pair
if self.eval_expr(e_cond).is_true() {
self.execute_statement_list(e_stmts, delta_ms)
return
}
}
self.execute_statement_list(else_stmts, delta_ms)
}
Stmt::While(cond, body) => {
let mut loops = 0
while self.eval_expr(cond).is_true() && loops < 10000 {
self.execute_statement_list(body, delta_ms)
loops = loops + 1
}
}
Stmt::For(var_name, start_expr, end_expr, step_expr, body) => {
let start_v = self.eval_expr(start_expr).to_int()
let end_v = self.eval_expr(end_expr).to_int()
let step_v = match step_expr {
Some(s) => self.eval_expr(s).to_int()
None => 1
}
let mut cur = start_v
let mut loops = 0
while cur <= end_v && loops < 10000 {
self.mem.set_var(var_name, PlcValue::Int(cur))
self.execute_statement_list(body, delta_ms)
cur = cur + step_v
loops = loops + 1
}
}
Stmt::FbCall(inst_name, _, args) => {
if !self.fb_instances.contains(inst_name) {
self.fb_instances[inst_name] = FbInstance::Ton(TonTimer::new())
}
match self.fb_instances.get(inst_name) {
Some(fb) =>
match fb {
FbInstance::Ton(ton) => {
let mut in_val = false
let mut pt_val = 0L
for pair in args {
let (k, expr) = pair
if k == "IN" || k == "in" {
in_val = self.eval_expr(expr).is_true()
} else if k == "PT" || k == "pt" {
match self.eval_expr(expr) {
PlcValue::Time(t) => pt_val = t
PlcValue::Int(i) => pt_val = i.to_int64()
_ => ()
}
}
}
ton.tick(in_val, pt_val, delta_ms)
self.mem.set_var("\{inst_name}.Q", PlcValue::Bool(ton.q))
self.mem.set_var("\{inst_name}.ET", PlcValue::Time(ton.et))
}
FbInstance::Tof(tof) => {
let mut in_val = false
let mut pt_val = 0L
for pair in args {
let (k, expr) = pair
if k == "IN" || k == "in" {
in_val = self.eval_expr(expr).is_true()
} else if k == "PT" || k == "pt" {
match self.eval_expr(expr) {
PlcValue::Time(t) => pt_val = t
PlcValue::Int(i) => pt_val = i.to_int64()
_ => ()
}
}
}
tof.tick(in_val, pt_val, delta_ms)
self.mem.set_var("\{inst_name}.Q", PlcValue::Bool(tof.q))
self.mem.set_var("\{inst_name}.ET", PlcValue::Time(tof.et))
}
FbInstance::Tp(tp) => {
let mut in_val = false
let mut pt_val = 0L
for pair in args {
let (k, expr) = pair
if k == "IN" || k == "in" {
in_val = self.eval_expr(expr).is_true()
} else if k == "PT" || k == "pt" {
match self.eval_expr(expr) {
PlcValue::Time(t) => pt_val = t
PlcValue::Int(i) => pt_val = i.to_int64()
_ => ()
}
}
}
tp.tick(in_val, pt_val, delta_ms)
self.mem.set_var("\{inst_name}.Q", PlcValue::Bool(tp.q))
self.mem.set_var("\{inst_name}.ET", PlcValue::Time(tp.et))
}
FbInstance::Tonr(tonr) => {
let mut in_val = false
let mut reset_val = false
let mut pt_val = 0L
for pair in args {
let (k, expr) = pair
if k == "IN" || k == "in" {
in_val = self.eval_expr(expr).is_true()
} else if k == "RESET" || k == "reset" || k == "R" || k == "r" {
reset_val = self.eval_expr(expr).is_true()
} else if k == "PT" || k == "pt" {
match self.eval_expr(expr) {
PlcValue::Time(t) => pt_val = t
PlcValue::Int(i) => pt_val = i.to_int64()
_ => ()
}
}
}
tonr.tick(in_val, reset_val, pt_val, delta_ms)
self.mem.set_var("\{inst_name}.Q", PlcValue::Bool(tonr.q))
self.mem.set_var("\{inst_name}.ET", PlcValue::Time(tonr.et))
}
FbInstance::Ctu(ctu) => {
let mut cu_val = false
let mut r_val = false
let mut pv_val = 0
for pair in args {
let (k, expr) = pair
if k == "CU" || k == "cu" {
cu_val = self.eval_expr(expr).is_true()
} else if k == "R" || k == "r" {
r_val = self.eval_expr(expr).is_true()
} else if k == "PV" || k == "pv" {
pv_val = self.eval_expr(expr).to_int()
}
}
ctu.tick(cu_val, r_val, pv_val)
self.mem.set_var("\{inst_name}.Q", PlcValue::Bool(ctu.q))
self.mem.set_var("\{inst_name}.CV", PlcValue::Int(ctu.cv))
}
FbInstance::Ctud(ctud) => {
let mut cu_val = false
let mut cd_val = false
let mut r_val = false
let mut ld_val = false
let mut pv_val = 0
for pair in args {
let (k, expr) = pair
if k == "CU" || k == "cu" {
cu_val = self.eval_expr(expr).is_true()
} else if k == "CD" || k == "cd" {
cd_val = self.eval_expr(expr).is_true()
} else if k == "R" || k == "r" {
r_val = self.eval_expr(expr).is_true()
} else if k == "LD" || k == "ld" {
ld_val = self.eval_expr(expr).is_true()
} else if k == "PV" || k == "pv" {
pv_val = self.eval_expr(expr).to_int()
}
}
ctud.tick(cu_val, cd_val, r_val, ld_val, pv_val)
self.mem.set_var("\{inst_name}.QU", PlcValue::Bool(ctud.qu))
self.mem.set_var("\{inst_name}.QD", PlcValue::Bool(ctud.qd))
self.mem.set_var("\{inst_name}.CV", PlcValue::Int(ctud.cv))
}
FbInstance::Sr(sr) => {
let mut s1 = false
let mut r = false
for pair in args {
let (k, expr) = pair
if k == "S1" || k == "s1" || k == "S" || k == "s" {
s1 = self.eval_expr(expr).is_true()
} else if k == "R" || k == "r" {
r = self.eval_expr(expr).is_true()
}
}
let q = sr.tick(s1, r)
self.mem.set_var("\{inst_name}.Q1", PlcValue::Bool(q))
self.mem.set_var("\{inst_name}.Q", PlcValue::Bool(q))
}
FbInstance::Rs(rs) => {
let mut s = false
let mut r1 = false
for pair in args {
let (k, expr) = pair
if k == "S" || k == "s" {
s = self.eval_expr(expr).is_true()
} else if k == "R1" || k == "r1" || k == "R" || k == "r" {
r1 = self.eval_expr(expr).is_true()
}
}
let q = rs.tick(s, r1)
self.mem.set_var("\{inst_name}.Q1", PlcValue::Bool(q))
self.mem.set_var("\{inst_name}.Q", PlcValue::Bool(q))
}
FbInstance::Rtrig(rtrig) => {
let mut clk = false
for pair in args {
let (k, expr) = pair
if k == "CLK" || k == "clk" {
clk = self.eval_expr(expr).is_true()
}
}
let q = rtrig.tick(clk)
self.mem.set_var("\{inst_name}.Q", PlcValue::Bool(q))
}
FbInstance::Ftrig(ftrig) => {
let mut clk = false
for pair in args {
let (k, expr) = pair
if k == "CLK" || k == "clk" {
clk = self.eval_expr(expr).is_true()
}
}
let q = ftrig.tick(clk)
self.mem.set_var("\{inst_name}.Q", PlcValue::Bool(q))
}
}
None => ()
}
}
Stmt::Empty => ()
}
}
///|
/// 执行语句序列
pub fn PlcRuntime::execute_statement_list(
self : PlcRuntime,
stmts : Array[Stmt],
delta_ms : Int64,
) -> Unit {
for s in stmts {
self.execute_statement(s, delta_ms)
}
}
// ------------------- 扫描周期调度器 -------------------
///|
/// 执行一个完整的扫描周期 (Scan Cycle)
pub fn PlcRuntime::step_cycle(self : PlcRuntime, delta_ms : Int64) -> Unit {
if !self.is_running {
return
}
// 阶段 1: 输入采样已在周期前写入内存
// 阶段 2: 程序执行
match self.program {
Some(prog) => self.execute_statement_list(prog.statements, delta_ms)
None => ()
}
// 阶段 3: 输出刷新已同步至 memory.outputs
// 周期计时推进
self.cycle_count = self.cycle_count + 1L
self.current_time_ms = self.current_time_ms + delta_ms
}