package main import ( "sync" "github.com/simonvetter/modbus" ) // Handler object, passed to the NewServer() type modbusTcpHandler struct { // this lock is used to avoid concurrency issues between goroutines, as // handler methods are called from different requests lock sync.RWMutex } // Coil handler method. // This method gets called whenever a valid modbus request asking for a coil operation is // received by the server. func (handler *modbusTcpHandler) HandleCoils(req *modbus.CoilsRequest) (res []bool, err error) { if rtu_client == nil { Logger.Print("Rtu client not running.") return } err = rtu_client.SetUnitId(req.UnitId) if err != nil { Logger.Printf("Could not set unit id (%s).", err.Error()) err = modbus.ErrConfigurationError return } handler.lock.Lock() defer handler.lock.Unlock() if req.IsWrite { err = rtu_client.WriteCoils(req.Addr, req.Args) if err != nil { Logger.Printf("Could not write coil(s) (%s).", err.Error()) err = modbus.ErrServerDeviceFailure return } } else { res, err = rtu_client.ReadCoils(req.Addr, req.Quantity) if err != nil { Logger.Printf("Could not read coil(s) (%s).", err.Error()) err = modbus.ErrServerDeviceFailure return } } err = nil return } // Discrete input handler method. // Note that we're returning ErrIllegalFunction unconditionally. func (handler *modbusTcpHandler) HandleDiscreteInputs(req *modbus.DiscreteInputsRequest) (res []bool, err error) { // this is the equivalent of saying // "discrete inputs are not supported by this device" err = modbus.ErrIllegalFunction return } // Holding register handler method. // This method gets called whenever a valid modbus request asking for a holding register // operation (either read or write) received by the server. func (handler *modbusTcpHandler) HandleHoldingRegisters(req *modbus.HoldingRegistersRequest) (res []uint16, err error) { if rtu_client == nil { Logger.Print("Rtu client not running.") return } err = rtu_client.SetUnitId(req.UnitId) if err != nil { Logger.Printf("Could not set unit id (%s).", err.Error()) err = modbus.ErrConfigurationError return } handler.lock.Lock() defer handler.lock.Unlock() if req.IsWrite { err = rtu_client.WriteRegisters(req.Addr, req.Args) if err != nil { Logger.Printf("Could not write holding register(s) (%s).", err.Error()) err = modbus.ErrServerDeviceFailure return } } else { res, err = rtu_client.ReadRegisters(req.Addr, req.Quantity, modbus.HOLDING_REGISTER) if err != nil { Logger.Printf("Could not read holding register(s) (%s).", err.Error()) err = modbus.ErrServerDeviceFailure return } } err = nil return } // Input register handler method. // This method gets called whenever a valid modbus request asking for an input register // operation is received by the server. // Note that input registers are always read-only as per the modbus spec. func (handler *modbusTcpHandler) HandleInputRegisters(req *modbus.InputRegistersRequest) (res []uint16, err error) { if rtu_client == nil { Logger.Print("Rtu client not running.") return } err = rtu_client.SetUnitId(req.UnitId) if err != nil { Logger.Printf("Could not set unit id (%s).", err.Error()) err = modbus.ErrConfigurationError return } handler.lock.Lock() defer handler.lock.Unlock() res, err = rtu_client.ReadRegisters(req.Addr, req.Quantity, modbus.INPUT_REGISTER) if err != nil { Logger.Printf("Could not read input register(s) (%s).", err.Error()) err = modbus.ErrServerDeviceFailure return } err = nil return }