package cnc import ( "context" "fmt" "log/slog" "net" "strconv" "strings" "time" "bjhardman.cn/bjhardman/gofocas" ) // Manager FANUC CNC 通讯管理器(FOCAS 协议)。 // 设备地址从 equipment.ipAddress 读取,格式 "IP:端口"(如 192.168.x.x:8193), // 端口不写死,由数据库配置;若省略端口,默认使用 8193。 type Manager struct { addrs map[int]string // machineID → "IP:端口" timeout time.Duration } // NewManager 用 equipment 地址映射创建 Manager。 // addrMap: machineID → "IP:端口"。 func NewManager(addrMap map[int]string) *Manager { return &Manager{addrs: addrMap, timeout: 3 * time.Second} } // MustNewManager 同 NewManager,为兼容旧调用保留。 func MustNewManager(addrMap map[int]string) *Manager { return NewManager(addrMap) } // WriteMacro 向指定设备的 FANUC 写入宏变量(如 #666/#888)。 // 设备地址未配置(ipAddress 为空)时跳过写入并记录警告,不阻塞流程; // 地址已配置但连接/写入失败时返回错误(防止清洗机用错程序)。 func (m *Manager) WriteMacro(ctx context.Context, machineID, macroNo int, value float64) error { addr := m.addrs[machineID] if addr == "" { slog.Warn("cnc: FANUC address not configured, skip macro write", "machineId", machineID, "macroNo", macroNo, "value", value) return nil } // 兼容仅配置 IP 未配置端口的场景,FANUC FOCAS 默认端口为 8193 if !strings.Contains(addr, ":") { addr = net.JoinHostPort(addr, "8193") } host, portStr, err := net.SplitHostPort(addr) if err != nil { return fmt.Errorf("cnc: invalid FANUC address %q for machine %d (expected \"IP:端口\"): %w", addr, machineID, err) } port, err := strconv.Atoi(portStr) if err != nil { return fmt.Errorf("cnc: invalid FANUC port %q for machine %d: %w", portStr, machineID, err) } client := focas.NewClient(host, port, focas.WithTimeout(m.timeout)) if err := client.Connect(); err != nil { return fmt.Errorf("cnc: connect %s failed: %w", addr, err) } defer client.Disconnect() if err := client.WriteMacro(macroNo, value); err != nil { return fmt.Errorf("cnc: write macro #%d=%v to %s failed: %w", macroNo, value, addr, err) } slog.Info("cnc: macro written", "machineId", machineID, "macroNo", macroNo, "value", value, "addr", addr) return nil } // Release 释放资源(当前为短连接模式,无长连接需要释放)。 func (m *Manager) Release() {}