From 75aaa8981e9d2c886394ec5f463903615bc04ceb Mon Sep 17 00:00:00 2001 From: Sven Rebhan <36194019+srebhan@users.noreply.github.com> Date: Fri, 18 Nov 2022 16:23:23 +0100 Subject: [PATCH] feat(inputs.modbus): Add 8-bit integer types (#12255) --- plugins/inputs/modbus/README.md | 32 +- plugins/inputs/modbus/configuration.go | 4 +- .../inputs/modbus/configuration_register.go | 4 +- .../inputs/modbus/configuration_request.go | 6 +- plugins/inputs/modbus/modbus_test.go | 1274 ++++++++++++++++- plugins/inputs/modbus/sample_register.conf | 3 +- plugins/inputs/modbus/sample_request.conf | 4 +- plugins/inputs/modbus/type_conversions.go | 16 + plugins/inputs/modbus/type_conversions8.go | 253 ++++ 9 files changed, 1575 insertions(+), 21 deletions(-) create mode 100644 plugins/inputs/modbus/type_conversions8.go diff --git a/plugins/inputs/modbus/README.md b/plugins/inputs/modbus/README.md index dd0bb9d59..7747081bd 100644 --- a/plugins/inputs/modbus/README.md +++ b/plugins/inputs/modbus/README.md @@ -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 diff --git a/plugins/inputs/modbus/configuration.go b/plugins/inputs/modbus/configuration.go index b15b51e49..c39729457 100644 --- a/plugins/inputs/modbus/configuration.go +++ b/plugins/inputs/modbus/configuration.go @@ -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) diff --git a/plugins/inputs/modbus/configuration_register.go b/plugins/inputs/modbus/configuration_register.go index 05c8294db..36f7a2806 100644 --- a/plugins/inputs/modbus/configuration_register.go +++ b/plugins/inputs/modbus/configuration_register.go @@ -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) } diff --git a/plugins/inputs/modbus/configuration_request.go b/plugins/inputs/modbus/configuration_request.go index e917d5f97..30c982605 100644 --- a/plugins/inputs/modbus/configuration_request.go +++ b/plugins/inputs/modbus/configuration_request.go @@ -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": diff --git a/plugins/inputs/modbus/modbus_test.go b/plugins/inputs/modbus/modbus_test.go index ea09f075c..73f367670 100644 --- a/plugins/inputs/modbus/modbus_test.go +++ b/plugins/inputs/modbus/modbus_test.go @@ -300,18 +300,138 @@ func TestHoldingRegisters(t *testing.T) { read: float64(18441921395.520346504), }, { - name: "register10_ab_uint16", + name: "register20_uint16", address: []uint16{10}, quantity: 1, byteOrder: "AB", + dataType: "UINT8L", + scale: 1, + write: []byte{0x18, 0x0D}, + read: uint8(13), + }, + { + name: "register20_uint16-scale_.1", + address: []uint16{10}, + quantity: 1, + byteOrder: "AB", + dataType: "UINT8L", + scale: .1, + write: []byte{0x18, 0x0D}, + read: uint8(1), + }, + { + name: "register20_uint16_scale_10", + address: []uint16{10}, + quantity: 1, + byteOrder: "AB", + dataType: "UINT8L", + scale: 10, + write: []byte{0x18, 0x0D}, + read: uint8(130), + }, + { + name: "register11_uint8H", + address: []uint16{11}, + quantity: 1, + byteOrder: "AB", + dataType: "UINT8H", + scale: 1, + write: []byte{0x18, 0x0D}, + read: uint8(24), + }, + { + name: "register11_uint8L-scale_.1", + address: []uint16{11}, + quantity: 1, + byteOrder: "AB", + dataType: "UINT8H", + scale: .1, + write: []byte{0x18, 0x0D}, + read: uint8(2), + }, + { + name: "register11_uint8L_scale_10", + address: []uint16{11}, + quantity: 1, + byteOrder: "AB", + dataType: "UINT8H", + scale: 10, + write: []byte{0x18, 0x0D}, + read: uint8(240), + }, + { + name: "register12_int8L", + address: []uint16{12}, + quantity: 1, + byteOrder: "AB", + dataType: "INT8L", + scale: 1, + write: []byte{0x98, 0x8D}, + read: int8(-115), + }, + { + name: "register12_int8L-scale_.1", + address: []uint16{12}, + quantity: 1, + byteOrder: "AB", + dataType: "INT8L", + scale: .1, + write: []byte{0x98, 0x8D}, + read: int8(-11), + }, + { + name: "register12_int8L_scale_10", + address: []uint16{12}, + quantity: 1, + byteOrder: "AB", + dataType: "INT8L", + scale: 10, + write: []byte{0x98, 0xF8}, + read: int8(-80), + }, + { + name: "register13_int8H", + address: []uint16{13}, + quantity: 1, + byteOrder: "AB", + dataType: "INT8H", + scale: 1, + write: []byte{0x98, 0x8D}, + read: int8(-104), + }, + { + name: "register13_int8H-scale_.1", + address: []uint16{13}, + quantity: 1, + byteOrder: "AB", + dataType: "INT8H", + scale: .1, + write: []byte{0x98, 0x8D}, + read: int8(-10), + }, + { + name: "register13_int8H_scale_10", + address: []uint16{13}, + quantity: 1, + byteOrder: "AB", + dataType: "INT8H", + scale: 10, + write: []byte{0xFD, 0x8D}, + read: int8(-30), + }, + { + name: "register15_ab_uint16", + address: []uint16{15}, + quantity: 1, + byteOrder: "AB", dataType: "UINT16", scale: 1, write: []byte{0xAB, 0xCD}, read: uint16(43981), }, { - name: "register10_ab_uint16-scale_.1", - address: []uint16{10}, + name: "register15_ab_uint16-scale_.1", + address: []uint16{15}, quantity: 1, byteOrder: "AB", dataType: "UINT16", @@ -320,8 +440,8 @@ func TestHoldingRegisters(t *testing.T) { read: uint16(4398), }, { - name: "register10_ab_uint16_scale_10", - address: []uint16{10}, + name: "register15_ab_uint16_scale_10", + address: []uint16{15}, quantity: 1, byteOrder: "AB", dataType: "UINT16", @@ -685,6 +805,1150 @@ func TestHoldingRegisters(t *testing.T) { } } +func TestRequestTypesHoldingABCD(t *testing.T) { + byteOrder := "ABCD" + tests := []struct { + name string + address uint16 + byteOrder string + dataTypeIn string + dataTypeOut string + scale float64 + write []byte + read interface{} + }{ + { + name: "register10_uint8L", + address: 10, + dataTypeIn: "UINT8L", + write: []byte{0x18, 0x0d}, + read: uint8(13), + }, + { + name: "register10_uint8L-scale_.1", + address: 10, + dataTypeIn: "UINT8L", + scale: .1, + write: []byte{0x18, 0x0d}, + read: float64(1.3), + }, + { + name: "register10_uint8L_scale_10", + address: 10, + dataTypeIn: "UINT8L", + scale: 10, + write: []byte{0x18, 0x0d}, + read: float64(130), + }, + { + name: "register10_uint8L_uint64", + address: 10, + dataTypeIn: "UINT8L", + dataTypeOut: "UINT64", + write: []byte{0x18, 0x0d}, + read: uint64(13), + }, + { + name: "register10_uint8L_int64", + address: 10, + dataTypeIn: "UINT8L", + dataTypeOut: "INT64", + write: []byte{0x18, 0x0d}, + read: int64(13), + }, + { + name: "register10_uint8L_float64", + address: 10, + dataTypeIn: "UINT8L", + dataTypeOut: "FLOAT64", + write: []byte{0x18, 0x0d}, + read: float64(13), + }, + { + name: "register10_uint8L_float64_scale", + address: 10, + dataTypeIn: "UINT8L", + scale: 1.0, + write: []byte{0x18, 0x0d}, + read: float64(13), + }, + { + name: "register15_int8L", + address: 15, + dataTypeIn: "UINT8L", + write: []byte{0x18, 0x0d}, + read: uint8(13), + }, + { + name: "register15_int8L-scale_.1", + address: 15, + dataTypeIn: "INT8L", + scale: .1, + write: []byte{0x18, 0x0d}, + read: float64(1.3), + }, + { + name: "register15_int8L_scale_10", + address: 15, + dataTypeIn: "INT8L", + scale: 10, + write: []byte{0x18, 0x0d}, + read: float64(130), + }, + { + name: "register15_int8L_uint64", + address: 15, + dataTypeIn: "INT8L", + dataTypeOut: "UINT64", + write: []byte{0x18, 0x0d}, + read: uint64(13), + }, + { + name: "register15_int8L_int64", + address: 15, + dataTypeIn: "INT8L", + dataTypeOut: "INT64", + write: []byte{0x18, 0x0d}, + read: int64(13), + }, + { + name: "register15_int8L_float64", + address: 15, + dataTypeIn: "INT8L", + dataTypeOut: "FLOAT64", + write: []byte{0x18, 0x0d}, + read: float64(13), + }, + { + name: "register15_int8L_float64_scale", + address: 15, + dataTypeIn: "INT8L", + scale: 1.0, + write: []byte{0x18, 0x0d}, + read: float64(13), + }, + { + name: "register20_uint16", + address: 20, + dataTypeIn: "UINT16", + write: []byte{0x08, 0x98}, + read: uint16(2200), + }, + { + name: "register20_uint16-scale_.1", + address: 20, + dataTypeIn: "UINT16", + scale: .1, + write: []byte{0x08, 0x98}, + read: float64(220), + }, + { + name: "register20_uint16_scale_10", + address: 20, + dataTypeIn: "UINT16", + scale: 10, + write: []byte{0x08, 0x98}, + read: float64(22000), + }, + { + name: "register20_uint16_uint64", + address: 20, + dataTypeIn: "UINT16", + dataTypeOut: "UINT64", + write: []byte{0x08, 0x98}, + read: uint64(2200), + }, + { + name: "register20_uint16_int64", + address: 20, + dataTypeIn: "UINT16", + dataTypeOut: "INT64", + write: []byte{0x08, 0x98}, + read: int64(2200), + }, + { + name: "register20_uint16_float64", + address: 20, + dataTypeIn: "UINT16", + dataTypeOut: "FLOAT64", + write: []byte{0x08, 0x98}, + read: float64(2200), + }, + { + name: "register20_uint16_float64_scale", + address: 20, + dataTypeIn: "UINT16", + scale: 1.0, + write: []byte{0x08, 0x98}, + read: float64(2200), + }, + { + name: "register30_int16", + address: 30, + dataTypeIn: "INT16", + write: []byte{0xf8, 0x98}, + read: int16(-1896), + }, + { + name: "register30_int16-scale_.1", + address: 30, + dataTypeIn: "INT16", + scale: .1, + write: []byte{0xf8, 0x98}, + read: float64(-189.60000000000002), + }, + { + name: "register30_int16_scale_10", + address: 30, + dataTypeIn: "INT16", + scale: 10, + write: []byte{0xf8, 0x98}, + read: float64(-18960), + }, + { + name: "register30_int16_uint64", + address: 30, + dataTypeIn: "INT16", + dataTypeOut: "UINT64", + write: []byte{0xf8, 0x98}, + read: uint64(18446744073709549720), + }, + { + name: "register30_int16_int64", + address: 30, + dataTypeIn: "INT16", + dataTypeOut: "INT64", + write: []byte{0xf8, 0x98}, + read: int64(-1896), + }, + { + name: "register30_int16_float64", + address: 30, + dataTypeIn: "INT16", + dataTypeOut: "FLOAT64", + write: []byte{0xf8, 0x98}, + read: float64(-1896), + }, + { + name: "register30_int16_float64_scale", + address: 30, + dataTypeIn: "INT16", + scale: 1.0, + write: []byte{0xf8, 0x98}, + read: float64(-1896), + }, + { + name: "register40_uint32", + address: 40, + dataTypeIn: "UINT32", + write: []byte{0x0a, 0x0b, 0x0c, 0x0d}, + read: uint32(168496141), + }, + { + name: "register40_uint32-scale_.1", + address: 40, + dataTypeIn: "UINT32", + scale: .1, + write: []byte{0x0a, 0x0b, 0x0c, 0x0d}, + read: float64(16849614.1), + }, + { + name: "register40_uint32_scale_10", + address: 40, + dataTypeIn: "UINT32", + scale: 10, + write: []byte{0x0a, 0x0b, 0x0c, 0x0d}, + read: float64(1684961410), + }, + { + name: "register40_uint32_uint64", + address: 40, + dataTypeIn: "UINT32", + dataTypeOut: "UINT64", + write: []byte{0x0a, 0x0b, 0x0c, 0x0d}, + read: uint64(168496141), + }, + { + name: "register40_uint32_int64", + address: 40, + dataTypeIn: "UINT32", + dataTypeOut: "INT64", + write: []byte{0x0a, 0x0b, 0x0c, 0x0d}, + read: int64(168496141), + }, + { + name: "register40_uint32_float64", + address: 40, + dataTypeIn: "UINT32", + dataTypeOut: "FLOAT64", + write: []byte{0x0a, 0x0b, 0x0c, 0x0d}, + read: float64(168496141), + }, + { + name: "register40_uint32_float64_scale", + address: 40, + dataTypeIn: "UINT32", + scale: 1.0, + write: []byte{0x0a, 0x0b, 0x0c, 0x0d}, + read: float64(168496141), + }, + { + name: "register50_int32", + address: 50, + dataTypeIn: "INT32", + write: []byte{0xfa, 0x0b, 0x0c, 0x0d}, + read: int32(-99939315), + }, + { + name: "register50_int32-scale_.1", + address: 50, + dataTypeIn: "INT32", + scale: .1, + write: []byte{0xfa, 0x0b, 0x0c, 0x0d}, + read: float64(-9993931.5), + }, + { + name: "register50_int32_scale_10", + address: 50, + dataTypeIn: "INT32", + scale: 10, + write: []byte{0xfa, 0x0b, 0x0c, 0x0d}, + read: float64(-999393150), + }, + { + name: "register50_int32_uint64", + address: 50, + dataTypeIn: "INT32", + dataTypeOut: "UINT64", + write: []byte{0xfa, 0x0b, 0x0c, 0x0d}, + read: uint64(18446744073609612301), + }, + { + name: "register50_int32_int64", + address: 50, + dataTypeIn: "INT32", + dataTypeOut: "INT64", + write: []byte{0xfa, 0x0b, 0x0c, 0x0d}, + read: int64(-99939315), + }, + { + name: "register50_int32_float64", + address: 50, + dataTypeIn: "INT32", + dataTypeOut: "FLOAT64", + write: []byte{0xfa, 0x0b, 0x0c, 0x0d}, + read: float64(-99939315), + }, + { + name: "register50_int32_float64_scale", + address: 50, + dataTypeIn: "INT32", + scale: 1.0, + write: []byte{0xfa, 0x0b, 0x0c, 0x0d}, + read: float64(-99939315), + }, + { + name: "register60_uint64", + address: 60, + dataTypeIn: "UINT64", + write: []byte{0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x01, 0x02}, + read: uint64(723685415333069058), + }, + { + name: "register60_uint64-scale_.1", + address: 60, + dataTypeIn: "UINT64", + scale: .1, + write: []byte{0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x01, 0x02}, + read: float64(72368541533306905.8), + }, + { + name: "register60_uint64_scale_10", + address: 60, + dataTypeIn: "UINT64", + scale: 10, + write: []byte{0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x01, 0x02}, + read: float64(7236854153330690000), // quantization error + }, + { + name: "register60_uint64_int64", + address: 60, + dataTypeIn: "UINT64", + dataTypeOut: "INT64", + write: []byte{0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x01, 0x02}, + read: int64(723685415333069058), + }, + { + name: "register60_uint64_float64", + address: 60, + dataTypeIn: "UINT64", + dataTypeOut: "FLOAT64", + write: []byte{0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x01, 0x02}, + read: float64(723685415333069058), + }, + { + name: "register60_uint64_float64_scale", + address: 60, + dataTypeIn: "UINT64", + scale: 1.0, + write: []byte{0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x01, 0x02}, + read: float64(723685415333069058), + }, + { + name: "register70_int64", + address: 70, + dataTypeIn: "INT64", + write: []byte{0xfa, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x01, 0x02}, + read: int64(-429236089273777918), + }, + { + name: "register70_int64-scale_.1", + address: 70, + dataTypeIn: "INT64", + scale: .1, + write: []byte{0xfa, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x01, 0x02}, + read: float64(-42923608927377791.8), + }, + { + name: "register70_int64_scale_10", + address: 70, + dataTypeIn: "INT64", + scale: 10, + write: []byte{0xfa, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x01, 0x02}, + read: float64(-4292360892737779180), + }, + { + name: "register70_int64_uint64", + address: 70, + dataTypeIn: "INT64", + dataTypeOut: "UINT64", + write: []byte{0xfa, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x01, 0x02}, + read: uint64(18017507984435773698), + }, + { + name: "register70_int64_float64", + address: 70, + dataTypeIn: "INT64", + dataTypeOut: "FLOAT64", + write: []byte{0xfa, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x01, 0x02}, + read: float64(-429236089273777918), + }, + { + name: "register70_int64_float64_scale", + address: 70, + dataTypeIn: "INT64", + scale: 1.0, + write: []byte{0xfa, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x01, 0x02}, + read: float64(-429236089273777918), + }, + { + name: "register80_float32", + address: 80, + dataTypeIn: "FLOAT32", + write: []byte{0x40, 0x49, 0x0f, 0xdb}, + read: float32(3.1415927410125732421875), + }, + { + name: "register80_float32-scale_.1", + address: 80, + dataTypeIn: "FLOAT32", + scale: .1, + write: []byte{0x40, 0x49, 0x0f, 0xdb}, + read: float64(0.31415927410125732421875), + }, + { + name: "register80_float32_scale_10", + address: 80, + dataTypeIn: "FLOAT32", + scale: 10, + write: []byte{0x40, 0x49, 0x0f, 0xdb}, + read: float64(31.415927410125732421875), + }, + { + name: "register80_float32_float64", + address: 80, + dataTypeIn: "FLOAT32", + dataTypeOut: "FLOAT64", + write: []byte{0x40, 0x49, 0x0f, 0xdb}, + read: float64(3.1415927410125732421875), + }, + { + name: "register80_float32_float64_scale", + address: 80, + dataTypeIn: "FLOAT32", + scale: 1.0, + write: []byte{0x40, 0x49, 0x0f, 0xdb}, + read: float64(3.1415927410125732421875), + }, + { + name: "register90_float64", + address: 90, + dataTypeIn: "FLOAT64", + write: []byte{0x40, 0x09, 0x21, 0xfb, 0x54, 0x44, 0x2e, 0xea}, + read: float64(3.14159265359000006156975359772), + }, + { + name: "register90_float64-scale_.1", + address: 90, + dataTypeIn: "FLOAT64", + scale: .1, + write: []byte{0x40, 0x09, 0x21, 0xfb, 0x54, 0x44, 0x2e, 0xea}, + read: float64(0.314159265359000006156975359772), + }, + { + name: "register90_float64_scale_10", + address: 90, + dataTypeIn: "FLOAT64", + scale: 10, + write: []byte{0x40, 0x09, 0x21, 0xfb, 0x54, 0x44, 0x2e, 0xea}, + read: float64(31.4159265359000006156975359772), + }, + { + name: "register90_float64_float64_scale", + address: 90, + dataTypeIn: "FLOAT64", + scale: 1.0, + write: []byte{0x40, 0x09, 0x21, 0xfb, 0x54, 0x44, 0x2e, 0xea}, + read: float64(3.14159265359000006156975359772), + }, + } + + serv := mbserver.NewServer() + require.NoError(t, serv.ListenTCP("localhost:1502")) + defer serv.Close() + + handler := mb.NewTCPClientHandler("localhost:1502") + require.NoError(t, handler.Connect()) + defer handler.Close() + client := mb.NewClient(handler) + + for _, hrt := range tests { + t.Run(hrt.name, func(t *testing.T) { + quantity := uint16(len(hrt.write) / 2) + _, err := client.WriteMultipleRegisters(hrt.address, quantity, hrt.write) + require.NoError(t, err) + + modbus := Modbus{ + Name: "TestRequestTypesHoldingABCD", + Controller: "tcp://localhost:1502", + ConfigurationType: "request", + Log: testutil.Logger{}, + } + modbus.Requests = []requestDefinition{ + { + SlaveID: 1, + ByteOrder: byteOrder, + RegisterType: "holding", + Fields: []requestFieldDefinition{ + { + Name: hrt.name, + InputType: hrt.dataTypeIn, + OutputType: hrt.dataTypeOut, + Scale: hrt.scale, + Address: hrt.address, + }, + }, + }, + } + + expected := []telegraf.Metric{ + testutil.MustMetric( + "modbus", + map[string]string{ + "type": cHoldingRegisters, + "slave_id": "1", + "name": modbus.Name, + }, + map[string]interface{}{hrt.name: hrt.read}, + time.Unix(0, 0), + ), + } + + var acc testutil.Accumulator + require.NoError(t, modbus.Init()) + require.NotEmpty(t, modbus.requests) + require.NoError(t, modbus.Gather(&acc)) + acc.Wait(len(expected)) + + testutil.RequireMetricsEqual(t, expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime()) + }) + } +} + +func TestRequestTypesHoldingDCBA(t *testing.T) { + byteOrder := "DCBA" + tests := []struct { + name string + address uint16 + byteOrder string + dataTypeIn string + dataTypeOut string + scale float64 + write []byte + read interface{} + }{ + { + name: "register10_uint8L", + address: 10, + dataTypeIn: "UINT8L", + write: []byte{0x18, 0x0d}, + read: uint8(13), + }, + { + name: "register10_uint8L-scale_.1", + address: 10, + dataTypeIn: "UINT8L", + scale: .1, + write: []byte{0x18, 0x0d}, + read: float64(1.3), + }, + { + name: "register10_uint8L_scale_10", + address: 10, + dataTypeIn: "UINT8L", + scale: 10, + write: []byte{0x18, 0x0d}, + read: float64(130), + }, + { + name: "register10_uint8L_uint64", + address: 10, + dataTypeIn: "UINT8L", + dataTypeOut: "UINT64", + write: []byte{0x18, 0x0d}, + read: uint64(13), + }, + { + name: "register10_uint8L_int64", + address: 10, + dataTypeIn: "UINT8L", + dataTypeOut: "INT64", + write: []byte{0x18, 0x0d}, + read: int64(13), + }, + { + name: "register10_uint8L_float64", + address: 10, + dataTypeIn: "UINT8L", + dataTypeOut: "FLOAT64", + write: []byte{0x18, 0x0d}, + read: float64(13), + }, + { + name: "register10_uint8L_float64_scale", + address: 10, + dataTypeIn: "UINT8L", + scale: 1.0, + write: []byte{0x18, 0x0d}, + read: float64(13), + }, + { + name: "register15_int8L", + address: 15, + dataTypeIn: "UINT8L", + write: []byte{0x18, 0x0d}, + read: uint8(13), + }, + { + name: "register15_int8L-scale_.1", + address: 15, + dataTypeIn: "INT8L", + scale: .1, + write: []byte{0x18, 0x0d}, + read: float64(1.3), + }, + { + name: "register15_int8L_scale_10", + address: 15, + dataTypeIn: "INT8L", + scale: 10, + write: []byte{0x18, 0x0d}, + read: float64(130), + }, + { + name: "register15_int8L_uint64", + address: 15, + dataTypeIn: "INT8L", + dataTypeOut: "UINT64", + write: []byte{0x18, 0x0d}, + read: uint64(13), + }, + { + name: "register15_int8L_int64", + address: 15, + dataTypeIn: "INT8L", + dataTypeOut: "INT64", + write: []byte{0x18, 0x0d}, + read: int64(13), + }, + { + name: "register15_int8L_float64", + address: 15, + dataTypeIn: "INT8L", + dataTypeOut: "FLOAT64", + write: []byte{0x18, 0x0d}, + read: float64(13), + }, + { + name: "register15_int8L_float64_scale", + address: 15, + dataTypeIn: "INT8L", + scale: 1.0, + write: []byte{0x18, 0x0d}, + read: float64(13), + }, + { + name: "register20_uint16", + address: 20, + dataTypeIn: "UINT16", + write: []byte{0x08, 0x98}, + read: uint16(2200), + }, + { + name: "register20_uint16-scale_.1", + address: 20, + dataTypeIn: "UINT16", + scale: .1, + write: []byte{0x08, 0x98}, + read: float64(220), + }, + { + name: "register20_uint16_scale_10", + address: 20, + dataTypeIn: "UINT16", + scale: 10, + write: []byte{0x08, 0x98}, + read: float64(22000), + }, + { + name: "register20_uint16_uint64", + address: 20, + dataTypeIn: "UINT16", + dataTypeOut: "UINT64", + write: []byte{0x08, 0x98}, + read: uint64(2200), + }, + { + name: "register20_uint16_int64", + address: 20, + dataTypeIn: "UINT16", + dataTypeOut: "INT64", + write: []byte{0x08, 0x98}, + read: int64(2200), + }, + { + name: "register20_uint16_float64", + address: 20, + dataTypeIn: "UINT16", + dataTypeOut: "FLOAT64", + write: []byte{0x08, 0x98}, + read: float64(2200), + }, + { + name: "register20_uint16_float64_scale", + address: 20, + dataTypeIn: "UINT16", + scale: 1.0, + write: []byte{0x08, 0x98}, + read: float64(2200), + }, + { + name: "register30_int16", + address: 30, + dataTypeIn: "INT16", + write: []byte{0xf8, 0x98}, + read: int16(-1896), + }, + { + name: "register30_int16-scale_.1", + address: 30, + dataTypeIn: "INT16", + scale: .1, + write: []byte{0xf8, 0x98}, + read: float64(-189.60000000000002), + }, + { + name: "register30_int16_scale_10", + address: 30, + dataTypeIn: "INT16", + scale: 10, + write: []byte{0xf8, 0x98}, + read: float64(-18960), + }, + { + name: "register30_int16_uint64", + address: 30, + dataTypeIn: "INT16", + dataTypeOut: "UINT64", + write: []byte{0xf8, 0x98}, + read: uint64(18446744073709549720), + }, + { + name: "register30_int16_int64", + address: 30, + dataTypeIn: "INT16", + dataTypeOut: "INT64", + write: []byte{0xf8, 0x98}, + read: int64(-1896), + }, + { + name: "register30_int16_float64", + address: 30, + dataTypeIn: "INT16", + dataTypeOut: "FLOAT64", + write: []byte{0xf8, 0x98}, + read: float64(-1896), + }, + { + name: "register30_int16_float64_scale", + address: 30, + dataTypeIn: "INT16", + scale: 1.0, + write: []byte{0xf8, 0x98}, + read: float64(-1896), + }, + { + name: "register40_uint32", + address: 40, + dataTypeIn: "UINT32", + write: []byte{0x0a, 0x0b, 0x0c, 0x0d}, + read: uint32(168496141), + }, + { + name: "register40_uint32-scale_.1", + address: 40, + dataTypeIn: "UINT32", + scale: .1, + write: []byte{0x0a, 0x0b, 0x0c, 0x0d}, + read: float64(16849614.1), + }, + { + name: "register40_uint32_scale_10", + address: 40, + dataTypeIn: "UINT32", + scale: 10, + write: []byte{0x0a, 0x0b, 0x0c, 0x0d}, + read: float64(1684961410), + }, + { + name: "register40_uint32_uint64", + address: 40, + dataTypeIn: "UINT32", + dataTypeOut: "UINT64", + write: []byte{0x0a, 0x0b, 0x0c, 0x0d}, + read: uint64(168496141), + }, + { + name: "register40_uint32_int64", + address: 40, + dataTypeIn: "UINT32", + dataTypeOut: "INT64", + write: []byte{0x0a, 0x0b, 0x0c, 0x0d}, + read: int64(168496141), + }, + { + name: "register40_uint32_float64", + address: 40, + dataTypeIn: "UINT32", + dataTypeOut: "FLOAT64", + write: []byte{0x0a, 0x0b, 0x0c, 0x0d}, + read: float64(168496141), + }, + { + name: "register40_uint32_float64_scale", + address: 40, + dataTypeIn: "UINT32", + scale: 1.0, + write: []byte{0x0a, 0x0b, 0x0c, 0x0d}, + read: float64(168496141), + }, + { + name: "register50_int32", + address: 50, + dataTypeIn: "INT32", + write: []byte{0xfa, 0x0b, 0x0c, 0x0d}, + read: int32(-99939315), + }, + { + name: "register50_int32-scale_.1", + address: 50, + dataTypeIn: "INT32", + scale: .1, + write: []byte{0xfa, 0x0b, 0x0c, 0x0d}, + read: float64(-9993931.5), + }, + { + name: "register50_int32_scale_10", + address: 50, + dataTypeIn: "INT32", + scale: 10, + write: []byte{0xfa, 0x0b, 0x0c, 0x0d}, + read: float64(-999393150), + }, + { + name: "register50_int32_uint64", + address: 50, + dataTypeIn: "INT32", + dataTypeOut: "UINT64", + write: []byte{0xfa, 0x0b, 0x0c, 0x0d}, + read: uint64(18446744073609612301), + }, + { + name: "register50_int32_int64", + address: 50, + dataTypeIn: "INT32", + dataTypeOut: "INT64", + write: []byte{0xfa, 0x0b, 0x0c, 0x0d}, + read: int64(-99939315), + }, + { + name: "register50_int32_float64", + address: 50, + dataTypeIn: "INT32", + dataTypeOut: "FLOAT64", + write: []byte{0xfa, 0x0b, 0x0c, 0x0d}, + read: float64(-99939315), + }, + { + name: "register50_int32_float64_scale", + address: 50, + dataTypeIn: "INT32", + scale: 1.0, + write: []byte{0xfa, 0x0b, 0x0c, 0x0d}, + read: float64(-99939315), + }, + { + name: "register60_uint64", + address: 60, + dataTypeIn: "UINT64", + write: []byte{0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x01, 0x02}, + read: uint64(723685415333069058), + }, + { + name: "register60_uint64-scale_.1", + address: 60, + dataTypeIn: "UINT64", + scale: .1, + write: []byte{0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x01, 0x02}, + read: float64(72368541533306905.8), + }, + { + name: "register60_uint64_scale_10", + address: 60, + dataTypeIn: "UINT64", + scale: 10, + write: []byte{0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x01, 0x02}, + read: float64(7236854153330690000), // quantization error + }, + { + name: "register60_uint64_int64", + address: 60, + dataTypeIn: "UINT64", + dataTypeOut: "INT64", + write: []byte{0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x01, 0x02}, + read: int64(723685415333069058), + }, + { + name: "register60_uint64_float64", + address: 60, + dataTypeIn: "UINT64", + dataTypeOut: "FLOAT64", + write: []byte{0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x01, 0x02}, + read: float64(723685415333069058), + }, + { + name: "register60_uint64_float64_scale", + address: 60, + dataTypeIn: "UINT64", + scale: 1.0, + write: []byte{0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x01, 0x02}, + read: float64(723685415333069058), + }, + { + name: "register70_int64", + address: 70, + dataTypeIn: "INT64", + write: []byte{0xfa, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x01, 0x02}, + read: int64(-429236089273777918), + }, + { + name: "register70_int64-scale_.1", + address: 70, + dataTypeIn: "INT64", + scale: .1, + write: []byte{0xfa, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x01, 0x02}, + read: float64(-42923608927377791.8), + }, + { + name: "register70_int64_scale_10", + address: 70, + dataTypeIn: "INT64", + scale: 10, + write: []byte{0xfa, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x01, 0x02}, + read: float64(-4292360892737779180), + }, + { + name: "register70_int64_uint64", + address: 70, + dataTypeIn: "INT64", + dataTypeOut: "UINT64", + write: []byte{0xfa, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x01, 0x02}, + read: uint64(18017507984435773698), + }, + { + name: "register70_int64_float64", + address: 70, + dataTypeIn: "INT64", + dataTypeOut: "FLOAT64", + write: []byte{0xfa, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x01, 0x02}, + read: float64(-429236089273777918), + }, + { + name: "register70_int64_float64_scale", + address: 70, + dataTypeIn: "INT64", + scale: 1.0, + write: []byte{0xfa, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x01, 0x02}, + read: float64(-429236089273777918), + }, + { + name: "register80_float32", + address: 80, + dataTypeIn: "FLOAT32", + write: []byte{0x40, 0x49, 0x0f, 0xdb}, + read: float32(3.1415927410125732421875), + }, + { + name: "register80_float32-scale_.1", + address: 80, + dataTypeIn: "FLOAT32", + scale: .1, + write: []byte{0x40, 0x49, 0x0f, 0xdb}, + read: float64(0.31415927410125732421875), + }, + { + name: "register80_float32_scale_10", + address: 80, + dataTypeIn: "FLOAT32", + scale: 10, + write: []byte{0x40, 0x49, 0x0f, 0xdb}, + read: float64(31.415927410125732421875), + }, + { + name: "register80_float32_float64", + address: 80, + dataTypeIn: "FLOAT32", + dataTypeOut: "FLOAT64", + write: []byte{0x40, 0x49, 0x0f, 0xdb}, + read: float64(3.1415927410125732421875), + }, + { + name: "register80_float32_float64_scale", + address: 80, + dataTypeIn: "FLOAT32", + scale: 1.0, + write: []byte{0x40, 0x49, 0x0f, 0xdb}, + read: float64(3.1415927410125732421875), + }, + { + name: "register90_float64", + address: 90, + dataTypeIn: "FLOAT64", + write: []byte{0x40, 0x09, 0x21, 0xfb, 0x54, 0x44, 0x2e, 0xea}, + read: float64(3.14159265359000006156975359772), + }, + { + name: "register90_float64-scale_.1", + address: 90, + dataTypeIn: "FLOAT64", + scale: .1, + write: []byte{0x40, 0x09, 0x21, 0xfb, 0x54, 0x44, 0x2e, 0xea}, + read: float64(0.314159265359000006156975359772), + }, + { + name: "register90_float64_scale_10", + address: 90, + dataTypeIn: "FLOAT64", + scale: 10, + write: []byte{0x40, 0x09, 0x21, 0xfb, 0x54, 0x44, 0x2e, 0xea}, + read: float64(31.4159265359000006156975359772), + }, + { + name: "register90_float64_float64_scale", + address: 90, + dataTypeIn: "FLOAT64", + scale: 1.0, + write: []byte{0x40, 0x09, 0x21, 0xfb, 0x54, 0x44, 0x2e, 0xea}, + read: float64(3.14159265359000006156975359772), + }, + } + + serv := mbserver.NewServer() + require.NoError(t, serv.ListenTCP("localhost:1502")) + defer serv.Close() + + handler := mb.NewTCPClientHandler("localhost:1502") + require.NoError(t, handler.Connect()) + defer handler.Close() + client := mb.NewClient(handler) + + for _, hrt := range tests { + t.Run(hrt.name, func(t *testing.T) { + quantity := uint16(len(hrt.write) / 2) + invert := make([]byte, 0, len(hrt.write)) + for i := len(hrt.write) - 1; i >= 0; i-- { + invert = append(invert, hrt.write[i]) + } + _, err := client.WriteMultipleRegisters(hrt.address, quantity, invert) + require.NoError(t, err) + + modbus := Modbus{ + Name: "TestRequestTypesHoldingABCD", + Controller: "tcp://localhost:1502", + ConfigurationType: "request", + Log: testutil.Logger{}, + } + modbus.Requests = []requestDefinition{ + { + SlaveID: 1, + ByteOrder: byteOrder, + RegisterType: "holding", + Fields: []requestFieldDefinition{ + { + Name: hrt.name, + InputType: hrt.dataTypeIn, + OutputType: hrt.dataTypeOut, + Scale: hrt.scale, + Address: hrt.address, + }, + }, + }, + } + + expected := []telegraf.Metric{ + testutil.MustMetric( + "modbus", + map[string]string{ + "type": cHoldingRegisters, + "slave_id": "1", + "name": modbus.Name, + }, + map[string]interface{}{hrt.name: hrt.read}, + time.Unix(0, 0), + ), + } + + var acc testutil.Accumulator + require.NoError(t, modbus.Init()) + require.NotEmpty(t, modbus.requests) + require.NoError(t, modbus.Gather(&acc)) + acc.Wait(len(expected)) + + testutil.RequireMetricsEqual(t, expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime()) + }) + } +} + func TestReadMultipleCoilWithHole(t *testing.T) { serv := mbserver.NewServer() require.NoError(t, serv.ListenTCP("localhost:1502")) diff --git a/plugins/inputs/modbus/sample_register.conf b/plugins/inputs/modbus/sample_register.conf index ebfaf636d..6afdb626c 100644 --- a/plugins/inputs/modbus/sample_register.conf +++ b/plugins/inputs/modbus/sample_register.conf @@ -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 diff --git a/plugins/inputs/modbus/sample_request.conf b/plugins/inputs/modbus/sample_request.conf index 876ff8d13..1e32ee448 100644 --- a/plugins/inputs/modbus/sample_request.conf +++ b/plugins/inputs/modbus/sample_request.conf @@ -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 diff --git a/plugins/inputs/modbus/type_conversions.go b/plugins/inputs/modbus/type_conversions.go index 556f7b423..3f856bd71 100644 --- a/plugins/inputs/modbus/type_conversions.go +++ b/plugins/inputs/modbus/type_conversions.go @@ -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": diff --git a/plugins/inputs/modbus/type_conversions8.go b/plugins/inputs/modbus/type_conversions8.go new file mode 100644 index 000000000..f4925a049 --- /dev/null +++ b/plugins/inputs/modbus/type_conversions8.go @@ -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) +}