fix: 寄存器应当根据主机地址进行区分以支持多主机单从机模式。

main v1.0.1
NoahLan 1 year ago
parent 4e4c3b1a03
commit 6835c6f011

@ -1,8 +1,96 @@
package handler
import "sync"
// DataMgr 数据管理器
type DataMgr struct {
Coils []byte // 线圈DO数字输出
DiscreteInputs []byte // 离散输入DI数字输入
HoldingRegisters []uint16 // 保持寄存器AO模拟输出
InputRegisters []uint16 // 输入寄存器AI模拟输入
Address uint8 // 主机地址
coils []byte // 线圈DO数字输出
discreteInputs []byte // 离散输入DI数字输入
holdingRegisters []uint16 // 保持寄存器AO模拟输出
inputRegisters []uint16 // 输入寄存器AI模拟输入
mu sync.RWMutex
}
func NewDataManager(address uint8) *DataMgr {
return &DataMgr{
Address: address,
coils: make([]byte, 65536),
discreteInputs: make([]byte, 65536),
holdingRegisters: make([]uint16, 65536),
inputRegisters: make([]uint16, 65536),
mu: sync.RWMutex{},
}
}
func (m *DataMgr) ReadCoils(start, end int) []byte {
m.mu.RLock()
defer m.mu.RUnlock()
return m.coils[start:end]
}
func (m *DataMgr) ReadDiscreteInputs(start, end int) []byte {
m.mu.RLock()
defer m.mu.RUnlock()
return m.discreteInputs[start:end]
}
func (m *DataMgr) ReadHoldingRegisters(start, end int) []uint16 {
m.mu.RLock()
defer m.mu.RUnlock()
return m.holdingRegisters[start:end]
}
func (m *DataMgr) ReadInputRegisters(start, end int) []uint16 {
m.mu.RLock()
defer m.mu.RUnlock()
return m.inputRegisters[start:end]
}
// WriteSingleCoil 写单个线圈数据
func (m *DataMgr) WriteSingleCoil(idx int, val byte) {
m.mu.Lock()
defer m.mu.Unlock()
m.coils[idx] = val
}
// WriteSingleHoldingRegister 写入单个保持寄存器
func (m *DataMgr) WriteSingleHoldingRegister(idx int, val uint16) {
m.mu.Lock()
defer m.mu.Unlock()
m.holdingRegisters[idx] = val
}
// WriteCoils 写多个线圈数据
// idxList 表示预写入的线圈地址列表
// values 表示预写入的线圈数据,与地址列表一一对应
func (m *DataMgr) WriteCoils(idxList []int, values []byte) {
m.mu.Lock()
defer m.mu.Unlock()
for i, idx := range idxList {
m.coils[idx] = values[i]
}
}
func (m *DataMgr) WriteHoldingRegisters(values []uint16, start int, end ...int) int {
m.mu.Lock()
defer m.mu.Unlock()
var e int
if len(end) == 0 {
e = len(m.holdingRegisters)
} else {
e = end[0]
}
return copy(m.holdingRegisters[start:e], values)
}

@ -19,7 +19,11 @@ func ReadCoils(s *Handler, packet protocol.Packet) ([]byte, *protocol.MError) {
}
data := make([]byte, 1+dataSize)
data[0] = byte(dataSize)
for i, value := range s.Coils[register:endRegister] {
dataMgr := s.DataManager(packet.GetAddress())
coils := dataMgr.ReadCoils(register, endRegister)
for i, value := range coils {
if value != 0 {
shift := uint(i) % 8
data[1+i/8] |= byte(1 << shift)
@ -40,7 +44,11 @@ func ReadDiscreteInputs(s *Handler, packet protocol.Packet) ([]byte, *protocol.M
}
data := make([]byte, 1+dataSize)
data[0] = byte(dataSize)
for i, value := range s.DiscreteInputs[register:endRegister] {
dataMgr := s.DataManager(packet.GetAddress())
discreteInputs := dataMgr.ReadDiscreteInputs(register, endRegister)
for i, value := range discreteInputs {
if value != 0 {
shift := uint(i) % 8
data[1+i/8] |= byte(1 << shift)
@ -55,7 +63,8 @@ func ReadHoldingRegisters(s *Handler, packet protocol.Packet) ([]byte, *protocol
if endRegister > 65536 {
return []byte{}, &protocol.IllegalDataAddress
}
return append([]byte{byte(numRegs * 2)}, util.Uint16ToBytes(s.byteOrder, s.HoldingRegisters[register:endRegister])...), &protocol.Success
dataMgr := s.DataManager(packet.GetAddress())
return append([]byte{byte(numRegs * 2)}, util.Uint16ToBytes(s.byteOrder, dataMgr.ReadHoldingRegisters(register, endRegister))...), &protocol.Success
}
// ReadInputRegisters 读输入寄存器AO 功能码0x04
@ -64,7 +73,8 @@ func ReadInputRegisters(s *Handler, packet protocol.Packet) ([]byte, *protocol.M
if endRegister > 65536 {
return []byte{}, &protocol.IllegalDataAddress
}
return append([]byte{byte(numRegs * 2)}, util.Uint16ToBytes(s.byteOrder, s.InputRegisters[register:endRegister])...), &protocol.Success
dataMgr := s.DataManager(packet.GetAddress())
return append([]byte{byte(numRegs * 2)}, util.Uint16ToBytes(s.byteOrder, dataMgr.ReadInputRegisters(register, endRegister))...), &protocol.Success
}
// WriteSingleCoil 写单个线圈寄存器 功能码0x05
@ -75,7 +85,9 @@ func WriteSingleCoil(s *Handler, packet protocol.Packet) ([]byte, *protocol.MErr
if value != 0 {
value = 1
}
s.Coils[register] = byte(value)
dataMgr := s.DataManager(packet.GetAddress())
dataMgr.WriteSingleCoil(register, byte(value))
return data[0:4], &protocol.Success
}
@ -83,7 +95,10 @@ func WriteSingleCoil(s *Handler, packet protocol.Packet) ([]byte, *protocol.MErr
func WriteHoldingRegister(s *Handler, packet protocol.Packet) ([]byte, *protocol.MError) {
data := packet.GetBody()
register, value := serialize.RegisterAddressAndValue(data)
s.HoldingRegisters[register] = value
dataMgr := s.DataManager(packet.GetAddress())
dataMgr.WriteSingleHoldingRegister(register, value)
return data[0:4], &protocol.Success
}
@ -102,10 +117,14 @@ func WriteMultipleCoils(s *Handler, packet protocol.Packet) ([]byte, *protocol.M
// return []byte{}, &IllegalDataAddress
//}
idxList := make([]int, 0)
values := make([]byte, 0)
bitCount := 0
for i, value := range valueBytes {
for bitPos := uint(0); bitPos < 8; bitPos++ {
s.Coils[register+(i*8)+int(bitPos)] = util.BitAtPosition(value, bitPos)
idxList = append(idxList, register+(i*8)+int(bitPos))
values = append(values, util.BitAtPosition(value, bitPos))
bitCount++
if bitCount >= numRegs {
break
@ -116,6 +135,9 @@ func WriteMultipleCoils(s *Handler, packet protocol.Packet) ([]byte, *protocol.M
}
}
dataMgr := s.DataManager(packet.GetAddress())
dataMgr.WriteCoils(idxList, values)
return data[0:4], &protocol.Success
}
@ -133,7 +155,9 @@ func WriteHoldingRegisters(s *Handler, packet protocol.Packet) ([]byte, *protoco
// Copy data to memory
values := util.BytesToUint16(s.byteOrder, valueBytes)
valuesUpdated := copy(s.HoldingRegisters[register:], values)
dataMgr := s.DataManager(packet.GetAddress())
valuesUpdated := dataMgr.WriteHoldingRegisters(values, register)
if valuesUpdated == numRegs {
err = &protocol.Success
data = pkgData[0:4]

@ -7,6 +7,7 @@ import (
rt "git.noahlan.cn/noahlan/nnet/router"
"git.noahlan.cn/noahlan/ntool-biz/nmodbus/protocol"
"git.noahlan.cn/noahlan/ntool/nlog"
"sync"
)
var _ rt.Handler = (*Handler)(nil)
@ -17,10 +18,8 @@ type (
functions map[uint8]FunctionHandler
DiscreteInputs []byte // 离散输入
Coils []byte // 线圈
HoldingRegisters []uint16 // 保持寄存器
InputRegisters []uint16 // 输入寄存器
dataMap map[uint8]*DataMgr
mu sync.RWMutex
}
FunctionHandler func(handler *Handler, pkg protocol.Packet) ([]byte, *protocol.MError)
@ -30,10 +29,8 @@ func NewHandler(byteOrder binary.ByteOrder) *Handler {
p := &Handler{
byteOrder: byteOrder,
functions: make(map[uint8]FunctionHandler),
DiscreteInputs: make([]byte, 65536),
Coils: make([]byte, 65536),
HoldingRegisters: make([]uint16, 65536),
InputRegisters: make([]uint16, 65536),
dataMap: make(map[uint8]*DataMgr),
mu: sync.RWMutex{},
}
// 添加功能码对应功能
@ -53,6 +50,18 @@ func (p *Handler) RegisterFunction(fnCode uint8, fn FunctionHandler) {
p.functions[fnCode] = fn
}
func (p *Handler) DataManager(address uint8) *DataMgr {
p.mu.Lock()
defer p.mu.Unlock()
ret, ok := p.dataMap[address]
if !ok {
ret = NewDataManager(address)
p.dataMap[address] = ret
}
return ret
}
func (p *Handler) Handle(conn *connection.Connection, pkg packet.IPacket) {
pp, ok := pkg.(protocol.Packet)
if !ok {

@ -20,7 +20,7 @@ import (
// defer ngin.Stop()
//
// handler.RegisterFunction(code, func()) // 自定义功能码对应的处理方法
// hr := handler.HoldingRegisters[0] // 获取地址为0x00的保持寄存器 2字节 uint16 (也可以设置,但非线程安全的)
// hr := handler.holdingRegisters[0] // 获取地址为0x00的保持寄存器 2字节 uint16 (也可以设置,但非线程安全的)
//
// ngin.ListenTCP(config.TCPServerConf{Protocol: "tcp", Addr: "0.0.0.0:5502"})
func NewModbusTCPEngine(conf ModbusTCPConf) (*nnet.Engine, *handler.Handler) {

Loading…
Cancel
Save