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 ## |---BA, DCBA - Little Endian
## |---BADC - Mid-Big Endian ## |---BADC - Mid-Big Endian
## |---CDAB - Mid-Little 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-IEEE, FLOAT64-IEEE (the IEEE 754 binary representation)
## FLOAT32, FIXED, UFIXED (fixed-point representation on input) ## FLOAT32, FIXED, UFIXED (fixed-point representation on input)
## scale - the final numeric variable representation ## 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 ## Analog Variables, Input Registers and Holding Registers
## address - address of the register to query. For coil and discrete inputs this is the bit address. ## address - address of the register to query. For coil and discrete inputs this is the bit address.
## name *1 - field name ## 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) ## FLOAT32, FLOAT64 (IEEE 754 binary representation)
## scale *1,2 - (optional) factor to scale the variable with ## 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 ## 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 for all supported input types. The mapping from the input type to the output
type is fixed and cannot be configured. 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` ##### Integers: `INT16`, `UINT16`, `INT32`, `UINT32`, `INT64`, `UINT64`
These types are used for integer input values. Select the one that matches your 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 ##### register datatype
The `register` setting specifies the datatype of the modbus register and can be The `type` setting specifies the datatype of the modbus register and can be
set to `INT16`, `UINT16`, `INT32`, `UINT32`, `INT64` or `UINT64` for integer set to `INT8L`, `INT8H`, `UINT8L`, `UINT8H` where `L` is the lower byte of the
types or `FLOAT32` and `FLOAT64` for IEEE 754 binary representations of floating register and `H` is the higher byte.
point values. Usually the datatype of the register is listed in the datasheet of Furthermore, the types `INT16`, `UINT16`, `INT32`, `UINT32`, `INT64` or `UINT64`
your modbus device in relation to the `address` described above. 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 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 `register` type is a bit-type (`coil` or `discrete`) and can be omitted in
these cases. these cases.
##### scaling ##### scaling

View File

@ -31,7 +31,9 @@ func removeDuplicates(elements []uint16) []uint16 {
func normalizeInputDatatype(dataType string) (string, error) { func normalizeInputDatatype(dataType string) (string, error) {
switch dataType { 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 dataType, nil
} }
return "unknown", fmt.Errorf("unknown input type %q", dataType) return "unknown", fmt.Errorf("unknown input type %q", dataType)

View File

@ -177,7 +177,9 @@ func (c *ConfigurationOriginal) validateFieldDefinitions(fieldDefs []fieldDefini
// search data type // search data type
switch item.DataType { 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: default:
return fmt.Errorf("invalid data type '%s' in '%s' - '%s'", item.DataType, registerType, item.Name) 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) { func (c *ConfigurationPerRequest) determineOutputDatatype(input string) (string, error) {
// Handle our special types // Handle our special types
switch input { switch input {
case "INT16", "INT32", "INT64": case "INT8L", "INT8H", "INT16", "INT32", "INT64":
return "INT64", nil return "INT64", nil
case "UINT16", "UINT32", "UINT64": case "UINT8L", "UINT8H", "UINT16", "UINT32", "UINT64":
return "UINT64", nil return "UINT64", nil
case "FLOAT32", "FLOAT64": case "FLOAT32", "FLOAT64":
return "FLOAT64", nil return "FLOAT64", nil
@ -346,6 +346,8 @@ func (c *ConfigurationPerRequest) determineOutputDatatype(input string) (string,
func (c *ConfigurationPerRequest) determineFieldLength(input string) (uint16, error) { func (c *ConfigurationPerRequest) determineFieldLength(input string) (uint16, error) {
// Handle our special types // Handle our special types
switch input { switch input {
case "INT8L", "INT8H", "UINT8L", "UINT8H":
return 1, nil
case "INT16", "UINT16": case "INT16", "UINT16":
return 1, nil return 1, nil
case "INT32", "UINT32", "FLOAT32": case "INT32", "UINT32", "FLOAT32":

File diff suppressed because it is too large Load Diff

View File

@ -28,7 +28,8 @@
## |---BA, DCBA - Little Endian ## |---BA, DCBA - Little Endian
## |---BADC - Mid-Big Endian ## |---BADC - Mid-Big Endian
## |---CDAB - Mid-Little 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-IEEE, FLOAT64-IEEE (the IEEE 754 binary representation)
## FLOAT32, FIXED, UFIXED (fixed-point representation on input) ## FLOAT32, FIXED, UFIXED (fixed-point representation on input)
## scale - the final numeric variable representation ## scale - the final numeric variable representation

View File

@ -42,7 +42,9 @@
## Analog Variables, Input Registers and Holding Registers ## Analog Variables, Input Registers and Holding Registers
## address - address of the register to query. For coil and discrete inputs this is the bit address. ## address - address of the register to query. For coil and discrete inputs this is the bit address.
## name *1 - field name ## 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) ## FLOAT32, FLOAT64 (IEEE 754 binary representation)
## scale *1,2 - (optional) factor to scale the variable with ## 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 ## 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) { func determineConverterScale(inType, byteOrder, outType string, scale float64) (fieldConverterFunc, error) {
switch inType { 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": case "INT16":
return determineConverterI16Scale(outType, byteOrder, scale) return determineConverterI16Scale(outType, byteOrder, scale)
case "UINT16": case "UINT16":
@ -33,6 +41,14 @@ func determineConverterScale(inType, byteOrder, outType string, scale float64) (
func determineConverterNoScale(inType, byteOrder, outType string) (fieldConverterFunc, error) { func determineConverterNoScale(inType, byteOrder, outType string) (fieldConverterFunc, error) {
switch inType { 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": case "INT16":
return determineConverterI16(outType, byteOrder) return determineConverterI16(outType, byteOrder)
case "UINT16": 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)
}