modbusbridge/tcphandler.go
2022-12-15 11:10:01 +01:00

128 lines
3.5 KiB
Go

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
}