package relay import ( "context" "errors" "github.com/grid-x/modbus" "go.uber.org/zap" "io" "io/fs" "os" "strings" "time" ) type Relay interface { io.Closer // SetSlave 设置继电器的 UnitID SetSlave(byte) // On 打开继电器 On(int) error // Off 关闭继电器 Off(int) error // OnAll 打开所有继电器 OnAll() error // OffAll 关闭所有继电器 OffAll() error } type device struct { h modbus.ClientHandler c modbus.Client } func (r *device) SetSlave(slaveID byte) { r.h.SetSlave(slaveID) } func (r *device) Close() error { return r.h.Close() } func (r *device) OnAll() error { _, err := r.c.WriteMultipleCoils(0, 16, []byte{0xFF, 0xFF}) return err } func (r *device) OffAll() error { _, err := r.c.WriteMultipleCoils(0, 16, []byte{0x00, 0x00}) return err } func (r *device) On(num int) error { _, err := r.c.WriteSingleCoil(uint16(num), 0xFF00) return err } func (r *device) Off(num int) error { _, err := r.c.WriteSingleCoil(uint16(num), 0x0000) return err } func New(address string) (Relay, error) { zap.S().Infoln("连接继电器: ", address) ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() a := make(chan struct{}) go func() { defer close(a) for { select { case <-ctx.Done(): zap.S().Infoln("等待继电器资源释放超时:", address) return case <-time.After(3 * time.Second): _, err := os.OpenFile(address, os.O_RDWR, 0666) var e *fs.PathError if errors.As(err, &e) && strings.Contains(e.Error(), "busy") { zap.S().Infoln("等待继电器资源释放:", address) } else { return } } } }() <-a h := modbus.NewRTUClientHandler(address) h.SlaveID = 1 h.BaudRate = 9600 h.DataBits = 8 h.Parity = "N" h.StopBits = 1 if err := h.Connect(); err != nil { return nil, err } c := modbus.NewClient(h) return &device{h, c}, nil }