feat(inputs.modbus): Add 8-bit integer types (#12255)

This commit is contained in:
Sven Rebhan 2022-11-18 16:23:23 +01:00 committed by GitHub
parent d3eec6166a
commit 75aaa8981e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 1575 additions and 21 deletions

View File

@ -91,7 +91,8 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
## |---BA, DCBA - Little Endian
## |---BADC - Mid-Big Endian
## |---CDAB - Mid-Little Endian
## data_type - INT16, UINT16, INT32, UINT32, INT64, UINT64,
## 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)
## FLOAT32, FIXED, UFIXED (fixed-point representation on input)
## scale - the final numeric variable representation
@ -155,7 +156,9 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
## Analog Variables, Input Registers and Holding Registers
## address - address of the register to query. For coil and discrete inputs this is the bit address.
## name *1 - field name
## type *1,2 - type of the modbus field, can be INT16, UINT16, INT32, UINT32, INT64, UINT64 and
## 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)
## 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
@ -280,6 +283,12 @@ 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: `INT8L`, `INT8H`, `UINT8L`, `UINT8H`
These types are used for 8-bit integer values. Select the one that matches your
modbus data source. The `L` and `H` suffix denotes the low- and high byte of
the register respectively.
##### Integers: `INT16`, `UINT16`, `INT32`, `UINT32`, `INT64`, `UINT64`
These types are used for integer input values. Select the one that matches your
@ -412,15 +421,18 @@ metric identified by `measurement`, `slave_id` and `register`.
##### register datatype
The `register` setting specifies the datatype of the modbus register and can be
set to `INT16`, `UINT16`, `INT32`, `UINT32`, `INT64` or `UINT64` for integer
types or `FLOAT32` and `FLOAT64` for IEEE 754 binary representations of floating
point values. Usually the datatype of the register is listed in the datasheet of
your modbus device in relation to the `address` described above.
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.
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
these cases.
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
these cases.
##### scaling

View File

@ -31,7 +31,9 @@ func removeDuplicates(elements []uint16) []uint16 {
func normalizeInputDatatype(dataType string) (string, error) {
switch dataType {
case "INT16", "UINT16", "INT32", "UINT32", "INT64", "UINT64", "FLOAT32", "FLOAT64":
case "INT8L", "INT8H", "UINT8L", "UINT8H",
"INT16", "UINT16", "INT32", "UINT32", "INT64", "UINT64",
"FLOAT32", "FLOAT64":
return dataType, nil
}
return "unknown", fmt.Errorf("unknown input type %q", dataType)

View File

@ -177,7 +177,9 @@ func (c *ConfigurationOriginal) validateFieldDefinitions(fieldDefs []fieldDefini
// search data type
switch item.DataType {
case "UINT16", "INT16", "UINT32", "INT32", "UINT64", "INT64", "FLOAT32-IEEE", "FLOAT64-IEEE", "FLOAT32", "FIXED", "UFIXED":
case "INT8L", "INT8H", "UINT8L", "UINT8H",
"UINT16", "INT16", "UINT32", "INT32", "UINT64", "INT64",
"FLOAT32-IEEE", "FLOAT64-IEEE", "FLOAT32", "FIXED", "UFIXED":
default:
return fmt.Errorf("invalid data type '%s' in '%s' - '%s'", item.DataType, registerType, item.Name)
}

View File

@ -333,9 +333,9 @@ func (c *ConfigurationPerRequest) fieldID(seed maphash.Seed, def requestDefiniti
func (c *ConfigurationPerRequest) determineOutputDatatype(input string) (string, error) {
// Handle our special types
switch input {
case "INT16", "INT32", "INT64":
case "INT8L", "INT8H", "INT16", "INT32", "INT64":
return "INT64", nil
case "UINT16", "UINT32", "UINT64":
case "UINT8L", "UINT8H", "UINT16", "UINT32", "UINT64":
return "UINT64", nil
case "FLOAT32", "FLOAT64":
return "FLOAT64", nil
@ -346,6 +346,8 @@ func (c *ConfigurationPerRequest) determineOutputDatatype(input string) (string,
func (c *ConfigurationPerRequest) determineFieldLength(input string) (uint16, error) {
// Handle our special types
switch input {
case "INT8L", "INT8H", "UINT8L", "UINT8H":
return 1, nil
case "INT16", "UINT16":
return 1, nil
case "INT32", "UINT32", "FLOAT32":

File diff suppressed because it is too large Load Diff

View File

@ -28,7 +28,8 @@
## |---BA, DCBA - Little Endian
## |---BADC - Mid-Big Endian
## |---CDAB - Mid-Little Endian
## data_type - INT16, UINT16, INT32, UINT32, INT64, UINT64,
## 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)
## FLOAT32, FIXED, UFIXED (fixed-point representation on input)
## scale - the final numeric variable representation

View File

@ -42,7 +42,9 @@
## Analog Variables, Input Registers and Holding Registers
## address - address of the register to query. For coil and discrete inputs this is the bit address.
## name *1 - field name
## type *1,2 - type of the modbus field, can be INT16, UINT16, INT32, UINT32, INT64, UINT64 and
## 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)
## 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

View File

@ -11,6 +11,14 @@ func determineConverter(inType, byteOrder, outType string, scale float64) (field
func determineConverterScale(inType, byteOrder, outType string, scale float64) (fieldConverterFunc, error) {
switch inType {
case "INT8L":
return determineConverterI8LScale(outType, byteOrder, scale)
case "INT8H":
return determineConverterI8HScale(outType, byteOrder, scale)
case "UINT8L":
return determineConverterU8LScale(outType, byteOrder, scale)
case "UINT8H":
return determineConverterU8HScale(outType, byteOrder, scale)
case "INT16":
return determineConverterI16Scale(outType, byteOrder, scale)
case "UINT16":
@ -33,6 +41,14 @@ func determineConverterScale(inType, byteOrder, outType string, scale float64) (
func determineConverterNoScale(inType, byteOrder, outType string) (fieldConverterFunc, error) {
switch inType {
case "INT8L":
return determineConverterI8L(outType, byteOrder)
case "INT8H":
return determineConverterI8H(outType, byteOrder)
case "UINT8L":
return determineConverterU8L(outType, byteOrder)
case "UINT8H":
return determineConverterU8H(outType, byteOrder)
case "INT16":
return determineConverterI16(outType, byteOrder)
case "UINT16":

View File

@ -0,0 +1,253 @@
package modbus
import (
"fmt"
)
func endianessIndex8(byteOrder string, low bool) (int, error) {
switch byteOrder {
case "ABCD": // Big endian (Motorola)
if low {
return 1, nil
}
return 0, nil
case "DCBA": // Little endian (Intel)
if low {
return 0, nil
}
return 1, nil
}
return -1, fmt.Errorf("invalid byte-order: %s", byteOrder)
}
// I8 lower byte - no scale
func determineConverterI8L(outType, byteOrder string) (fieldConverterFunc, error) {
idx, err := endianessIndex8(byteOrder, true)
if err != nil {
return nil, err
}
switch outType {
case "native":
return func(b []byte) interface{} {
return int8(b[idx])
}, nil
case "INT64":
return func(b []byte) interface{} {
return int64(int8(b[idx]))
}, nil
case "UINT64":
return func(b []byte) interface{} {
return uint64(int8(b[idx]))
}, nil
case "FLOAT64":
return func(b []byte) interface{} {
return float64(int8(b[idx]))
}, nil
}
return nil, fmt.Errorf("invalid output data-type: %s", outType)
}
// I8 higher byte - no scale
func determineConverterI8H(outType, byteOrder string) (fieldConverterFunc, error) {
idx, err := endianessIndex8(byteOrder, false)
if err != nil {
return nil, err
}
switch outType {
case "native":
return func(b []byte) interface{} {
return int8(b[idx])
}, nil
case "INT64":
return func(b []byte) interface{} {
return int64(int8(b[idx]))
}, nil
case "UINT64":
return func(b []byte) interface{} {
return uint64(int8(b[idx]))
}, nil
case "FLOAT64":
return func(b []byte) interface{} {
return float64(int8(b[idx]))
}, nil
}
return nil, fmt.Errorf("invalid output data-type: %s", outType)
}
// U8 lower byte - no scale
func determineConverterU8L(outType, byteOrder string) (fieldConverterFunc, error) {
idx, err := endianessIndex8(byteOrder, true)
if err != nil {
return nil, err
}
switch outType {
case "native":
return func(b []byte) interface{} {
return b[idx]
}, nil
case "INT64":
return func(b []byte) interface{} {
return int64(b[idx])
}, nil
case "UINT64":
return func(b []byte) interface{} {
return uint64(b[idx])
}, nil
case "FLOAT64":
return func(b []byte) interface{} {
return float64(b[idx])
}, nil
}
return nil, fmt.Errorf("invalid output data-type: %s", outType)
}
// U8 higher byte - no scale
func determineConverterU8H(outType, byteOrder string) (fieldConverterFunc, error) {
idx, err := endianessIndex8(byteOrder, false)
if err != nil {
return nil, err
}
switch outType {
case "native":
return func(b []byte) interface{} {
return b[idx]
}, nil
case "INT64":
return func(b []byte) interface{} {
return int64(b[idx])
}, nil
case "UINT64":
return func(b []byte) interface{} {
return uint64(b[idx])
}, nil
case "FLOAT64":
return func(b []byte) interface{} {
return float64(b[idx])
}, nil
}
return nil, fmt.Errorf("invalid output data-type: %s", outType)
}
// I8 lower byte - scale
func determineConverterI8LScale(outType, byteOrder string, scale float64) (fieldConverterFunc, error) {
idx, err := endianessIndex8(byteOrder, true)
if err != nil {
return nil, err
}
switch outType {
case "native":
return func(b []byte) interface{} {
in := int8(b[idx])
return int8(float64(in) * scale)
}, nil
case "INT64":
return func(b []byte) interface{} {
in := int8(b[idx])
return int64(float64(in) * scale)
}, nil
case "UINT64":
return func(b []byte) interface{} {
in := int8(b[idx])
return uint64(float64(in) * scale)
}, nil
case "FLOAT64":
return func(b []byte) interface{} {
in := int8(b[idx])
return float64(in) * scale
}, nil
}
return nil, fmt.Errorf("invalid output data-type: %s", outType)
}
// I8 higher byte - scale
func determineConverterI8HScale(outType, byteOrder string, scale float64) (fieldConverterFunc, error) {
idx, err := endianessIndex8(byteOrder, false)
if err != nil {
return nil, err
}
switch outType {
case "native":
return func(b []byte) interface{} {
in := int8(b[idx])
return int8(float64(in) * scale)
}, nil
case "INT64":
return func(b []byte) interface{} {
in := int8(b[idx])
return int64(float64(in) * scale)
}, nil
case "UINT64":
return func(b []byte) interface{} {
in := int8(b[idx])
return uint64(float64(in) * scale)
}, nil
case "FLOAT64":
return func(b []byte) interface{} {
in := int8(b[idx])
return float64(in) * scale
}, nil
}
return nil, fmt.Errorf("invalid output data-type: %s", outType)
}
// U8 lower byte - scale
func determineConverterU8LScale(outType, byteOrder string, scale float64) (fieldConverterFunc, error) {
idx, err := endianessIndex8(byteOrder, true)
if err != nil {
return nil, err
}
switch outType {
case "native":
return func(b []byte) interface{} {
return uint8(float64(b[idx]) * scale)
}, nil
case "INT64":
return func(b []byte) interface{} {
return int64(float64(b[idx]) * scale)
}, nil
case "UINT64":
return func(b []byte) interface{} {
return uint64(float64(b[idx]) * scale)
}, nil
case "FLOAT64":
return func(b []byte) interface{} {
return float64(b[idx]) * scale
}, nil
}
return nil, fmt.Errorf("invalid output data-type: %s", outType)
}
// U8 higher byte - scale
func determineConverterU8HScale(outType, byteOrder string, scale float64) (fieldConverterFunc, error) {
idx, err := endianessIndex8(byteOrder, false)
if err != nil {
return nil, err
}
switch outType {
case "native":
return func(b []byte) interface{} {
return uint8(float64(b[idx]) * scale)
}, nil
case "INT64":
return func(b []byte) interface{} {
return int64(float64(b[idx]) * scale)
}, nil
case "UINT64":
return func(b []byte) interface{} {
return uint64(float64(b[idx]) * scale)
}, nil
case "FLOAT64":
return func(b []byte) interface{} {
return float64(b[idx]) * scale
}, nil
}
return nil, fmt.Errorf("invalid output data-type: %s", outType)
}