github.com/simpleiot/simpleiot@v0.18.3/modbus/reg.go (about)

     1  package modbus
     2  
     3  import (
     4  	"errors"
     5  	"sync"
     6  )
     7  
     8  // Reg defines a Modbus register
     9  type Reg struct {
    10  	Address  uint16
    11  	Value    uint16
    12  	Validate func(value uint16) bool
    13  }
    14  
    15  // RegProvider is the interface for a register provider.
    16  // Regs is the canonical implementation.
    17  type RegProvider interface {
    18  	ReadReg(address int) (uint16, error)
    19  	WriteReg(address int, value uint16) error
    20  	ReadInputReg(address int) (uint16, error)
    21  	ReadDiscreteInput(num int) (bool, error)
    22  	ReadCoil(num int) (bool, error)
    23  	WriteCoil(num int, value bool) error
    24  }
    25  
    26  // Regs represents all registers in a modbus device and provides functions
    27  // to read/write 16-bit and bit values. This register module assumes all
    28  // register types map into one address space
    29  // as described in the modbus spec
    30  // (http://www.modbus.org/docs/Modbus_Application_Protocol_V1_1b3.pdf)
    31  // on page 6 and 7.
    32  // All operations on Regs are threadsafe and protected by a mutex.
    33  type Regs struct {
    34  	regs []Reg
    35  	lock sync.RWMutex
    36  }
    37  
    38  // AddReg is used to add a modbus register to the server.
    39  // the callback function is called when the reg is updated
    40  // The register can be updated by word or bit operations.
    41  func (r *Regs) AddReg(address int, count int) {
    42  	r.lock.Lock()
    43  	defer r.lock.Unlock()
    44  	// first check if reg already exists
    45  	for i := 0; i < count; i++ {
    46  		found := false
    47  		adr := address + i
    48  		for _, reg := range r.regs {
    49  			if reg.Address == uint16(adr) {
    50  				found = true
    51  			}
    52  		}
    53  		if !found {
    54  			r.regs = append(r.regs, Reg{uint16(adr), 0, nil})
    55  		}
    56  	}
    57  }
    58  
    59  // ErrUnknownRegister is returned if a validator is added on a register that has
    60  // not been added.
    61  var ErrUnknownRegister = errors.New("unknown register")
    62  
    63  // AddRegValueValidator is used to add a validator function to a modbus register.
    64  // The validator function is called when a modbus client tries to write a value.
    65  // If the value is invalid, ExcIllegalValue (modbus exception 3) is returned to
    66  // the client.
    67  func (r *Regs) AddRegValueValidator(address int, validate func(uint16) bool) error {
    68  	r.lock.Lock()
    69  	defer r.lock.Unlock()
    70  	for ix := range r.regs {
    71  		if r.regs[ix].Address == uint16(address) {
    72  			r.regs[ix].Validate = validate
    73  			return nil
    74  		}
    75  	}
    76  	return ErrUnknownRegister
    77  }
    78  
    79  func (r *Regs) readReg(address int) (uint16, error) {
    80  	for _, reg := range r.regs {
    81  		if reg.Address == uint16(address) {
    82  			return reg.Value, nil
    83  		}
    84  	}
    85  
    86  	return 0, ExcIllegalAddress
    87  }
    88  
    89  // ReadReg is used to read a modbus holding register
    90  func (r *Regs) ReadReg(address int) (uint16, error) {
    91  	r.lock.RLock()
    92  	defer r.lock.RUnlock()
    93  	v, err := r.readReg(address)
    94  	return v, err
    95  }
    96  
    97  // ReadInputReg is used to read a modbus input register
    98  func (r *Regs) ReadInputReg(address int) (uint16, error) {
    99  	return r.ReadReg(address)
   100  }
   101  
   102  func (r *Regs) writeReg(address int, value uint16) error {
   103  	for i, reg := range r.regs {
   104  		if reg.Address == uint16(address) {
   105  			// if a validator is present, check if the value is allowed
   106  			if reg.Validate != nil && !reg.Validate(value) {
   107  				return ExcIllegalValue
   108  			}
   109  			(r.regs)[i].Value = value
   110  			return nil
   111  		}
   112  	}
   113  
   114  	return ExcIllegalAddress
   115  }
   116  
   117  // WriteReg is used to write a modbus register
   118  func (r *Regs) WriteReg(address int, value uint16) error {
   119  	r.lock.Lock()
   120  	defer r.lock.Unlock()
   121  	return r.writeReg(address, value)
   122  }
   123  
   124  // AddCoil is used to add a discrete io to the register map.
   125  // Note coils are aliased on top of other registers, so coil 20
   126  // would be register 1 bit 4 (16 + 4 = 20).
   127  func (r *Regs) AddCoil(num int) {
   128  	regAddress := num / 16
   129  	r.AddReg(regAddress, 1)
   130  }
   131  
   132  // ReadCoil gets a coil value
   133  func (r *Regs) ReadCoil(num int) (bool, error) {
   134  	regAddress := (num / 16)
   135  	regValue, err := r.ReadReg(regAddress)
   136  	if err != nil {
   137  		return false, err
   138  	}
   139  
   140  	bitPos := uint16(num % 16)
   141  	ret := (regValue & (1 << bitPos)) != 0
   142  	return ret, nil
   143  }
   144  
   145  // ReadDiscreteInput gets a discrete input
   146  func (r *Regs) ReadDiscreteInput(num int) (bool, error) {
   147  	return r.ReadCoil(num)
   148  }
   149  
   150  // WriteCoil writes a coil value
   151  func (r *Regs) WriteCoil(num int, value bool) error {
   152  	r.lock.Lock()
   153  	defer r.lock.Unlock()
   154  
   155  	regAddress := (num / 16)
   156  	regValue, err := r.readReg(regAddress)
   157  	if err != nil {
   158  		return err
   159  	}
   160  
   161  	bitPos := uint16(num % 16)
   162  
   163  	if value {
   164  		regValue |= 1 << bitPos
   165  	} else {
   166  		regValue &= ^(1 << bitPos)
   167  	}
   168  
   169  	return r.writeReg(regAddress, regValue)
   170  }
   171  
   172  // ReadRegUint32 reads a uint32 from regs
   173  func (r *Regs) ReadRegUint32(address int) (uint32, error) {
   174  	r.lock.Lock()
   175  	defer r.lock.Unlock()
   176  
   177  	regs := make([]uint16, 2)
   178  
   179  	var err error
   180  	regs[0], err = r.readReg(address)
   181  	if err != nil {
   182  		return 0, err
   183  	}
   184  	regs[1], err = r.readReg(address + 1)
   185  	if err != nil {
   186  		return 0, err
   187  	}
   188  
   189  	return RegsToUint32(regs)[0], nil
   190  }
   191  
   192  // WriteRegUint32 writes a uint32 to regs
   193  func (r *Regs) WriteRegUint32(address int, value uint32) error {
   194  	r.lock.Lock()
   195  	defer r.lock.Unlock()
   196  
   197  	regs := Uint32ToRegs([]uint32{value})
   198  
   199  	for i, reg := range regs {
   200  		err := r.writeReg(address+i, reg)
   201  		if err != nil {
   202  			return err
   203  		}
   204  	}
   205  
   206  	return nil
   207  }
   208  
   209  // ReadRegInt32 reads a int32 from regs
   210  func (r *Regs) ReadRegInt32(address int) (int32, error) {
   211  	r.lock.Lock()
   212  	defer r.lock.Unlock()
   213  
   214  	regs := make([]uint16, 2)
   215  
   216  	var err error
   217  	regs[0], err = r.readReg(address)
   218  	if err != nil {
   219  		return 0, err
   220  	}
   221  	regs[1], err = r.readReg(address + 1)
   222  	if err != nil {
   223  		return 0, err
   224  	}
   225  
   226  	return RegsToInt32(regs)[0], nil
   227  }
   228  
   229  // WriteRegInt32 writes a int32 to regs
   230  func (r *Regs) WriteRegInt32(address int, value int32) error {
   231  	r.lock.Lock()
   232  	defer r.lock.Unlock()
   233  
   234  	regs := Int32ToRegs([]int32{value})
   235  
   236  	for i, reg := range regs {
   237  		err := r.writeReg(address+i, reg)
   238  		if err != nil {
   239  			return err
   240  		}
   241  	}
   242  
   243  	return nil
   244  }
   245  
   246  // ReadRegFloat32 reads a float32 from regs
   247  func (r *Regs) ReadRegFloat32(address int) (float32, error) {
   248  	r.lock.Lock()
   249  	defer r.lock.Unlock()
   250  
   251  	regs := make([]uint16, 2)
   252  
   253  	var err error
   254  	regs[0], err = r.readReg(address)
   255  	if err != nil {
   256  		return 0, err
   257  	}
   258  	regs[1], err = r.readReg(address + 1)
   259  	if err != nil {
   260  		return 0, err
   261  	}
   262  
   263  	return RegsToFloat32(regs)[0], nil
   264  }
   265  
   266  // WriteRegFloat32 writes a float32 to regs
   267  func (r *Regs) WriteRegFloat32(address int, value float32) error {
   268  	r.lock.Lock()
   269  	defer r.lock.Unlock()
   270  
   271  	regs := Float32ToRegs([]float32{value})
   272  
   273  	for i, reg := range regs {
   274  		err := r.writeReg(address+i, reg)
   275  		if err != nil {
   276  			return err
   277  		}
   278  	}
   279  
   280  	return nil
   281  }