Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 53 additions & 34 deletions plugins/inputs/modbus/modbus.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,14 @@ type Modbus struct {
type register struct {
Type string
RegistersRange []registerRange
ReadValue func(uint16, uint16) ([]byte, error)
Fields []fieldContainer
}

type fieldContainer struct {
Name string `toml:"name"`
ByteOrder string `toml:"byte_order"`
DataType string `toml:"data_type"`
Scale float32 `toml:"scale"`
Scale float64 `toml:"scale"`
Address []uint16 `toml:"address"`
value interface{}
}
Expand Down Expand Up @@ -155,13 +154,7 @@ func (m *Modbus) Init() error {
return fmt.Errorf("device name is empty")
}

err := connect(m)
if err != nil {
m.isConnected = false
return err
}

err = m.InitRegister(m.DiscreteInputs, cDiscreteInputs)
err := m.InitRegister(m.DiscreteInputs, cDiscreteInputs)
if err != nil {
return err
}
Expand Down Expand Up @@ -224,21 +217,7 @@ func (m *Modbus) InitRegister(fields []fieldContainer, name string) error {
}
}

var fn func(uint16, uint16) ([]byte, error)

if name == cDiscreteInputs {
fn = m.client.ReadDiscreteInputs
} else if name == cCoils {
fn = m.client.ReadCoils
} else if name == cInputRegisters {
fn = m.client.ReadInputRegisters
} else if name == cHoldingRegisters {
fn = m.client.ReadHoldingRegisters
} else {
return fmt.Errorf("not Valid function")
}

m.registers = append(m.registers, register{name, registersRange, fn, fields})
m.registers = append(m.registers, register{name, registersRange, fields})

return nil
}
Expand Down Expand Up @@ -306,6 +285,31 @@ func connect(m *Modbus) error {
}
}

func disconnect(m *Modbus) error {
u, err := url.Parse(m.Controller)
if err != nil {
return err
}

switch u.Scheme {
case "tcp":
m.tcpHandler.Close()
return nil
case "file":
if m.TransmissionMode == "RTU" {
m.rtuHandler.Close()
return nil
} else if m.TransmissionMode == "ASCII" {
m.asciiHandler.Close()
return nil
} else {
return fmt.Errorf("invalid protocol '%s' - '%s' ", u.Scheme, m.TransmissionMode)
}
default:
return fmt.Errorf("invalid controller")
}
}

func validateFieldContainers(t []fieldContainer, n string) error {
nameEncountered := map[string]bool{}
for _, item := range t {
Expand Down Expand Up @@ -379,13 +383,27 @@ func removeDuplicates(elements []uint16) []uint16 {
return result
}

func readRegisterValues(m *Modbus, rt string, rr registerRange) ([]byte, error) {
if rt == cDiscreteInputs {
return m.client.ReadDiscreteInputs(uint16(rr.address), uint16(rr.length))
} else if rt == cCoils {
return m.client.ReadCoils(uint16(rr.address), uint16(rr.length))
} else if rt == cInputRegisters {
return m.client.ReadInputRegisters(uint16(rr.address), uint16(rr.length))
} else if rt == cHoldingRegisters {
return m.client.ReadHoldingRegisters(uint16(rr.address), uint16(rr.length))
} else {
return []byte{}, fmt.Errorf("not Valid function")
}
}

func (m *Modbus) getFields() error {
for _, register := range m.registers {
rawValues := make(map[uint16][]byte)
bitRawValues := make(map[uint16]uint16)
for _, rr := range register.RegistersRange {
address := rr.address
readValues, err := register.ReadValue(uint16(rr.address), uint16(rr.length))
readValues, err := readRegisterValues(m, register.Type, rr)
if err != nil {
return err
}
Expand Down Expand Up @@ -530,23 +548,23 @@ func format32(f string, r uint32) interface{} {
}
}

func scale16toFloat32(s float32, v uint16) float32 {
return float32(v) * s
func scale16toFloat32(s float64, v uint16) float64 {
return float64(v) * s
}

func scale32toFloat32(s float32, v uint32) float32 {
return float32(v) * s
func scale32toFloat32(s float64, v uint32) float64 {
return float64(float64(v) * float64(s))
}

func scaleInt16(s float32, v int16) int16 {
return int16(float32(v) * s)
func scaleInt16(s float64, v int16) int16 {
return int16(float64(v) * s)
}

func scaleUint16(s float32, v uint16) uint16 {
return uint16(float32(v) * s)
func scaleUint16(s float64, v uint16) uint16 {
return uint16(float64(v) * s)
}

func scaleUint32(s float32, v uint32) uint32 {
func scaleUint32(s float64, v uint32) uint32 {
return uint32(float64(v) * float64(s))
}

Expand All @@ -562,6 +580,7 @@ func (m *Modbus) Gather(acc telegraf.Accumulator) error {

err := m.getFields()
if err != nil {
disconnect(m)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to disconnect?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because if something wrong happened we need to close the connection,

for example, if I turn off my serial controller and then turn on, I get multiple errors about the connection.

m.isConnected = false
return err
}
Expand Down
12 changes: 6 additions & 6 deletions plugins/inputs/modbus/modbus_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ func TestHoldingRegisters(t *testing.T) {
quantity uint16
byteOrder string
dataType string
scale float32
scale float64
write []byte
read interface{}
}{
Expand All @@ -137,7 +137,7 @@ func TestHoldingRegisters(t *testing.T) {
dataType: "FLOAT32",
scale: 0.1,
write: []byte{0x08, 0x98},
read: float32(220),
read: float64(220),
},
{
name: "register0_register1_ab_float32",
Expand All @@ -147,7 +147,7 @@ func TestHoldingRegisters(t *testing.T) {
dataType: "FLOAT32",
scale: 0.001,
write: []byte{0x00, 0x00, 0x03, 0xE8},
read: float32(1),
read: float64(1),
},
{
name: "register1_register2_abcd_float32",
Expand All @@ -157,7 +157,7 @@ func TestHoldingRegisters(t *testing.T) {
dataType: "FLOAT32",
scale: 0.1,
write: []byte{0x00, 0x00, 0x08, 0x98},
read: float32(220),
read: float64(220),
},
{
name: "register3_register4_abcd_float32",
Expand All @@ -167,7 +167,7 @@ func TestHoldingRegisters(t *testing.T) {
dataType: "FLOAT32",
scale: 0.1,
write: []byte{0x00, 0x00, 0x08, 0x98},
read: float32(220),
read: float64(220),
},
{
name: "register7_ab_float32",
Expand All @@ -177,7 +177,7 @@ func TestHoldingRegisters(t *testing.T) {
dataType: "FLOAT32",
scale: 0.1,
write: []byte{0x01, 0xF4},
read: float32(50),
read: float64(50),
},
{
name: "register10_ab_uint16",
Expand Down