feat(inputs.modbus): add support for half-precision float (float16) (#12340)
This commit is contained in:
parent
da0c186a71
commit
5cb40a1882
|
|
@ -302,6 +302,7 @@ following works:
|
|||
- github.com/wavefronthq/wavefront-sdk-go [Apache License 2.0](https://github.com/wavefrontHQ/wavefront-sdk-go/blob/master/LICENSE)
|
||||
- github.com/wvanbergen/kafka [MIT License](https://github.com/wvanbergen/kafka/blob/master/LICENSE)
|
||||
- github.com/wvanbergen/kazoo-go [MIT License](https://github.com/wvanbergen/kazoo-go/blob/master/MIT-LICENSE)
|
||||
- github.com/x448/float16 [MIT License](https://github.com/x448/float16/blob/master/LICENSE)
|
||||
- github.com/xdg-go/pbkdf2 [Apache License 2.0](https://github.com/xdg-go/pbkdf2/blob/main/LICENSE)
|
||||
- github.com/xdg-go/scram [Apache License 2.0](https://github.com/xdg-go/scram/blob/master/LICENSE)
|
||||
- github.com/xdg-go/stringprep [Apache License 2.0](https://github.com/xdg-go/stringprep/blob/master/LICENSE)
|
||||
|
|
|
|||
1
go.mod
1
go.mod
|
|
@ -160,6 +160,7 @@ require (
|
|||
github.com/vmware/govmomi v0.28.1-0.20220921224932-b4b508abf208
|
||||
github.com/wavefronthq/wavefront-sdk-go v0.10.4
|
||||
github.com/wvanbergen/kafka v0.0.0-20171203153745-e2edea948ddf
|
||||
github.com/x448/float16 v0.8.4
|
||||
github.com/xdg/scram v1.0.5
|
||||
github.com/yuin/goldmark v1.5.3
|
||||
go.mongodb.org/mongo-driver v1.11.0
|
||||
|
|
|
|||
2
go.sum
2
go.sum
|
|
@ -2547,6 +2547,8 @@ github.com/wvanbergen/kafka v0.0.0-20171203153745-e2edea948ddf h1:TOV5PC6fIWwFOF
|
|||
github.com/wvanbergen/kafka v0.0.0-20171203153745-e2edea948ddf/go.mod h1:nxx7XRXbR9ykhnC8lXqQyJS0rfvJGxKyKw/sT1YOttg=
|
||||
github.com/wvanbergen/kazoo-go v0.0.0-20180202103751-f72d8611297a h1:ILoU84rj4AQ3q6cjQvtb9jBjx4xzR/Riq/zYhmDQiOk=
|
||||
github.com/wvanbergen/kazoo-go v0.0.0-20180202103751-f72d8611297a/go.mod h1:vQQATAGxVK20DC1rRubTJbZDDhhpA4QfU02pMdPxGO4=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||
github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
|
|||
## |---CDAB - Mid-Little Endian
|
||||
## data_type - INT8L, INT8H, UINT8L, UINT8H (low and high byte variants)
|
||||
## INT16, UINT16, INT32, UINT32, INT64, UINT64,
|
||||
## FLOAT32-IEEE, FLOAT64-IEEE (the IEEE 754 binary representation)
|
||||
## FLOAT16-IEEE, FLOAT32-IEEE, FLOAT64-IEEE (IEEE 754 binary representation)
|
||||
## FLOAT32, FIXED, UFIXED (fixed-point representation on input)
|
||||
## scale - the final numeric variable representation
|
||||
## address - variable address
|
||||
|
|
@ -172,7 +172,7 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
|
|||
## type *1,2 - type of the modbus field, can be
|
||||
## INT8L, INT8H, UINT8L, UINT8H (low and high byte variants)
|
||||
## INT16, UINT16, INT32, UINT32, INT64, UINT64 and
|
||||
## FLOAT32, FLOAT64 (IEEE 754 binary representation)
|
||||
## FLOAT16, FLOAT32, FLOAT64 (IEEE 754 binary representation)
|
||||
## scale *1,2 - (optional) factor to scale the variable with
|
||||
## output *1,2 - (optional) type of resulting field, can be INT64, UINT64 or FLOAT64. Defaults to FLOAT64 if
|
||||
## "scale" is provided and to the input "type" class otherwise (i.e. INT* -> INT64, etc).
|
||||
|
|
@ -307,7 +307,7 @@ the register respectively.
|
|||
These types are used for integer input values. Select the one that matches your
|
||||
modbus data source.
|
||||
|
||||
##### Floating Point: `FLOAT32-IEEE`, `FLOAT64-IEEE`
|
||||
##### Floating Point: `FLOAT16-IEEE`, `FLOAT32-IEEE`, `FLOAT64-IEEE`
|
||||
|
||||
Use these types if your modbus registers contain a value that is encoded in this
|
||||
format. These types always include the sign, therefore no variant exists.
|
||||
|
|
@ -455,10 +455,11 @@ The `type` setting specifies the datatype of the modbus register and can be
|
|||
set to `INT8L`, `INT8H`, `UINT8L`, `UINT8H` where `L` is the lower byte of the
|
||||
register and `H` is the higher byte.
|
||||
Furthermore, the types `INT16`, `UINT16`, `INT32`, `UINT32`, `INT64` or `UINT64`
|
||||
for integer types or `FLOAT32` and `FLOAT64` for IEEE 754 binary representations
|
||||
of floating point values exist. Usually the datatype of the register is listed
|
||||
in the datasheet of your modbus device in relation to the `address` described
|
||||
above.
|
||||
for integer types or `FLOAT16`, `FLOAT32` and `FLOAT64` for IEEE 754 binary
|
||||
representations of floating point values exist. `FLOAT16` denotes a
|
||||
half-precision float with a 16-bit representation.
|
||||
Usually the datatype of the register is listed in the datasheet of your modbus
|
||||
device in relation to the `address` described above.
|
||||
|
||||
This setting is ignored if the field's `omit` is set to `true` or if the
|
||||
`register` type is a bit-type (`coil` or `discrete`) and can be omitted in
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ func normalizeInputDatatype(dataType string) (string, error) {
|
|||
switch dataType {
|
||||
case "INT8L", "INT8H", "UINT8L", "UINT8H",
|
||||
"INT16", "UINT16", "INT32", "UINT32", "INT64", "UINT64",
|
||||
"FLOAT32", "FLOAT64":
|
||||
"FLOAT16", "FLOAT32", "FLOAT64":
|
||||
return dataType, nil
|
||||
}
|
||||
return "unknown", fmt.Errorf("unknown input type %q", dataType)
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@ func (c *ConfigurationOriginal) validateFieldDefinitions(fieldDefs []fieldDefini
|
|||
switch item.DataType {
|
||||
case "INT8L", "INT8H", "UINT8L", "UINT8H",
|
||||
"UINT16", "INT16", "UINT32", "INT32", "UINT64", "INT64",
|
||||
"FLOAT32-IEEE", "FLOAT64-IEEE", "FLOAT32", "FIXED", "UFIXED":
|
||||
"FLOAT16-IEEE", "FLOAT32-IEEE", "FLOAT64-IEEE", "FLOAT32", "FIXED", "UFIXED":
|
||||
default:
|
||||
return fmt.Errorf("invalid data type '%s' in '%s' - '%s'", item.DataType, registerType, item.Name)
|
||||
}
|
||||
|
|
@ -236,6 +236,8 @@ func (c *ConfigurationOriginal) normalizeInputDatatype(dataType string, words in
|
|||
default:
|
||||
return "unknown", fmt.Errorf("invalid length %d for type %q", words, dataType)
|
||||
}
|
||||
case "FLOAT16-IEEE":
|
||||
return "FLOAT16", nil
|
||||
case "FLOAT32-IEEE":
|
||||
return "FLOAT32", nil
|
||||
case "FLOAT64-IEEE":
|
||||
|
|
|
|||
|
|
@ -354,7 +354,7 @@ func (c *ConfigurationPerRequest) determineOutputDatatype(input string) (string,
|
|||
return "INT64", nil
|
||||
case "UINT8L", "UINT8H", "UINT16", "UINT32", "UINT64":
|
||||
return "UINT64", nil
|
||||
case "FLOAT32", "FLOAT64":
|
||||
case "FLOAT16", "FLOAT32", "FLOAT64":
|
||||
return "FLOAT64", nil
|
||||
}
|
||||
return "unknown", fmt.Errorf("invalid input datatype %q for determining output", input)
|
||||
|
|
@ -365,7 +365,7 @@ func (c *ConfigurationPerRequest) determineFieldLength(input string) (uint16, er
|
|||
switch input {
|
||||
case "INT8L", "INT8H", "UINT8L", "UINT8H":
|
||||
return 1, nil
|
||||
case "INT16", "UINT16":
|
||||
case "INT16", "UINT16", "FLOAT16":
|
||||
return 1, nil
|
||||
case "INT32", "UINT32", "FLOAT32":
|
||||
return 2, nil
|
||||
|
|
|
|||
|
|
@ -819,6 +819,26 @@ func TestHoldingRegisters(t *testing.T) {
|
|||
write: []byte{0x8F, 0x55, 0xC3, 0x47, 0x6A, 0x40, 0xBF, 0x9C},
|
||||
read: float64(-0.02774907295123737),
|
||||
},
|
||||
{
|
||||
name: "register240_abcd_float16",
|
||||
address: []uint16{240},
|
||||
quantity: 1,
|
||||
byteOrder: "AB",
|
||||
dataType: "FLOAT16-IEEE",
|
||||
scale: 1,
|
||||
write: []byte{0xb8, 0x14},
|
||||
read: float64(-0.509765625),
|
||||
},
|
||||
{
|
||||
name: "register240_dcba_float16",
|
||||
address: []uint16{240},
|
||||
quantity: 1,
|
||||
byteOrder: "BA",
|
||||
dataType: "FLOAT16-IEEE",
|
||||
scale: 1,
|
||||
write: []byte{0x14, 0xb8},
|
||||
read: float64(-0.509765625),
|
||||
},
|
||||
}
|
||||
|
||||
serv := mbserver.NewServer()
|
||||
|
|
@ -1381,6 +1401,37 @@ func TestRequestTypesHoldingABCD(t *testing.T) {
|
|||
write: []byte{0x40, 0x09, 0x21, 0xfb, 0x54, 0x44, 0x2e, 0xea},
|
||||
read: float64(3.14159265359000006156975359772),
|
||||
},
|
||||
{
|
||||
name: "register100_float16",
|
||||
address: 100,
|
||||
dataTypeIn: "FLOAT16",
|
||||
write: []byte{0xb8, 0x14},
|
||||
read: float64(-0.509765625),
|
||||
},
|
||||
{
|
||||
name: "register100_float16-scale_.1",
|
||||
address: 100,
|
||||
dataTypeIn: "FLOAT16",
|
||||
scale: .1,
|
||||
write: []byte{0xb8, 0x14},
|
||||
read: float64(-0.0509765625),
|
||||
},
|
||||
{
|
||||
name: "register100_float16_scale_10",
|
||||
address: 100,
|
||||
dataTypeIn: "FLOAT16",
|
||||
scale: 10,
|
||||
write: []byte{0xb8, 0x14},
|
||||
read: float64(-5.09765625),
|
||||
},
|
||||
{
|
||||
name: "register100_float16_float64_scale",
|
||||
address: 100,
|
||||
dataTypeIn: "FLOAT16",
|
||||
scale: 1.0,
|
||||
write: []byte{0xb8, 0x14},
|
||||
read: float64(-0.509765625),
|
||||
},
|
||||
}
|
||||
|
||||
serv := mbserver.NewServer()
|
||||
|
|
@ -1951,6 +2002,37 @@ func TestRequestTypesHoldingDCBA(t *testing.T) {
|
|||
write: []byte{0x40, 0x09, 0x21, 0xfb, 0x54, 0x44, 0x2e, 0xea},
|
||||
read: float64(3.14159265359000006156975359772),
|
||||
},
|
||||
{
|
||||
name: "register100_float16",
|
||||
address: 100,
|
||||
dataTypeIn: "FLOAT16",
|
||||
write: []byte{0xb8, 0x14},
|
||||
read: float64(-0.509765625),
|
||||
},
|
||||
{
|
||||
name: "register100_float16-scale_.1",
|
||||
address: 100,
|
||||
dataTypeIn: "FLOAT16",
|
||||
scale: .1,
|
||||
write: []byte{0xb8, 0x14},
|
||||
read: float64(-0.0509765625),
|
||||
},
|
||||
{
|
||||
name: "register100_float16_scale_10",
|
||||
address: 100,
|
||||
dataTypeIn: "FLOAT16",
|
||||
scale: 10,
|
||||
write: []byte{0xb8, 0x14},
|
||||
read: float64(-5.09765625),
|
||||
},
|
||||
{
|
||||
name: "register100_float16_float64_scale",
|
||||
address: 100,
|
||||
dataTypeIn: "FLOAT16",
|
||||
scale: 1.0,
|
||||
write: []byte{0xb8, 0x14},
|
||||
read: float64(-0.509765625),
|
||||
},
|
||||
}
|
||||
|
||||
serv := mbserver.NewServer()
|
||||
|
|
@ -1973,7 +2055,7 @@ func TestRequestTypesHoldingDCBA(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
|
||||
modbus := Modbus{
|
||||
Name: "TestRequestTypesHoldingABCD",
|
||||
Name: "TestRequestTypesHoldingDCBA",
|
||||
Controller: "tcp://localhost:1502",
|
||||
ConfigurationType: "request",
|
||||
Log: testutil.Logger{},
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
## |---CDAB - Mid-Little Endian
|
||||
## data_type - INT8L, INT8H, UINT8L, UINT8H (low and high byte variants)
|
||||
## INT16, UINT16, INT32, UINT32, INT64, UINT64,
|
||||
## FLOAT32-IEEE, FLOAT64-IEEE (the IEEE 754 binary representation)
|
||||
## FLOAT16-IEEE, FLOAT32-IEEE, FLOAT64-IEEE (IEEE 754 binary representation)
|
||||
## FLOAT32, FIXED, UFIXED (fixed-point representation on input)
|
||||
## scale - the final numeric variable representation
|
||||
## address - variable address
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@
|
|||
## type *1,2 - type of the modbus field, can be
|
||||
## INT8L, INT8H, UINT8L, UINT8H (low and high byte variants)
|
||||
## INT16, UINT16, INT32, UINT32, INT64, UINT64 and
|
||||
## FLOAT32, FLOAT64 (IEEE 754 binary representation)
|
||||
## FLOAT16, FLOAT32, FLOAT64 (IEEE 754 binary representation)
|
||||
## scale *1,2 - (optional) factor to scale the variable with
|
||||
## output *1,2 - (optional) type of resulting field, can be INT64, UINT64 or FLOAT64. Defaults to FLOAT64 if
|
||||
## "scale" is provided and to the input "type" class otherwise (i.e. INT* -> INT64, etc).
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@ func determineConverterScale(inType, byteOrder, outType string, scale float64) (
|
|||
return determineConverterI64Scale(outType, byteOrder, scale)
|
||||
case "UINT64":
|
||||
return determineConverterU64Scale(outType, byteOrder, scale)
|
||||
case "FLOAT16":
|
||||
return determineConverterF16Scale(outType, byteOrder, scale)
|
||||
case "FLOAT32":
|
||||
return determineConverterF32Scale(outType, byteOrder, scale)
|
||||
case "FLOAT64":
|
||||
|
|
@ -61,6 +63,8 @@ func determineConverterNoScale(inType, byteOrder, outType string) (fieldConverte
|
|||
return determineConverterI64(outType, byteOrder)
|
||||
case "UINT64":
|
||||
return determineConverterU64(outType, byteOrder)
|
||||
case "FLOAT16":
|
||||
return determineConverterF16(outType, byteOrder)
|
||||
case "FLOAT32":
|
||||
return determineConverterF32(outType, byteOrder)
|
||||
case "FLOAT64":
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ package modbus
|
|||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"github.com/x448/float16"
|
||||
)
|
||||
|
||||
type convert16 func([]byte) uint16
|
||||
|
|
@ -73,6 +75,29 @@ func determineConverterU16(outType, byteOrder string) (fieldConverterFunc, error
|
|||
return nil, fmt.Errorf("invalid output data-type: %s", outType)
|
||||
}
|
||||
|
||||
// F16 - no scale
|
||||
func determineConverterF16(outType, byteOrder string) (fieldConverterFunc, error) {
|
||||
tohost, err := endianessConverter16(byteOrder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch outType {
|
||||
case "native":
|
||||
return func(b []byte) interface{} {
|
||||
raw := tohost(b)
|
||||
return float16.Frombits(raw).Float32()
|
||||
}, nil
|
||||
case "FLOAT64":
|
||||
return func(b []byte) interface{} {
|
||||
raw := tohost(b)
|
||||
in := float16.Frombits(raw).Float32()
|
||||
return float64(in)
|
||||
}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("invalid output data-type: %s", outType)
|
||||
}
|
||||
|
||||
// I16 - scale
|
||||
func determineConverterI16Scale(outType, byteOrder string, scale float64) (fieldConverterFunc, error) {
|
||||
tohost, err := endianessConverter16(byteOrder)
|
||||
|
|
@ -136,3 +161,27 @@ func determineConverterU16Scale(outType, byteOrder string, scale float64) (field
|
|||
}
|
||||
return nil, fmt.Errorf("invalid output data-type: %s", outType)
|
||||
}
|
||||
|
||||
// F16 - scale
|
||||
func determineConverterF16Scale(outType, byteOrder string, scale float64) (fieldConverterFunc, error) {
|
||||
tohost, err := endianessConverter16(byteOrder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch outType {
|
||||
case "native":
|
||||
return func(b []byte) interface{} {
|
||||
raw := tohost(b)
|
||||
in := float16.Frombits(raw)
|
||||
return in.Float32() * float32(scale)
|
||||
}, nil
|
||||
case "FLOAT64":
|
||||
return func(b []byte) interface{} {
|
||||
raw := tohost(b)
|
||||
in := float16.Frombits(raw)
|
||||
return float64(in.Float32()) * scale
|
||||
}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("invalid output data-type: %s", outType)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue