modbus客户端添加设置字节序接口

modbus服务配置删除id字段
This commit is contained in:
walker 2023-12-11 17:28:52 +08:00
parent a8c9c6857a
commit 374dc77a0e
6 changed files with 58 additions and 37 deletions

View File

@ -18,9 +18,8 @@ func main() {
}))) })))
dc := model.NewDC(make([]byte, 2), make([]byte, 2)) dc := model.NewDC(make([]byte, 2), make([]byte, 2))
mds, err := service.NewModbusDcService(&proto.ModbusConfig{ mds, err := service.NewModbusDcService(&proto.ModbusConfig{
Id: 1,
Url: "tcp://127.0.0.1:502", Url: "tcp://127.0.0.1:502",
UnitId: 1, UnitId: 2,
Timeout: 500, Timeout: 500,
Interval: 1000, Interval: 1000,
Mapping: []*proto.ModbusDcMapping{ Mapping: []*proto.ModbusDcMapping{

View File

@ -1,5 +1,12 @@
package modbus package modbus
type Endianness int32
const (
BigEndian Endianness = 0
LittleEndian Endianness = 1
)
// 在modbus协议中主机正常是客户端 // 在modbus协议中主机正常是客户端
type MasterClient interface { type MasterClient interface {
// 启动 // 启动
@ -12,6 +19,8 @@ type MasterClient interface {
IsConnected() bool IsConnected() bool
// 设置从机id // 设置从机id
SetUnitId(id uint8) error SetUnitId(id uint8) error
// 设置字节序
SetEndianness(endianness Endianness) error
// 读线圈,位操作,功能码为0x01 // 读线圈,位操作,功能码为0x01
ReadCoil(addr uint16, quantity uint16) ([]bool, error) ReadCoil(addr uint16, quantity uint16) ([]bool, error)
// 读一个线圈 // 读一个线圈

View File

@ -51,6 +51,17 @@ func (c *client) SetUnitId(id uint8) error {
return c.cli.SetUnitId(id) return c.cli.SetUnitId(id)
} }
func (c *client) SetEndianness(endianness Endianness) error {
switch endianness {
case BigEndian:
return c.cli.SetEncoding(modbus.BIG_ENDIAN, modbus.HIGH_WORD_FIRST)
case LittleEndian:
return c.cli.SetEncoding(modbus.LITTLE_ENDIAN, modbus.HIGH_WORD_FIRST)
default:
return fmt.Errorf("unknown endianness value %v", endianness)
}
}
func (c *client) Start() error { func (c *client) Start() error {
c.started = true c.started = true
return nil return nil
@ -73,7 +84,7 @@ func (c *client) connManage(ctx context.Context) {
if c.started && !c.connected { // 已经启动, 尝试重连 if c.started && !c.connected { // 已经启动, 尝试重连
err := c.cli.Open() err := c.cli.Open()
if err != nil { if err != nil {
slog.Error("modbus客户端尝试连接失败", "url", c.url, "err", err) slog.Error("modbus客户端连接失败", "url", c.url, "err", err)
} else { } else {
c.connected = true c.connected = true
slog.Info("modbus客户端连接成功", "url", c.url) slog.Info("modbus客户端连接成功", "url", c.url)

View File

@ -31,7 +31,6 @@ message Modbus {
// modbus任务配置 // modbus任务配置
message ModbusConfig { message ModbusConfig {
uint32 id = 1;
string url = 2; // string url = 2; //
uint32 unitId = 3; // unitId uint32 unitId = 3; // unitId
Modbus.Endianness endianness = 4; // 16 Modbus.Endianness endianness = 4; // 16
@ -51,7 +50,7 @@ message ModbusDcMapping {
Modbus.Function function = 1; // Modbus.Function function = 1; //
uint32 addr = 2; // ,,,,(2) uint32 addr = 2; // ,,,,(2)
uint32 quantity = 3; // ,,,,(2) uint32 quantity = 3; // ,,,,(2)
Modbus.WriteStrategy writeStrategy = 4; // (), Modbus.WriteStrategy writeStrategy = 4; // ,
DataType type = 5; // DataType type = 5; //
uint32 start = 6; // /,,,, uint32 start = 6; // /,,,,
} }

View File

@ -13,6 +13,7 @@ import (
sproto "joylink.club/iot/service/proto" sproto "joylink.club/iot/service/proto"
) )
// Modbus驱动采集服务
type modbusDcService struct { type modbusDcService struct {
config *sproto.ModbusConfig config *sproto.ModbusConfig
cli modbus.MasterClient cli modbus.MasterClient
@ -36,6 +37,8 @@ func NewModbusDcService(config *sproto.ModbusConfig, dc model.DC) (IotService, e
return nil, errors.Join(err, fmt.Errorf("modbus客户端创建失败,url=%s", config.Url)) return nil, errors.Join(err, fmt.Errorf("modbus客户端创建失败,url=%s", config.Url))
} }
cli.SetUnitId(uint8(config.UnitId)) cli.SetUnitId(uint8(config.UnitId))
cli.SetEndianness(convertEndianness(config.Endianness))
cli.Start()
s := &modbusDcService{ s := &modbusDcService{
config: config, config: config,
cli: cli, cli: cli,
@ -58,23 +61,23 @@ func (m *modbusDcService) initOnUpdateTask() {
} }
m.dc.On(et, func(d model.DC) { m.dc.On(et, func(d model.DC) {
if !m.cli.IsConnected() { if !m.cli.IsConnected() {
slog.Warn("Modbus驱动采集服务数据更新写入失败,modbus客户端未连接", "id", m.config.Id, "url", m.config.Url, "Function", mdm.Function) slog.Warn("Modbus驱动采集服务数据更新写入失败,modbus客户端未连接", "url", m.config.Url, "Function", mdm.Function)
return return
} }
switch mdm.Function { switch mdm.Function {
case sproto.Modbus_WriteCoil, sproto.Modbus_WriteCoils, sproto.Modbus_RWCoils: case sproto.Modbus_WriteCoil, sproto.Modbus_WriteCoils, sproto.Modbus_RWCoils:
err := m.cli.WriteCoils(uint16(mdm.Addr), m.GetDcBits(mdm)) err := m.cli.WriteCoils(uint16(mdm.Addr), m.GetDcBits(mdm))
if err != nil { if err != nil {
slog.Error("Modbus驱动采集服务写入线圈失败", "id", m.config.Id, "url", m.config.Url, "error", err, "Function", mdm.Function) slog.Error("Modbus驱动采集服务写入线圈失败", "url", m.config.Url, "error", err, "Function", mdm.Function)
} else { } else {
slog.Info("Modbus驱动采集服务写入线圈成功", "id", m.config.Id, "url", m.config.Url, "Function", mdm.Function) slog.Info("Modbus驱动采集服务写入线圈成功", "url", m.config.Url, "Function", mdm.Function)
} }
case sproto.Modbus_WriteRegister, sproto.Modbus_WriteRegisters, sproto.Modbus_RWRegisters: case sproto.Modbus_WriteRegister, sproto.Modbus_WriteRegisters, sproto.Modbus_RWRegisters:
err := m.cli.WriteRegisterBytes(uint16(mdm.Addr), m.GetDcBytes(mdm)) err := m.cli.WriteRegisterBytes(uint16(mdm.Addr), m.GetDcBytes(mdm))
if err != nil { if err != nil {
slog.Error("Modbus驱动采集服务写入寄存器失败", "id", m.config.Id, "url", m.config.Url, "error", err, "Function", mdm.Function) slog.Error("Modbus驱动采集服务写入寄存器失败", "url", m.config.Url, "error", err, "Function", mdm.Function)
} else { } else {
slog.Info("Modbus驱动采集服务写入寄存器成功", "id", m.config.Id, "url", m.config.Url, "Function", mdm.Function) slog.Info("Modbus驱动采集服务写入寄存器成功", "url", m.config.Url, "Function", mdm.Function)
} }
} }
}) })
@ -92,11 +95,10 @@ func isWriteFunction(modbus_Function sproto.Modbus_Function) bool {
} }
func (m *modbusDcService) run(ctx context.Context) { func (m *modbusDcService) run(ctx context.Context) {
m.cli.Start()
for { for {
select { select {
case <-ctx.Done(): case <-ctx.Done():
slog.Info("Modbus驱动采集服务退出", "id", m.config.Id, "url", m.config.Url) slog.Info("Modbus驱动采集服务退出", "url", m.config.Url)
return return
default: default:
} }
@ -112,45 +114,45 @@ func (m *modbusDcService) mappingTaskExecute() {
case sproto.Modbus_ReadCoil, sproto.Modbus_RWCoils: case sproto.Modbus_ReadCoil, sproto.Modbus_RWCoils:
data, err := m.cli.ReadCoil(uint16(mdm.Addr), uint16(mdm.Quantity)) data, err := m.cli.ReadCoil(uint16(mdm.Addr), uint16(mdm.Quantity))
if err != nil { if err != nil {
slog.Error("Modbus驱动采集服务读取线圈失败", "id", m.config.Id, "url", m.config.Url, "error", err) slog.Error("Modbus驱动采集服务读取线圈失败", "url", m.config.Url, "error", err)
continue continue
} }
err = m.updateDcByBits(mdm, data) err = m.updateDcByBits(mdm, data)
if err != nil { if err != nil {
slog.Error("Modbus驱动采集服务更新驱采数据失败", "id", m.config.Id, "url", m.config.Url, "error", err) slog.Error("Modbus驱动采集服务更新驱采数据失败", "url", m.config.Url, "error", err)
continue continue
} }
case sproto.Modbus_ReadDiscreteInput: case sproto.Modbus_ReadDiscreteInput:
data, err := m.cli.ReadDiscreteInput(uint16(mdm.Addr), uint16(mdm.Quantity)) data, err := m.cli.ReadDiscreteInput(uint16(mdm.Addr), uint16(mdm.Quantity))
if err != nil { if err != nil {
slog.Error("Modbus驱动采集服务读取离散输入失败", "id", m.config.Id, "url", m.config.Url, "error", err) slog.Error("Modbus驱动采集服务读取离散输入失败", "url", m.config.Url, "error", err)
continue continue
} }
err = m.updateDcByBits(mdm, data) err = m.updateDcByBits(mdm, data)
if err != nil { if err != nil {
slog.Error("Modbus驱动采集服务更新驱采数据失败", "id", m.config.Id, "url", m.config.Url, "error", err) slog.Error("Modbus驱动采集服务更新驱采数据失败", "url", m.config.Url, "error", err)
continue continue
} }
case sproto.Modbus_ReadHoldingRegister, sproto.Modbus_RWRegisters: case sproto.Modbus_ReadHoldingRegister, sproto.Modbus_RWRegisters:
data, err := m.cli.ReadHoldingRegisterBytes(uint16(mdm.Addr), uint16(mdm.Quantity*2)) data, err := m.cli.ReadHoldingRegisterBytes(uint16(mdm.Addr), uint16(mdm.Quantity*2))
if err != nil { if err != nil {
slog.Error("Modbus驱动采集服务读取保持寄存器失败", "id", m.config.Id, "url", m.config.Url, "error", err) slog.Error("Modbus驱动采集服务读取保持寄存器失败", "url", m.config.Url, "error", err)
continue continue
} }
err = m.updateDcByBytes(mdm, data) err = m.updateDcByBytes(mdm, data)
if err != nil { if err != nil {
slog.Error("Modbus驱动采集服务更新驱采数据失败", "id", m.config.Id, "url", m.config.Url, "error", err) slog.Error("Modbus驱动采集服务更新驱采数据失败", "url", m.config.Url, "error", err)
continue continue
} }
case sproto.Modbus_ReadInputRegister: case sproto.Modbus_ReadInputRegister:
data, err := m.cli.ReadInputRegisterBytes(uint16(mdm.Addr), uint16(mdm.Quantity*2)) data, err := m.cli.ReadInputRegisterBytes(uint16(mdm.Addr), uint16(mdm.Quantity*2))
if err != nil { if err != nil {
slog.Error("Modbus驱动采集服务读取输入寄存器失败", "id", m.config.Id, "url", m.config.Url, "error", err) slog.Error("Modbus驱动采集服务读取输入寄存器失败", "url", m.config.Url, "error", err)
continue continue
} }
err = m.updateDcByBytes(mdm, data) err = m.updateDcByBytes(mdm, data)
if err != nil { if err != nil {
slog.Error("Modbus驱动采集服务更新驱采数据失败", "id", m.config.Id, "url", m.config.Url, "error", err) slog.Error("Modbus驱动采集服务更新驱采数据失败", "url", m.config.Url, "error", err)
continue continue
} }
case sproto.Modbus_WriteCoil: case sproto.Modbus_WriteCoil:
@ -158,7 +160,7 @@ func (m *modbusDcService) mappingTaskExecute() {
bits := m.GetDcBits(mdm) bits := m.GetDcBits(mdm)
err := m.cli.WriteCoil(uint16(mdm.Addr), bits[0]) err := m.cli.WriteCoil(uint16(mdm.Addr), bits[0])
if err != nil { if err != nil {
slog.Error("Modbus驱动采集服务写单线圈失败", "id", m.config.Id, "url", m.config.Url, "error", err) slog.Error("Modbus驱动采集服务写单线圈失败", "url", m.config.Url, "error", err)
continue continue
} }
} }
@ -167,7 +169,7 @@ func (m *modbusDcService) mappingTaskExecute() {
bits := m.GetDcBits(mdm) bits := m.GetDcBits(mdm)
err := m.cli.WriteCoils(uint16(mdm.Addr), bits) err := m.cli.WriteCoils(uint16(mdm.Addr), bits)
if err != nil { if err != nil {
slog.Error("Modbus驱动采集服务写多线圈失败", "id", m.config.Id, "url", m.config.Url, "error", err) slog.Error("Modbus驱动采集服务写多线圈失败", "url", m.config.Url, "error", err)
continue continue
} }
} }
@ -176,7 +178,7 @@ func (m *modbusDcService) mappingTaskExecute() {
data := m.GetDcBytes(mdm) data := m.GetDcBytes(mdm)
err := m.cli.WriteRegisterBytes(uint16(mdm.Addr), data) err := m.cli.WriteRegisterBytes(uint16(mdm.Addr), data)
if err != nil { if err != nil {
slog.Error("Modbus驱动采集服务写单寄存器失败", "id", m.config.Id, "url", m.config.Url, "error", err) slog.Error("Modbus驱动采集服务写单寄存器失败", "url", m.config.Url, "error", err)
continue continue
} }
} }
@ -185,14 +187,14 @@ func (m *modbusDcService) mappingTaskExecute() {
data := m.GetDcBytes(mdm) data := m.GetDcBytes(mdm)
err := m.cli.WriteRegisterBytes(uint16(mdm.Addr), data) err := m.cli.WriteRegisterBytes(uint16(mdm.Addr), data)
if err != nil { if err != nil {
slog.Error("Modbus驱动采集服务写多寄存器失败", "id", m.config.Id, "url", m.config.Url, "error", err) slog.Error("Modbus驱动采集服务写多寄存器失败", "url", m.config.Url, "error", err)
continue continue
} }
} }
} }
} }
} else { } else {
slog.Error("Modbus驱动采集服务映射任务执行失败,Modbus未连接", "id", m.config.Id, "url", m.config.Url) slog.Error("Modbus驱动采集服务映射任务执行失败,Modbus未连接", "url", m.config.Url)
} }
} }
@ -267,3 +269,13 @@ func (m *modbusDcService) Stop() error {
modbus.DeleteClient(m.config.Url) modbus.DeleteClient(m.config.Url)
return nil return nil
} }
func convertEndianness(endianness sproto.Modbus_Endianness) modbus.Endianness {
switch endianness {
case sproto.Modbus_BigEndian:
return modbus.BigEndian
case sproto.Modbus_LittleEndian:
return modbus.LittleEndian
}
return modbus.BigEndian
}

View File

@ -276,7 +276,6 @@ type ModbusConfig struct {
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
Url string `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"` // 连接地址 Url string `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"` // 连接地址
UnitId uint32 `protobuf:"varint,3,opt,name=unitId,proto3" json:"unitId,omitempty"` // 从机unitId UnitId uint32 `protobuf:"varint,3,opt,name=unitId,proto3" json:"unitId,omitempty"` // 从机unitId
Endianness Modbus_Endianness `protobuf:"varint,4,opt,name=endianness,proto3,enum=iot_service.Modbus_Endianness" json:"endianness,omitempty"` // 16位寄存器字节序 Endianness Modbus_Endianness `protobuf:"varint,4,opt,name=endianness,proto3,enum=iot_service.Modbus_Endianness" json:"endianness,omitempty"` // 16位寄存器字节序
@ -317,13 +316,6 @@ func (*ModbusConfig) Descriptor() ([]byte, []int) {
return file_dc_proto_rawDescGZIP(), []int{1} return file_dc_proto_rawDescGZIP(), []int{1}
} }
func (x *ModbusConfig) GetId() uint32 {
if x != nil {
return x.Id
}
return 0
}
func (x *ModbusConfig) GetUrl() string { func (x *ModbusConfig) GetUrl() string {
if x != nil { if x != nil {
return x.Url return x.Url
@ -375,7 +367,7 @@ type ModbusDcMapping struct {
Function Modbus_Function `protobuf:"varint,1,opt,name=function,proto3,enum=iot_service.Modbus_Function" json:"function,omitempty"` // 功能 Function Modbus_Function `protobuf:"varint,1,opt,name=function,proto3,enum=iot_service.Modbus_Function" json:"function,omitempty"` // 功能
Addr uint32 `protobuf:"varint,2,opt,name=addr,proto3" json:"addr,omitempty"` // 起始地址,当功能为位功能时,表示起始位地址,当功能为寄存器功能时,表示起始字(2个字节)地址 Addr uint32 `protobuf:"varint,2,opt,name=addr,proto3" json:"addr,omitempty"` // 起始地址,当功能为位功能时,表示起始位地址,当功能为寄存器功能时,表示起始字(2个字节)地址
Quantity uint32 `protobuf:"varint,3,opt,name=quantity,proto3" json:"quantity,omitempty"` // 数量,当功能为位功能时,表示位数,当功能为寄存器功能时,表示字(2个字节)数 Quantity uint32 `protobuf:"varint,3,opt,name=quantity,proto3" json:"quantity,omitempty"` // 数量,当功能为位功能时,表示位数,当功能为寄存器功能时,表示字(2个字节)数
WriteStrategy Modbus_WriteStrategy `protobuf:"varint,4,opt,name=writeStrategy,proto3,enum=iot_service.Modbus_WriteStrategy" json:"writeStrategy,omitempty"` // 当功能为写入类功能时(不包含读写类功能),写策略 WriteStrategy Modbus_WriteStrategy `protobuf:"varint,4,opt,name=writeStrategy,proto3,enum=iot_service.Modbus_WriteStrategy" json:"writeStrategy,omitempty"` // 当功能为写入类功能时,写策略
Type DataType `protobuf:"varint,5,opt,name=type,proto3,enum=iot_service.DataType" json:"type,omitempty"` // 对应数据类型 Type DataType `protobuf:"varint,5,opt,name=type,proto3,enum=iot_service.DataType" json:"type,omitempty"` // 对应数据类型
Start uint32 `protobuf:"varint,6,opt,name=start,proto3" json:"start,omitempty"` // 驱动/采集码表中的起始下标,当功能为位功能时,表示起始位,当功能为寄存器功能时,表示起始字节 Start uint32 `protobuf:"varint,6,opt,name=start,proto3" json:"start,omitempty"` // 驱动/采集码表中的起始下标,当功能为位功能时,表示起始位,当功能为寄存器功能时,表示起始字节
} }
@ -477,9 +469,8 @@ var file_dc_proto_rawDesc = []byte{
0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x10, 0x01, 0x22, 0x2d, 0x0a, 0x0a, 0x45, 0x6e, 0x64, 0x69, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x10, 0x01, 0x22, 0x2d, 0x0a, 0x0a, 0x45, 0x6e, 0x64, 0x69,
0x61, 0x6e, 0x6e, 0x65, 0x73, 0x73, 0x12, 0x0d, 0x0a, 0x09, 0x42, 0x69, 0x67, 0x45, 0x6e, 0x64, 0x61, 0x6e, 0x6e, 0x65, 0x73, 0x73, 0x12, 0x0d, 0x0a, 0x09, 0x42, 0x69, 0x67, 0x45, 0x6e, 0x64,
0x69, 0x61, 0x6e, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x4c, 0x69, 0x74, 0x74, 0x6c, 0x65, 0x45, 0x69, 0x61, 0x6e, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x4c, 0x69, 0x74, 0x74, 0x6c, 0x65, 0x45,
0x6e, 0x64, 0x69, 0x61, 0x6e, 0x10, 0x01, 0x22, 0xf6, 0x01, 0x0a, 0x0c, 0x4d, 0x6f, 0x64, 0x62, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x10, 0x01, 0x22, 0xe6, 0x01, 0x0a, 0x0c, 0x4d, 0x6f, 0x64, 0x62,
0x75, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x75, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18,
0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18,
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x6e, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x6e,
0x69, 0x74, 0x49, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x75, 0x6e, 0x69, 0x74, 0x69, 0x74, 0x49, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x75, 0x6e, 0x69, 0x74,
0x49, 0x64, 0x12, 0x3e, 0x0a, 0x0a, 0x65, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x6e, 0x65, 0x73, 0x73, 0x49, 0x64, 0x12, 0x3e, 0x0a, 0x0a, 0x65, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x6e, 0x65, 0x73, 0x73,