parent
a8c9c6857a
commit
374dc77a0e
3
main.go
3
main.go
|
@ -18,9 +18,8 @@ func main() {
|
|||
})))
|
||||
dc := model.NewDC(make([]byte, 2), make([]byte, 2))
|
||||
mds, err := service.NewModbusDcService(&proto.ModbusConfig{
|
||||
Id: 1,
|
||||
Url: "tcp://127.0.0.1:502",
|
||||
UnitId: 1,
|
||||
UnitId: 2,
|
||||
Timeout: 500,
|
||||
Interval: 1000,
|
||||
Mapping: []*proto.ModbusDcMapping{
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
package modbus
|
||||
|
||||
type Endianness int32
|
||||
|
||||
const (
|
||||
BigEndian Endianness = 0
|
||||
LittleEndian Endianness = 1
|
||||
)
|
||||
|
||||
// 在modbus协议中,主机正常是客户端
|
||||
type MasterClient interface {
|
||||
// 启动
|
||||
|
@ -12,6 +19,8 @@ type MasterClient interface {
|
|||
IsConnected() bool
|
||||
// 设置从机id
|
||||
SetUnitId(id uint8) error
|
||||
// 设置字节序
|
||||
SetEndianness(endianness Endianness) error
|
||||
// 读线圈,位操作,功能码为0x01
|
||||
ReadCoil(addr uint16, quantity uint16) ([]bool, error)
|
||||
// 读一个线圈
|
||||
|
|
|
@ -51,6 +51,17 @@ func (c *client) SetUnitId(id uint8) error {
|
|||
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 {
|
||||
c.started = true
|
||||
return nil
|
||||
|
@ -73,7 +84,7 @@ func (c *client) connManage(ctx context.Context) {
|
|||
if c.started && !c.connected { // 已经启动, 尝试重连
|
||||
err := c.cli.Open()
|
||||
if err != nil {
|
||||
slog.Error("modbus客户端尝试连接失败", "url", c.url, "err", err)
|
||||
slog.Error("modbus客户端连接失败", "url", c.url, "err", err)
|
||||
} else {
|
||||
c.connected = true
|
||||
slog.Info("modbus客户端连接成功", "url", c.url)
|
||||
|
|
|
@ -31,7 +31,6 @@ message Modbus {
|
|||
|
||||
// modbus任务配置
|
||||
message ModbusConfig {
|
||||
uint32 id = 1;
|
||||
string url = 2; // 连接地址
|
||||
uint32 unitId = 3; // 从机unitId
|
||||
Modbus.Endianness endianness = 4; // 16位寄存器字节序
|
||||
|
@ -51,7 +50,7 @@ message ModbusDcMapping {
|
|||
Modbus.Function function = 1; // 功能
|
||||
uint32 addr = 2; // 起始地址,当功能为位功能时,表示起始位地址,当功能为寄存器功能时,表示起始字(2个字节)地址
|
||||
uint32 quantity = 3; // 数量,当功能为位功能时,表示位数,当功能为寄存器功能时,表示字(2个字节)数
|
||||
Modbus.WriteStrategy writeStrategy = 4; // 当功能为写入类功能时(不包含读写类功能),写策略
|
||||
Modbus.WriteStrategy writeStrategy = 4; // 当功能为写入类功能时,写策略
|
||||
DataType type = 5; // 对应数据类型
|
||||
uint32 start = 6; // 驱动/采集码表中的起始下标,当功能为位功能时,表示起始位,当功能为寄存器功能时,表示起始字节
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
sproto "joylink.club/iot/service/proto"
|
||||
)
|
||||
|
||||
// Modbus驱动采集服务
|
||||
type modbusDcService struct {
|
||||
config *sproto.ModbusConfig
|
||||
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))
|
||||
}
|
||||
cli.SetUnitId(uint8(config.UnitId))
|
||||
cli.SetEndianness(convertEndianness(config.Endianness))
|
||||
cli.Start()
|
||||
s := &modbusDcService{
|
||||
config: config,
|
||||
cli: cli,
|
||||
|
@ -58,23 +61,23 @@ func (m *modbusDcService) initOnUpdateTask() {
|
|||
}
|
||||
m.dc.On(et, func(d model.DC) {
|
||||
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
|
||||
}
|
||||
switch mdm.Function {
|
||||
case sproto.Modbus_WriteCoil, sproto.Modbus_WriteCoils, sproto.Modbus_RWCoils:
|
||||
err := m.cli.WriteCoils(uint16(mdm.Addr), m.GetDcBits(mdm))
|
||||
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 {
|
||||
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:
|
||||
err := m.cli.WriteRegisterBytes(uint16(mdm.Addr), m.GetDcBytes(mdm))
|
||||
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 {
|
||||
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) {
|
||||
m.cli.Start()
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
slog.Info("Modbus驱动采集服务退出", "id", m.config.Id, "url", m.config.Url)
|
||||
slog.Info("Modbus驱动采集服务退出", "url", m.config.Url)
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
@ -112,45 +114,45 @@ func (m *modbusDcService) mappingTaskExecute() {
|
|||
case sproto.Modbus_ReadCoil, sproto.Modbus_RWCoils:
|
||||
data, err := m.cli.ReadCoil(uint16(mdm.Addr), uint16(mdm.Quantity))
|
||||
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
|
||||
}
|
||||
err = m.updateDcByBits(mdm, data)
|
||||
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
|
||||
}
|
||||
case sproto.Modbus_ReadDiscreteInput:
|
||||
data, err := m.cli.ReadDiscreteInput(uint16(mdm.Addr), uint16(mdm.Quantity))
|
||||
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
|
||||
}
|
||||
err = m.updateDcByBits(mdm, data)
|
||||
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
|
||||
}
|
||||
case sproto.Modbus_ReadHoldingRegister, sproto.Modbus_RWRegisters:
|
||||
data, err := m.cli.ReadHoldingRegisterBytes(uint16(mdm.Addr), uint16(mdm.Quantity*2))
|
||||
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
|
||||
}
|
||||
err = m.updateDcByBytes(mdm, data)
|
||||
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
|
||||
}
|
||||
case sproto.Modbus_ReadInputRegister:
|
||||
data, err := m.cli.ReadInputRegisterBytes(uint16(mdm.Addr), uint16(mdm.Quantity*2))
|
||||
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
|
||||
}
|
||||
err = m.updateDcByBytes(mdm, data)
|
||||
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
|
||||
}
|
||||
case sproto.Modbus_WriteCoil:
|
||||
|
@ -158,7 +160,7 @@ func (m *modbusDcService) mappingTaskExecute() {
|
|||
bits := m.GetDcBits(mdm)
|
||||
err := m.cli.WriteCoil(uint16(mdm.Addr), bits[0])
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -167,7 +169,7 @@ func (m *modbusDcService) mappingTaskExecute() {
|
|||
bits := m.GetDcBits(mdm)
|
||||
err := m.cli.WriteCoils(uint16(mdm.Addr), bits)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -176,7 +178,7 @@ func (m *modbusDcService) mappingTaskExecute() {
|
|||
data := m.GetDcBytes(mdm)
|
||||
err := m.cli.WriteRegisterBytes(uint16(mdm.Addr), data)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -185,14 +187,14 @@ func (m *modbusDcService) mappingTaskExecute() {
|
|||
data := m.GetDcBytes(mdm)
|
||||
err := m.cli.WriteRegisterBytes(uint16(mdm.Addr), data)
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} 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)
|
||||
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
|
||||
}
|
||||
|
|
|
@ -276,7 +276,6 @@ type ModbusConfig struct {
|
|||
sizeCache protoimpl.SizeCache
|
||||
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"` // 连接地址
|
||||
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位寄存器字节序
|
||||
|
@ -317,13 +316,6 @@ func (*ModbusConfig) Descriptor() ([]byte, []int) {
|
|||
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 {
|
||||
if x != nil {
|
||||
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"` // 功能
|
||||
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个字节)数
|
||||
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"` // 对应数据类型
|
||||
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,
|
||||
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,
|
||||
0x6e, 0x64, 0x69, 0x61, 0x6e, 0x10, 0x01, 0x22, 0xf6, 0x01, 0x0a, 0x0c, 0x4d, 0x6f, 0x64, 0x62,
|
||||
0x75, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18,
|
||||
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, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18,
|
||||
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,
|
||||
0x49, 0x64, 0x12, 0x3e, 0x0a, 0x0a, 0x65, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x6e, 0x65, 0x73, 0x73,
|
||||
|
|
Loading…
Reference in New Issue