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 }