Fix/extend support of fixed point values on input (modbus plugin) (#7869)
* Add input float types FIXED/UFIXED (#7317)
This commit is contained in:
parent
dab07c61e8
commit
d233b4c27f
|
|
@ -9,7 +9,7 @@ Registers via Modbus TCP or Modbus RTU/ASCII.
|
|||
[[inputs.modbus]]
|
||||
## Connection Configuration
|
||||
##
|
||||
## The module supports connections to PLCs via MODBUS/TCP or
|
||||
## The plugin supports connections to PLCs via MODBUS/TCP or
|
||||
## via serial line communication in binary (RTU) or readable (ASCII) encoding
|
||||
##
|
||||
## Device name
|
||||
|
|
@ -43,45 +43,47 @@ Registers via Modbus TCP or Modbus RTU/ASCII.
|
|||
##
|
||||
|
||||
## Digital Variables, Discrete Inputs and Coils
|
||||
## name - the variable name
|
||||
## address - variable address
|
||||
## measurement - the (optional) measurement name, defaults to "modbus"
|
||||
## name - the variable name
|
||||
## address - variable address
|
||||
|
||||
discrete_inputs = [
|
||||
{ name = "Start", address = [0]},
|
||||
{ name = "Stop", address = [1]},
|
||||
{ name = "Reset", address = [2]},
|
||||
{ name = "EmergencyStop", address = [3]},
|
||||
{ name = "start", address = [0]},
|
||||
{ name = "stop", address = [1]},
|
||||
{ name = "reset", address = [2]},
|
||||
{ name = "emergency_stop", address = [3]},
|
||||
]
|
||||
coils = [
|
||||
{ name = "Motor1-Run", address = [0]},
|
||||
{ name = "Motor1-Jog", address = [1]},
|
||||
{ name = "Motor1-Stop", address = [2]},
|
||||
{ name = "motor1_run", address = [0]},
|
||||
{ name = "motor1_jog", address = [1]},
|
||||
{ name = "motor1_stop", address = [2]},
|
||||
]
|
||||
|
||||
## Analog Variables, Input Registers and Holding Registers
|
||||
## measurement - the (optional) measurement name, defaults to "modbus"
|
||||
## name - the variable name
|
||||
## byte_order - the ordering of bytes
|
||||
## name - the variable name
|
||||
## byte_order - the ordering of bytes
|
||||
## |---AB, ABCD - Big Endian
|
||||
## |---BA, DCBA - Little Endian
|
||||
## |---BADC - Mid-Big Endian
|
||||
## |---CDAB - Mid-Little Endian
|
||||
## data_type - INT16, UINT16, INT32, UINT32, INT64, UINT64, FLOAT32, FLOAT32-IEEE (the IEEE 754 binary representation)
|
||||
## data_type - INT16, UINT16, INT32, UINT32, INT64, UINT64, FLOAT32-IEEE (the IEEE 754 binary representation)
|
||||
## FLOAT32 (deprecated), FIXED, UFIXED (fixed-point representation on input)
|
||||
## scale - the final numeric variable representation
|
||||
## address - variable address
|
||||
|
||||
holding_registers = [
|
||||
{ name = "PowerFactor", byte_order = "AB", data_type = "FLOAT32", scale=0.01, address = [8]},
|
||||
{ name = "Voltage", byte_order = "AB", data_type = "FLOAT32", scale=0.1, address = [0]},
|
||||
{ name = "Energy", byte_order = "ABCD", data_type = "FLOAT32", scale=0.001, address = [5,6]},
|
||||
{ name = "Current", byte_order = "ABCD", data_type = "FLOAT32", scale=0.001, address = [1,2]},
|
||||
{ name = "Frequency", byte_order = "AB", data_type = "FLOAT32", scale=0.1, address = [7]},
|
||||
{ name = "Power", byte_order = "ABCD", data_type = "FLOAT32", scale=0.1, address = [3,4]},
|
||||
{ name = "power_factor", byte_order = "AB", data_type = "FIXED", scale=0.01, address = [8]},
|
||||
{ name = "voltage", byte_order = "AB", data_type = "FIXED", scale=0.1, address = [0]},
|
||||
{ name = "energy", byte_order = "ABCD", data_type = "FIXED", scale=0.001, address = [5,6]},
|
||||
{ name = "current", byte_order = "ABCD", data_type = "FIXED", scale=0.001, address = [1,2]},
|
||||
{ name = "frequency", byte_order = "AB", data_type = "UFIXED", scale=0.1, address = [7]},
|
||||
{ name = "power", byte_order = "ABCD", data_type = "UFIXED", scale=0.1, address = [3,4]},
|
||||
]
|
||||
input_registers = [
|
||||
{ name = "TankLevel", byte_order = "AB", data_type = "INT16", scale=1.0, address = [0]},
|
||||
{ name = "TankPH", byte_order = "AB", data_type = "INT16", scale=1.0, address = [1]},
|
||||
{ name = "Pump1-Speed", byte_order = "ABCD", data_type = "INT32", scale=1.0, address = [3,4]},
|
||||
{ name = "tank_level", byte_order = "AB", data_type = "INT16", scale=1.0, address = [0]},
|
||||
{ name = "tank_ph", byte_order = "AB", data_type = "INT16", scale=1.0, address = [1]},
|
||||
{ name = "pump1_speed", byte_order = "ABCD", data_type = "INT32", scale=1.0, address = [3,4]},
|
||||
]
|
||||
```
|
||||
|
||||
|
|
@ -90,6 +92,40 @@ Registers via Modbus TCP or Modbus RTU/ASCII.
|
|||
Metric are custom and configured using the `discrete_inputs`, `coils`,
|
||||
`holding_register` and `input_registers` options.
|
||||
|
||||
### Usage of `data_type`
|
||||
|
||||
The field `data_type` defines the representation of the data value on input from the modbus registers.
|
||||
The input values are then converted from the given `data_type` to a type that is apropriate when
|
||||
sending the value to the output plugin. These output types are usually one of string,
|
||||
integer or floating-point-number. The size of the output type is assumed to be large enough
|
||||
for all supported input types. The mapping from the input type to the output type is fixed
|
||||
and cannot be configured.
|
||||
|
||||
#### Integers: `INT16`, `UINT16`, `INT32`, `UINT32`, `INT64`, `UINT64`
|
||||
|
||||
These types are used for integer input values. Select the one that matches your modbus data source.
|
||||
|
||||
#### Floating Point: `FLOAT32-IEEE`
|
||||
|
||||
Use this type if your modbus registers contain a value that is encoded in this format. This type
|
||||
always includes the sign and therefore there exists no variant.
|
||||
|
||||
#### Fixed Point: `FIXED`, `UFIXED` (`FLOAT32`)
|
||||
|
||||
These types are handled as an integer type on input, but are converted to floating point representation
|
||||
for further processing (e.g. scaling). Use one of these types when the input value is a decimal fixed point
|
||||
representation of a non-integer value.
|
||||
|
||||
Select the type `UFIXED` when the input type is declared to hold unsigned integer values, which cannot
|
||||
be negative. The documentation of your modbus device should indicate this by a term like
|
||||
'uint16 containing fixed-point representation with N decimal places'.
|
||||
|
||||
Select the type `FIXED` when the input type is declared to hold signed integer values. Your documentation
|
||||
of the modbus device should indicate this with a term like 'int32 containing fixed-point representation
|
||||
with N decimal places'.
|
||||
|
||||
(FLOAT32 is deprecated and should not be used any more. UFIXED provides the same conversion
|
||||
from unsigned values).
|
||||
|
||||
### Example Output
|
||||
|
||||
|
|
|
|||
|
|
@ -132,17 +132,18 @@ const sampleConfig = `
|
|||
## |---BA, DCBA - Little Endian
|
||||
## |---BADC - Mid-Big Endian
|
||||
## |---CDAB - Mid-Little Endian
|
||||
## data_type - INT16, UINT16, INT32, UINT32, INT64, UINT64, FLOAT32, FLOAT32-IEEE (the IEEE 754 binary representation)
|
||||
## data_type - INT16, UINT16, INT32, UINT32, INT64, UINT64, FLOAT32-IEEE (the IEEE 754 binary representation)
|
||||
## FLOAT32, FIXED, UFIXED (fixed-point representation on input)
|
||||
## scale - the final numeric variable representation
|
||||
## address - variable address
|
||||
|
||||
holding_registers = [
|
||||
{ name = "power_factor", byte_order = "AB", data_type = "FLOAT32", scale=0.01, address = [8]},
|
||||
{ name = "voltage", byte_order = "AB", data_type = "FLOAT32", scale=0.1, address = [0]},
|
||||
{ name = "energy", byte_order = "ABCD", data_type = "FLOAT32", scale=0.001, address = [5,6]},
|
||||
{ name = "current", byte_order = "ABCD", data_type = "FLOAT32", scale=0.001, address = [1,2]},
|
||||
{ name = "frequency", byte_order = "AB", data_type = "FLOAT32", scale=0.1, address = [7]},
|
||||
{ name = "power", byte_order = "ABCD", data_type = "FLOAT32", scale=0.1, address = [3,4]},
|
||||
{ name = "power_factor", byte_order = "AB", data_type = "FIXED", scale=0.01, address = [8]},
|
||||
{ name = "voltage", byte_order = "AB", data_type = "FIXED", scale=0.1, address = [0]},
|
||||
{ name = "energy", byte_order = "ABCD", data_type = "FIXED", scale=0.001, address = [5,6]},
|
||||
{ name = "current", byte_order = "ABCD", data_type = "FIXED", scale=0.001, address = [1,2]},
|
||||
{ name = "frequency", byte_order = "AB", data_type = "UFIXED", scale=0.1, address = [7]},
|
||||
{ name = "power", byte_order = "ABCD", data_type = "UFIXED", scale=0.1, address = [3,4]},
|
||||
]
|
||||
input_registers = [
|
||||
{ name = "tank_level", byte_order = "AB", data_type = "INT16", scale=1.0, address = [0]},
|
||||
|
|
@ -354,7 +355,7 @@ func validateFieldContainers(t []fieldContainer, n string) error {
|
|||
|
||||
// search data type
|
||||
switch item.DataType {
|
||||
case "UINT16", "INT16", "UINT32", "INT32", "UINT64", "INT64", "FLOAT32-IEEE", "FLOAT32":
|
||||
case "UINT16", "INT16", "UINT32", "INT32", "UINT64", "INT64", "FLOAT32-IEEE", "FLOAT32", "FIXED", "UFIXED":
|
||||
break
|
||||
default:
|
||||
return fmt.Errorf("invalid data type '%s' in '%s' - '%s'", item.DataType, n, item.Name)
|
||||
|
|
@ -511,16 +512,30 @@ func convertDataType(t fieldContainer, bytes []byte) interface{} {
|
|||
e32 := convertEndianness32(t.ByteOrder, bytes)
|
||||
f32 := math.Float32frombits(e32)
|
||||
return scaleFloat32(t.Scale, f32)
|
||||
case "FLOAT32":
|
||||
case "FIXED":
|
||||
if len(bytes) == 2 {
|
||||
e16 := convertEndianness16(t.ByteOrder, bytes)
|
||||
return scale16toFloat32(t.Scale, e16)
|
||||
f16 := int16(e16)
|
||||
return scale16toFloat(t.Scale, f16)
|
||||
} else if len(bytes) == 4 {
|
||||
e32 := convertEndianness32(t.ByteOrder, bytes)
|
||||
return scale32toFloat32(t.Scale, e32)
|
||||
f32 := int32(e32)
|
||||
return scale32toFloat(t.Scale, f32)
|
||||
} else {
|
||||
e64 := convertEndianness64(t.ByteOrder, bytes)
|
||||
return scale64toFloat32(t.Scale, e64)
|
||||
f64 := int64(e64)
|
||||
return scale64toFloat(t.Scale, f64)
|
||||
}
|
||||
case "FLOAT32", "UFIXED":
|
||||
if len(bytes) == 2 {
|
||||
e16 := convertEndianness16(t.ByteOrder, bytes)
|
||||
return scale16UtoFloat(t.Scale, e16)
|
||||
} else if len(bytes) == 4 {
|
||||
e32 := convertEndianness32(t.ByteOrder, bytes)
|
||||
return scale32UtoFloat(t.Scale, e32)
|
||||
} else {
|
||||
e64 := convertEndianness64(t.ByteOrder, bytes)
|
||||
return scale64UtoFloat(t.Scale, e64)
|
||||
}
|
||||
default:
|
||||
return 0
|
||||
|
|
@ -603,15 +618,27 @@ func format64(f string, r uint64) interface{} {
|
|||
}
|
||||
}
|
||||
|
||||
func scale16toFloat32(s float64, v uint16) float64 {
|
||||
func scale16toFloat(s float64, v int16) float64 {
|
||||
return float64(v) * s
|
||||
}
|
||||
|
||||
func scale32toFloat32(s float64, v uint32) float64 {
|
||||
func scale32toFloat(s float64, v int32) float64 {
|
||||
return float64(float64(v) * float64(s))
|
||||
}
|
||||
|
||||
func scale64toFloat32(s float64, v uint64) float64 {
|
||||
func scale64toFloat(s float64, v int64) float64 {
|
||||
return float64(float64(v) * float64(s))
|
||||
}
|
||||
|
||||
func scale16UtoFloat(s float64, v uint16) float64 {
|
||||
return float64(v) * s
|
||||
}
|
||||
|
||||
func scale32UtoFloat(s float64, v uint32) float64 {
|
||||
return float64(float64(v) * float64(s))
|
||||
}
|
||||
|
||||
func scale64UtoFloat(s float64, v uint64) float64 {
|
||||
return float64(float64(v) * float64(s))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -179,6 +179,106 @@ func TestHoldingRegisters(t *testing.T) {
|
|||
write: []byte{0x01, 0xF4},
|
||||
read: float64(50),
|
||||
},
|
||||
{
|
||||
name: "register0_ab_float32_msb",
|
||||
address: []uint16{0},
|
||||
quantity: 1,
|
||||
byteOrder: "AB",
|
||||
dataType: "FLOAT32",
|
||||
scale: 0.1,
|
||||
write: []byte{0x89, 0x65},
|
||||
read: float64(3517.3),
|
||||
},
|
||||
{
|
||||
name: "register0_register1_ab_float32_msb",
|
||||
address: []uint16{0, 1},
|
||||
quantity: 2,
|
||||
byteOrder: "ABCD",
|
||||
dataType: "FLOAT32",
|
||||
scale: 0.001,
|
||||
write: []byte{0xFF, 0xFF, 0xFF, 0xFF},
|
||||
read: float64(4294967.295),
|
||||
},
|
||||
{
|
||||
name: "register5_to_register8_abcdefgh_float32",
|
||||
address: []uint16{5, 6, 7, 8},
|
||||
quantity: 4,
|
||||
byteOrder: "ABCDEFGH",
|
||||
dataType: "FLOAT32",
|
||||
scale: 0.000001,
|
||||
write: []byte{0x00, 0x00, 0x00, 0x62, 0xC6, 0xD1, 0xA9, 0xB2},
|
||||
read: float64(424242.424242),
|
||||
},
|
||||
{
|
||||
name: "register6_to_register9_hgfedcba_float32_msb",
|
||||
address: []uint16{6, 7, 8, 9},
|
||||
quantity: 4,
|
||||
byteOrder: "HGFEDCBA",
|
||||
dataType: "FLOAT32",
|
||||
scale: 0.0000000001,
|
||||
write: []byte{0xEA, 0x1E, 0x39, 0xEE, 0x8E, 0xA9, 0x54, 0xAB},
|
||||
read: float64(1234567890.9876544),
|
||||
},
|
||||
{
|
||||
name: "register0_ab_float",
|
||||
address: []uint16{0},
|
||||
quantity: 1,
|
||||
byteOrder: "AB",
|
||||
dataType: "FIXED",
|
||||
scale: 0.1,
|
||||
write: []byte{0xFF, 0xD6},
|
||||
read: float64(-4.2),
|
||||
},
|
||||
{
|
||||
name: "register1_ba_ufloat",
|
||||
address: []uint16{1},
|
||||
quantity: 1,
|
||||
byteOrder: "BA",
|
||||
dataType: "UFIXED",
|
||||
scale: 0.1,
|
||||
write: []byte{0xD8, 0xFF},
|
||||
read: float64(6549.6),
|
||||
},
|
||||
{
|
||||
name: "register4_register5_abcd_float",
|
||||
address: []uint16{4, 5},
|
||||
quantity: 2,
|
||||
byteOrder: "ABCD",
|
||||
dataType: "FIXED",
|
||||
scale: 0.1,
|
||||
write: []byte{0xFF, 0xFF, 0xFF, 0xD6},
|
||||
read: float64(-4.2),
|
||||
},
|
||||
{
|
||||
name: "register5_register6_dcba_ufloat",
|
||||
address: []uint16{5, 6},
|
||||
quantity: 2,
|
||||
byteOrder: "DCBA",
|
||||
dataType: "UFIXED",
|
||||
scale: 0.001,
|
||||
write: []byte{0xD8, 0xFF, 0xFF, 0xFF},
|
||||
read: float64(4294967.256),
|
||||
},
|
||||
{
|
||||
name: "register5_to_register8_abcdefgh_float",
|
||||
address: []uint16{5, 6, 7, 8},
|
||||
quantity: 4,
|
||||
byteOrder: "ABCDEFGH",
|
||||
dataType: "FIXED",
|
||||
scale: 0.000001,
|
||||
write: []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD6},
|
||||
read: float64(-0.000042),
|
||||
},
|
||||
{
|
||||
name: "register6_to_register9_hgfedcba_ufloat",
|
||||
address: []uint16{6, 7, 8, 9},
|
||||
quantity: 4,
|
||||
byteOrder: "HGFEDCBA",
|
||||
dataType: "UFIXED",
|
||||
scale: 0.000000001,
|
||||
write: []byte{0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF},
|
||||
read: float64(18441921395.520346504),
|
||||
},
|
||||
{
|
||||
name: "register10_ab_uint16",
|
||||
address: []uint16{10},
|
||||
|
|
|
|||
Loading…
Reference in New Issue