如何在Golang中开发类似PLC功能块(如TON定时器)的模块?
在Go中实现PLC风格的功能块(以TON定时器为例)
可行性说明
完全可以在Go中实现PLC功能块的特性。PLC里的函数对应Go的普通函数(无内部状态,相同输入必返回相同输出);而功能块依赖内部状态,这正好可以用Go的结构体+指针方法来实现——结构体存储功能块的内部状态,指针方法在每次调用时更新状态并计算输出,完美匹配功能块"相同输入可能因状态不同返回不同输出"的核心特性。
TON定时器的核心逻辑
TON是通电延时定时器,逻辑规则如下:
- 当输入
IN为true时,定时器启动计时,已流逝时间ET随时间增长;当ET达到设定时长PT后,ET保持为PT,输出Q变为true。 - 当输入
IN变为false时,定时器立即复位:ET清零,Q变为false。 - 若
IN再次变为true,定时器重新开始计时。
Go实现代码
package plcblocks import "time" // TON 通电延时定时器功能块 type TON struct { pt int64 // 设定定时时长(单位:毫秒) startTime time.Time // 定时器启动时刻 isActive bool // 定时器是否处于激活状态 outputQ bool // 输出Q:是否达到定时时长 elapsedET int64 // 输出ET:已流逝时长(单位:毫秒) } // NewTON 创建TON定时器实例,传入设定时长PT(毫秒) func NewTON(pt int64) *TON { return &TON{ pt: pt, } } // Update 调用定时器,传入当前输入IN,返回输出Q和ET func (t *TON) Update(in bool) (q bool, et int64) { currentTime := time.Now() switch { case in && !t.isActive: // IN从false变为true,启动定时器 t.isActive = true t.startTime = currentTime t.outputQ = false t.elapsedET = 0 case in && t.isActive: // IN保持true,持续计时 t.elapsedET = int64(currentTime.Sub(t.startTime).Milliseconds()) if t.elapsedET >= t.pt { t.elapsedET = t.pt t.outputQ = true } case !in && t.isActive: // IN从true变为false,复位定时器 t.isActive = false t.outputQ = false t.elapsedET = 0 } return t.outputQ, t.elapsedET }
使用示例(模拟PLC循环扫描)
package main import ( "fmt" "time" "your-module-path/plcblocks" ) func main() { // 创建一个5秒(5000毫秒)的TON定时器 ton := plcblocks.NewTON(5000) in := false // 模拟PLC无限循环扫描,每隔100ms调用一次定时器 for { // 前3秒IN为false,之后变为true保持5秒,再变回false currentSec := int(time.Now().Unix() % 13) if currentSec >= 3 && currentSec < 8 { in = true } else { in = false } q, et := ton.Update(in) fmt.Printf("IN: %t | Q: %t | ET: %dms\n", in, q, et) time.Sleep(100 * time.Millisecond) } }
实现原理说明
- 结构体
TON存储了定时器的所有内部状态(设定时长、启动时刻、激活状态、输出值),相当于PLC功能块的内部内存。 Update方法使用指针接收者,确保每次调用都能修改同一个实例的状态,实现状态的持久化。- 每次调用
Update时,基于当前输入IN和内部状态计算新的输出Q和ET,完全符合TON定时器的逻辑。
内容的提问来源于stack exchange,提问作者Subhadeep Datta
相关产品推荐
相关产品推荐

