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() }