105 lines
2.9 KiB
Go
105 lines
2.9 KiB
Go
package plc
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"log/slog"
|
||
|
|
"sync"
|
||
|
|
|
||
|
|
goplc "bjhardman.cn/bjhardman/goplc"
|
||
|
|
"bjhardman.cn/bjhardman/goplc/s7"
|
||
|
|
)
|
||
|
|
|
||
|
|
// Manager PLC 连接管理器
|
||
|
|
//
|
||
|
|
// 业务场景:管理与西门子 S7 PLC 的连接生命周期,包括连接、断开、重连,
|
||
|
|
// 供上层下发工序号等指令使用。
|
||
|
|
type Manager struct {
|
||
|
|
client goplc.Client // PLC 客户端实例(真实 S7 客户端或 Mock 客户端)
|
||
|
|
}
|
||
|
|
|
||
|
|
var (
|
||
|
|
globalPLCManager *Manager
|
||
|
|
globalPLCManagerOnce sync.Once
|
||
|
|
)
|
||
|
|
|
||
|
|
// InitManager 初始化全局 PLC 管理器单例(生产环境调用,仅首次有效)
|
||
|
|
func InitManager(m *Manager) *Manager {
|
||
|
|
globalPLCManagerOnce.Do(func() {
|
||
|
|
globalPLCManager = m
|
||
|
|
})
|
||
|
|
return globalPLCManager
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetManager 获取全局 PLC 管理器单例(未初始化返回 nil)
|
||
|
|
func GetManager() *Manager {
|
||
|
|
return globalPLCManager
|
||
|
|
}
|
||
|
|
|
||
|
|
// Build 创建 PLC 管理器(真实 PLC 模式)
|
||
|
|
//
|
||
|
|
// 业务场景:系统启动时根据配置创建 PLC 管理器,建立与真实西门子 S7 PLC 的连接。
|
||
|
|
//
|
||
|
|
// 参数:
|
||
|
|
// - conf: PLC 配置(主机地址、连接类型等)
|
||
|
|
//
|
||
|
|
// 返回:PLC 管理器实例
|
||
|
|
func Build(conf PlcConf) *Manager {
|
||
|
|
m := &Manager{}
|
||
|
|
client := s7.NewClient(conf.Host,
|
||
|
|
s7.WithMaxRetries(-1),
|
||
|
|
s7.WithConnectionType(s7.ConnectionType(conf.ConnectType)),
|
||
|
|
s7.WithOnConnected(func() {
|
||
|
|
slog.Info("PLC connected", "host", conf.Host)
|
||
|
|
}),
|
||
|
|
s7.WithOnDisconnected(func(err error) {
|
||
|
|
if err != nil {
|
||
|
|
slog.Error("PLC disconnected with error", "host", conf.Host, "error", err)
|
||
|
|
} else {
|
||
|
|
slog.Warn("PLC disconnected", "host", conf.Host)
|
||
|
|
}
|
||
|
|
}),
|
||
|
|
s7.WithOnReconnecting(func(attempt int) {
|
||
|
|
slog.Info("PLC reconnecting", "host", conf.Host, "attempt", attempt)
|
||
|
|
}),
|
||
|
|
)
|
||
|
|
m.client = client
|
||
|
|
|
||
|
|
// 异步连接:在独立 goroutine 中执行连接操作,避免阻塞主启动流程
|
||
|
|
go func() {
|
||
|
|
defer func() {
|
||
|
|
if r := recover(); r != nil {
|
||
|
|
slog.Error("PLC: 异步连接panic恢复", "host", conf.Host, "panic", r)
|
||
|
|
}
|
||
|
|
}()
|
||
|
|
if err := client.Connect(); err != nil {
|
||
|
|
slog.Error("PLC: 异步连接失败", "host", conf.Host, "error", err)
|
||
|
|
}
|
||
|
|
}()
|
||
|
|
|
||
|
|
return m
|
||
|
|
}
|
||
|
|
|
||
|
|
// NewManager 创建 PLC 管理器(使用预构建的客户端)
|
||
|
|
//
|
||
|
|
// 业务场景:测试或 Mock 模式下使用,传入自定义的 PLC 客户端实现。
|
||
|
|
//
|
||
|
|
// 参数:
|
||
|
|
// - client: 预构建的 PLC 客户端实例(可以是 Mock 客户端)
|
||
|
|
//
|
||
|
|
// 返回:PLC 管理器实例
|
||
|
|
func NewManager(client goplc.Client) *Manager {
|
||
|
|
return &Manager{client: client}
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetPlc 获取 PLC 客户端实例
|
||
|
|
//
|
||
|
|
// 业务场景:SignalRouter 等组件通过此方法获取 PLC 客户端,执行读写操作。
|
||
|
|
func (m *Manager) GetPlc() (rc goplc.Client) {
|
||
|
|
return m.client
|
||
|
|
}
|
||
|
|
|
||
|
|
// IsConnected 检查 PLC 连接状态
|
||
|
|
//
|
||
|
|
// 业务场景:SignalRouter 在轮询前检查连接状态,避免无效读取。
|
||
|
|
func (m *Manager) IsConnected() bool {
|
||
|
|
return m.client != nil && m.client.IsConnected()
|
||
|
|
}
|