From ed99c08c2c17c68da10d73195fe3d6590811951c Mon Sep 17 00:00:00 2001 From: Malte Muench Date: Sun, 28 Jul 2024 09:22:36 +0200 Subject: [PATCH 01/16] Introduce new interface ConvertMode Signed-off-by: Malte Muench --- src/types.go | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/types.go b/src/types.go index cb87f5f..c59abee 100644 --- a/src/types.go +++ b/src/types.go @@ -1,10 +1,23 @@ package main -import "github.com/brutella/can" +import ( + "fmt" + "github.com/brutella/can" +) type convertToCan func(input []byte) (can.Frame, error) type convertToMqtt func(input can.Frame) ([]byte, error) +// ConvertMode is the interface that defines the two methods necessary +// to handle MQTT-Messages as well as CAN-Frames. It also includes Stringer +// to make types that implement it print their human-readable convertmode, as it +// appears in the can2mqtt file. +type ConvertMode interface { + ToCan(input []byte) (can.Frame, error) + ToMqtt(input can.Frame) ([]byte, error) + fmt.Stringer +} + // can2mqtt is a struct that represents the internal type of // one line of the can2mqtt.csv file. It has // the same three fields as the can2mqtt.csv file: CAN-ID, @@ -15,4 +28,4 @@ type can2mqtt struct { toCan convertToCan toMqtt convertToMqtt mqttTopic string -} +} \ No newline at end of file From e10cbbc7f2722719fad93e2f6ea1684e76a16182 Mon Sep 17 00:00:00 2001 From: Malte Muench Date: Sun, 28 Jul 2024 09:30:57 +0200 Subject: [PATCH 02/16] Refine ConvertMode interface doc --- src/types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types.go b/src/types.go index c59abee..7e66a4d 100644 --- a/src/types.go +++ b/src/types.go @@ -9,7 +9,7 @@ type convertToCan func(input []byte) (can.Frame, error) type convertToMqtt func(input can.Frame) ([]byte, error) // ConvertMode is the interface that defines the two methods necessary -// to handle MQTT-Messages as well as CAN-Frames. It also includes Stringer +// to handle MQTT-Messages (ToMqtt) as well as CAN-Frames(ToCan). It also includes fmt.Stringer // to make types that implement it print their human-readable convertmode, as it // appears in the can2mqtt file. type ConvertMode interface { From a2d7891bac33da8cc076044139919f0afe386723 Mon Sep 17 00:00:00 2001 From: Malte Muench Date: Sun, 28 Jul 2024 09:31:15 +0200 Subject: [PATCH 03/16] Implements first convertmode (none) as interface --- src/convertfunctions/none.go | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/convertfunctions/none.go b/src/convertfunctions/none.go index 01d07b0..60fef6b 100644 --- a/src/convertfunctions/none.go +++ b/src/convertfunctions/none.go @@ -2,6 +2,25 @@ package convertfunctions import "github.com/brutella/can" +type None struct{} + +func (_ None) String() string { + return "none" +} + +func (_ None) ToCan(input []byte) (can.Frame, error) { + var returner [8]byte + var i uint8 = 0 + for ; int(i) < len(input) && i < 8; i++ { + returner[i] = input[i] + } + return can.Frame{Length: i, Data: returner}, nil +} + +func (_ None) ToMqtt(input can.Frame) ([]byte, error) { + return input.Data[:input.Length], nil +} + func NoneToCan(input []byte) (can.Frame, error) { var returner [8]byte var i uint8 = 0 @@ -13,4 +32,4 @@ func NoneToCan(input []byte) (can.Frame, error) { func NoneToMqtt(input can.Frame) ([]byte, error) { return input.Data[:input.Length], nil -} +} \ No newline at end of file From 8e4d5d16f717ea7ada29bf323491206d2c1b92fc Mon Sep 17 00:00:00 2001 From: Malte Muench Date: Sun, 28 Jul 2024 09:32:42 +0200 Subject: [PATCH 04/16] Adds convertmode interface to the can2mqtt struct --- src/types.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/types.go b/src/types.go index 7e66a4d..e203e31 100644 --- a/src/types.go +++ b/src/types.go @@ -23,9 +23,10 @@ type ConvertMode interface { // the same three fields as the can2mqtt.csv file: CAN-ID, // conversion method and MQTT-Topic. type can2mqtt struct { - canId uint32 - convMethod string - toCan convertToCan - toMqtt convertToMqtt - mqttTopic string + canId uint32 + convMethod string + toCan convertToCan + toMqtt convertToMqtt + convertMode ConvertMode + mqttTopic string } \ No newline at end of file From e088206da528fe11b0fc4a946f8218915b8b922b Mon Sep 17 00:00:00 2001 From: Malte Muench Date: Sun, 28 Jul 2024 09:57:58 +0200 Subject: [PATCH 05/16] bytecolor2colorcode change to interfaced version --- src/convertfunctions/bytecolor2colorcode.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/convertfunctions/bytecolor2colorcode.go b/src/convertfunctions/bytecolor2colorcode.go index 14909cf..c402ef2 100644 --- a/src/convertfunctions/bytecolor2colorcode.go +++ b/src/convertfunctions/bytecolor2colorcode.go @@ -8,7 +8,13 @@ import ( "strings" ) -func ByteColor2ColorCodeToCan(input []byte) (can.Frame, error) { +type ByteColor2ColorCode struct{} + +func (_ ByteColor2ColorCode) String() string { + return "bytecolor2colorcode" +} + +func (_ ByteColor2ColorCode) ToCan(input []byte) (can.Frame, error) { colorBytes, _ := strings.CutPrefix(string(input), "#") if len(colorBytes) != 6 { return can.Frame{}, errors.New(fmt.Sprintf("input does not contain exactly 6 nibbles each represented by one character, got %d instead", len(colorBytes))) @@ -22,10 +28,10 @@ func ByteColor2ColorCodeToCan(input []byte) (can.Frame, error) { return returner, nil } -func ByteColor2ColorCodeToMqtt(input can.Frame) ([]byte, error) { +func (_ ByteColor2ColorCode) ToMqtt(input can.Frame) ([]byte, error) { if input.Length != 3 { return []byte{}, errors.New(fmt.Sprintf("Input does not contain exactly 3 bytes, got %d instead", input.Length)) } colorstring := hex.EncodeToString(input.Data[0:3]) return []byte("#" + colorstring), nil -} +} \ No newline at end of file From 45b4ce607fa1f3b299a4573c49c4e1038cae18e8 Mon Sep 17 00:00:00 2001 From: Malte Muench Date: Sun, 28 Jul 2024 10:03:57 +0200 Subject: [PATCH 06/16] mymode changed to interfaced version --- README.md | 7 +++---- src/convertfunctions/mymode.go | 12 +++++++++--- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 2967a56..fb13001 100644 --- a/README.md +++ b/README.md @@ -106,9 +106,8 @@ To debug can2mqtts behaviour you need to be able to send and receive CAN frames If you want to add a convert-Mode think about a name. This is the name that you can later refer to when you want to use your convert-Mode in the `can2mqtt.csv` config-file. In this example the name of the new convert-Mode is `"mymode"`. -Next, add the new convert-Mode in the [switch-case block in `src/main.go`](./src/main.go#L247). In the new case -statement you refer to two functions. One is called when a MQTT-message is received and the other one when a CAN-frame is -received. Change the contents of those functions to the behaviour that you seek for. Mockup code for `MyModeToCan` and -`MyModeToMqtt` can be found in [mymode.go](./src/mymode.go). +Next, add the new convert-Mode in the [switch-case block in `src/main.go`](./src/main.go#L247). In the new case you select your convertMode, in this case "convertfunctions.MyMode{}". This struct implements two functions. One is called when a MQTT-message is received and the other one when a CAN-frame is +received. Change the contents of those functions to the behaviour that you seek for. Mockup code for `(_ MyMode) ToCan` and +`(_ MyMode) ToMqtt` can be found in [mymode.go](./src/mymode.go). Good luck & happy hacking ✌ \ No newline at end of file diff --git a/src/convertfunctions/mymode.go b/src/convertfunctions/mymode.go index f72572f..8123651 100644 --- a/src/convertfunctions/mymode.go +++ b/src/convertfunctions/mymode.go @@ -7,7 +7,13 @@ import ( const mockErr string = "I am just mockup-code and not supposed to be actually used, implement something useful here" -func MyModeToCan(input []byte) (can.Frame, error) { +type MyMode struct{} + +func (_ MyMode) String() string { + return "mymode" +} + +func (_ MyMode) ToCan(input []byte) (can.Frame, error) { /* This is your area to create your convertMode (Receive MQTT, convert to CAN). You can find the payload of the received MQTT-Message @@ -26,7 +32,7 @@ func MyModeToCan(input []byte) (can.Frame, error) { return can.Frame{}, errors.New(mockErr) } -func MyModeToMqtt(input can.Frame) ([]byte, error) { +func (_ MyMode) ToMqtt(input can.Frame) ([]byte, error) { /* This is your area to create your convertMode (Receive CAN, convert to MQTT). You can find the received CAN-Frame in the can.Frame input. You can craft your returning MQTT-Payload here. @@ -37,4 +43,4 @@ func MyModeToMqtt(input can.Frame) ([]byte, error) { return input.Data[:input.Length], nil */ return []byte{}, errors.New(mockErr) -} +} \ No newline at end of file From 6e68390c1067a95cd5e06c6be2614400998d9052 Mon Sep 17 00:00:00 2001 From: Malte Muench Date: Sun, 28 Jul 2024 10:05:47 +0200 Subject: [PATCH 07/16] pixelbin2ascii changed to interfaced version --- src/convertfunctions/pixelbin2ascii.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/convertfunctions/pixelbin2ascii.go b/src/convertfunctions/pixelbin2ascii.go index f2b6881..f3e4d42 100644 --- a/src/convertfunctions/pixelbin2ascii.go +++ b/src/convertfunctions/pixelbin2ascii.go @@ -9,7 +9,13 @@ import ( "strings" ) -func PixelBin2AsciiToCan(input []byte) (can.Frame, error) { +type PixelBin2Ascii struct{} + +func (_ PixelBin2Ascii) String() string { + return "pixelbin2ascii" +} + +func (_ PixelBin2Ascii) ToCan(input []byte) (can.Frame, error) { colorBytesAndNumber := strings.Fields(string(input)) if len(colorBytesAndNumber) != 2 { return can.Frame{}, errors.New(fmt.Sprintf("input does not contain exactly two fields, one for the number and one for the color, got %d fields instead.", len(colorBytesAndNumber))) @@ -32,11 +38,11 @@ func PixelBin2AsciiToCan(input []byte) (can.Frame, error) { return returner, nil } -func PixelBin2AsciiToMqtt(input can.Frame) ([]byte, error) { +func (_ PixelBin2Ascii) ToMqtt(input can.Frame) ([]byte, error) { if input.Length != 4 { return []byte{}, errors.New(fmt.Sprintf("Input does not contain exactly 4 bytes, got %d instead", input.Length)) } colorString := "#" + hex.EncodeToString(input.Data[0:3]) numberString := strconv.FormatUint(uint64(input.Data[0]), 10) return []byte(strings.Join([]string{numberString, colorString}, " ")), nil -} +} \ No newline at end of file From c38bc91ae4be22cb441db2073ccb68c98fcc6686 Mon Sep 17 00:00:00 2001 From: Malte Muench Date: Sun, 28 Jul 2024 10:50:27 +0200 Subject: [PATCH 08/16] 16bool2ascii into interfaced version --- src/convertfunctions/sixteenbool2ascii.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/convertfunctions/sixteenbool2ascii.go b/src/convertfunctions/sixteenbool2ascii.go index 4ccf56d..03117d2 100644 --- a/src/convertfunctions/sixteenbool2ascii.go +++ b/src/convertfunctions/sixteenbool2ascii.go @@ -8,7 +8,13 @@ import ( "strings" ) -func SixteenBool2AsciiToCan(input []byte) (can.Frame, error) { +type SixteenBool2Ascii struct{} + +func (_ SixteenBool2Ascii) String() string { + return "16bool2ascii" +} + +func (_ SixteenBool2Ascii) ToCan(input []byte) (can.Frame, error) { splitInput := strings.Split(string(input), " ") // TODO use strings.Fields here if len(splitInput) != 16 { return can.Frame{}, errors.New("input does not contain exactly 16 numbers seperated by spaces") @@ -28,7 +34,7 @@ func SixteenBool2AsciiToCan(input []byte) (can.Frame, error) { } return can.Frame{Length: 2, Data: returnData}, nil } -func SixteenBool2AsciiToMqtt(input can.Frame) ([]byte, error) { +func (_ SixteenBool2Ascii) ToMqtt(input can.Frame) ([]byte, error) { var returnStrings [16]string for i := 0; i < 16; i++ { if (input.Data[i>>3]>>(i%8))&0x1 == 1 { @@ -38,4 +44,4 @@ func SixteenBool2AsciiToMqtt(input can.Frame) ([]byte, error) { } } return []byte(strings.Join(returnStrings[:], " ")), nil -} +} \ No newline at end of file From e4a5d863595277a490b36a3b1d955a73ebb14faa Mon Sep 17 00:00:00 2001 From: Malte Muench Date: Sun, 28 Jul 2024 10:50:44 +0200 Subject: [PATCH 09/16] int2ascii into interfaced version --- src/convertfunctions/int2ascii.go | 143 ++++++++++-------------------- 1 file changed, 45 insertions(+), 98 deletions(-) diff --git a/src/convertfunctions/int2ascii.go b/src/convertfunctions/int2ascii.go index 332a7d6..3e8e11b 100644 --- a/src/convertfunctions/int2ascii.go +++ b/src/convertfunctions/int2ascii.go @@ -9,99 +9,52 @@ import ( "strings" ) -func Int82AsciiToCan(input []byte) (can.Frame, error) { - return NIntM2AsciiToCan(1, 8, input) +// Int2Ascii is a convertMode that can take multiple signed integers of one size. +// instances describe the amount of numbers that should be converted, bits is the size of each number +// instances * bits must fit into 64 bits. +type Int2Ascii struct { + instances, bits uint } -func Int82AsciiToMqtt(input can.Frame) ([]byte, error) { - return NIntM2AsciiToMqtt(1, 8, input) -} - -func Int162AsciiToCan(input []byte) (can.Frame, error) { - return NIntM2AsciiToCan(1, 16, input) -} - -func Int162AsciiToMqtt(input can.Frame) ([]byte, error) { - return NIntM2AsciiToMqtt(1, 16, input) -} - -func Int322AsciiToCan(input []byte) (can.Frame, error) { - return NIntM2AsciiToCan(1, 32, input) -} - -func Int322AsciiToMqtt(input can.Frame) ([]byte, error) { - return NIntM2AsciiToMqtt(1, 32, input) -} - -func Int642AsciiToCan(input []byte) (can.Frame, error) { - return NIntM2AsciiToCan(1, 64, input) -} - -func Int642AsciiToMqtt(input can.Frame) ([]byte, error) { - return NIntM2AsciiToMqtt(1, 64, input) -} - -func TwoInt322AsciiToCan(input []byte) (can.Frame, error) { - return NIntM2AsciiToCan(2, 32, input) -} - -func TwoInt322AsciiToMqtt(input can.Frame) ([]byte, error) { - return NIntM2AsciiToMqtt(2, 32, input) -} - -func EightInt82AsciiToCan(input []byte) (can.Frame, error) { - return NIntM2AsciiToCan(8, 8, input) -} - -func EightInt82AsciiToMqtt(input can.Frame) ([]byte, error) { - return NIntM2AsciiToMqtt(8, 8, input) -} - -func FourInt82AsciiToCan(input []byte) (can.Frame, error) { - return NIntM2AsciiToCan(4, 8, input) -} - -func FourInt82AsciiToMqtt(input can.Frame) ([]byte, error) { - return NIntM2AsciiToMqtt(4, 8, input) -} - -func FourInt162AsciiToCan(input []byte) (can.Frame, error) { - return NIntM2AsciiToCan(4, 16, input) +func (i2a Int2Ascii) String() string { + instanceString := "" + if i2a.instances > 1 { + instanceString = fmt.Sprintf("%d", i2a.instances) + } + return fmt.Sprintf("%sint%d2ascii", instanceString, i2a.bits) } -func FourInt162AsciiToMqtt(input can.Frame) ([]byte, error) { - return NIntM2AsciiToMqtt(4, 16, input) +func NewInt2Ascii(instances, bits uint) (Int2Ascii, error) { + if !(bits == 8 || bits == 16 || bits == 32 || bits == 64) { + return Int2Ascii{}, errors.New(fmt.Sprintf("bitsize %d not supported, please choose one of 8, 16. 32 or 64\n", bits)) + } + if bits*instances > 64 { + return Int2Ascii{}, errors.New(fmt.Sprintf("%d instances of %d bit size would not fit into a 8 byte CAN-Frame. %d exceeds 64 bits.\n", instances, bits, instances*bits)) + } + return Int2Ascii{instances, bits}, nil } -// NIntM2AsciiToCan is the generic approach to convert numberAmount occurrences of numbers with numberWidth bits size. -// Allowed values for numberAmount are 1-8. -// Allowed values for numberWidth are 8, 16, 32 or 64 -// numberAmount*numberWidth shall not be larger than 64 +// ToCan is the generic approach to convert instances instances of numbers with bits bits size. +// Allowed values for instances are 1-8. +// Allowed values for bits are 8, 16, 32 or 64 +// instances*bits must not be larger than 64 // input has to contain the data that shall be converted. The input is split at whitespaces, the amount of fields has -// to match numberAmount. -// If the amount of fields matches, each field is converted to an uint of size numberWidth. The results are then added to the CAN-frame. -func NIntM2AsciiToCan(numberAmount, numberWidth uint, input []byte) (can.Frame, error) { - if !(numberWidth == 8 || numberWidth == 16 || numberWidth == 32 || numberWidth == 64) { - - return can.Frame{}, errors.New(fmt.Sprintf("numberWitdh %d uknown please choose one of 8, 16. 32 or 64\n", numberWidth)) - - } - if numberWidth*numberAmount > 64 { - return can.Frame{}, errors.New(fmt.Sprintf("%d number of %d bit width would not fit into a 8 byte CAN-Frame %d exceeds 64 bits.\n", numberAmount, numberWidth, numberAmount*numberWidth)) - } +// to match instances. +// If the amount of fields matches, each field is converted to an uint of size bits. The results are then added to the CAN-frame. +func (i2a Int2Ascii) ToCan(input []byte) (can.Frame, error) { splitInput := strings.Fields(string(input)) - if uint(len(splitInput)) != numberAmount { - return can.Frame{}, errors.New(fmt.Sprintf("input does not contain exactly %d numbers seperated by whitespace", numberAmount)) + if uint(len(splitInput)) != i2a.instances { + return can.Frame{}, errors.New(fmt.Sprintf("input does not contain exactly %d numbers seperated by whitespace", i2a.instances)) } var ret can.Frame - ret.Length = uint8((numberAmount * numberWidth) >> 3) - bytePerNumber := numberWidth >> 3 - for i := uint(0); i < numberAmount; i++ { - res, err := strconv.ParseInt(splitInput[i], 10, int(numberWidth)) + ret.Length = uint8((i2a.instances * i2a.bits) >> 3) + bytePerNumber := i2a.bits >> 3 + for i := uint(0); i < i2a.instances; i++ { + res, err := strconv.ParseInt(splitInput[i], 10, int(i2a.bits)) if err != nil { return can.Frame{}, errors.New(fmt.Sprintf("Error while converting string %d: %s, %s", i, splitInput[i], err)) } - switch numberWidth { + switch i2a.bits { case 64: binary.LittleEndian.PutUint64(ret.Data[i*bytePerNumber:(i+1)*bytePerNumber], uint64(res)) case 32: @@ -115,29 +68,23 @@ func NIntM2AsciiToCan(numberAmount, numberWidth uint, input []byte) (can.Frame, return ret, nil } -// NIntM2AsciiToMqtt is the generic approach to convert numberAmount occurrences of numbers with numberWidth bits size. -// Allowed values for numberAmount are 1-8. -// Allowed values for numberWidth are 8, 16, 32 or 64 -// numberAmount*numberWidth shall not be larger than 64 +// ToMqtt is the generic approach to convert instances instances of numbers with bits bits size. +// Allowed values for instances are 1-8. +// Allowed values for bits are 8, 16, 32 or 64 +// instances*bits must not be larger than 64 // input has to Contain the Data that shall be converted. The Size of the CAN-Frame has to fit the expected size. // If we have for example 1 amount of 32-Bits numbers the CAN-Frame size input.Length has to be 4 (bytes). -// If the size fits, the Data is split up in numberAmount pieces and are then processed to a string representation +// If the size fits, the Data is split up in instances pieces and are then processed to a string representation // via strconv.FormatUint. // The successful return value is a byte-slice that represents the converted strings joined with a space between them. -func NIntM2AsciiToMqtt(numberAmount, numberWidth uint, input can.Frame) ([]byte, error) { - if !(numberWidth == 8 || numberWidth == 16 || numberWidth == 32 || numberWidth == 64) { - return []byte{}, errors.New(fmt.Sprintf("numberWitdh %d uknown please choose one of 8, 16. 32 or 64\n", numberWidth)) - } - if numberWidth*numberAmount > 64 { - return []byte{}, errors.New(fmt.Sprintf("%d number of %d bit width would not fit into a 8 byte CAN-Frame %d exceeds 64 bits.\n", numberAmount, numberWidth, numberAmount*numberWidth)) - } - if input.Length != uint8((numberWidth*numberAmount)>>3) { - return []byte{}, errors.New(fmt.Sprintf("Input is of wrong length: %d, expected %d because of %d numbers of %d-bits.", input.Length, (numberAmount*numberWidth)>>3, numberAmount, numberWidth)) +func (i2a Int2Ascii) ToMqtt(input can.Frame) ([]byte, error) { + if input.Length != uint8((i2a.bits*i2a.instances)>>3) { + return []byte{}, errors.New(fmt.Sprintf("Input is of wrong length: %d, expected %d because of %d numbers of %d-bits.", input.Length, (i2a.instances*i2a.bits)>>3, i2a.instances, i2a.bits)) } var returnStrings []string - bytePerNumber := numberWidth >> 3 - for i := uint(0); i < numberAmount; i++ { - switch numberWidth { + bytePerNumber := i2a.bits >> 3 + for i := uint(0); i < i2a.instances; i++ { + switch i2a.bits { case 64: returnStrings = append(returnStrings, strconv.FormatInt(int64(binary.LittleEndian.Uint64(input.Data[i*bytePerNumber:(i+1)*bytePerNumber])), 10)) case 32: @@ -149,4 +96,4 @@ func NIntM2AsciiToMqtt(numberAmount, numberWidth uint, input can.Frame) ([]byte, } } return []byte(strings.Join(returnStrings, " ")), nil -} +} \ No newline at end of file From 7ab64f8255059d12293c4c8656724dd9ec5d2dbd Mon Sep 17 00:00:00 2001 From: Malte Muench Date: Sun, 28 Jul 2024 11:01:39 +0200 Subject: [PATCH 10/16] uint2ascii into interfaced version --- src/convertfunctions/uint2ascii.go | 143 +++++++++-------------------- 1 file changed, 45 insertions(+), 98 deletions(-) diff --git a/src/convertfunctions/uint2ascii.go b/src/convertfunctions/uint2ascii.go index 82dc8ce..eabdaf4 100644 --- a/src/convertfunctions/uint2ascii.go +++ b/src/convertfunctions/uint2ascii.go @@ -9,99 +9,52 @@ import ( "strings" ) -func Uint82AsciiToCan(input []byte) (can.Frame, error) { - return NUintM2AsciiToCan(1, 8, input) +// Uint2Ascii is a convertMode that can take multiple signed integers of one size. +// instances describe the amount of numbers that should be converted, bits is the size of each number +// instances * bits must fit into 64 bits. +type Uint2Ascii struct { + instances, bits uint } -func Uint82AsciiToMqtt(input can.Frame) ([]byte, error) { - return NUintM2AsciiToMqtt(1, 8, input) -} - -func Uint162AsciiToCan(input []byte) (can.Frame, error) { - return NUintM2AsciiToCan(1, 16, input) -} - -func Uint162AsciiToMqtt(input can.Frame) ([]byte, error) { - return NUintM2AsciiToMqtt(1, 16, input) -} - -func Uint322AsciiToCan(input []byte) (can.Frame, error) { - return NUintM2AsciiToCan(1, 32, input) -} - -func Uint322AsciiToMqtt(input can.Frame) ([]byte, error) { - return NUintM2AsciiToMqtt(1, 32, input) -} - -func Uint642AsciiToCan(input []byte) (can.Frame, error) { - return NUintM2AsciiToCan(1, 64, input) -} - -func Uint642AsciiToMqtt(input can.Frame) ([]byte, error) { - return NUintM2AsciiToMqtt(1, 64, input) -} - -func TwoUint322AsciiToCan(input []byte) (can.Frame, error) { - return NUintM2AsciiToCan(2, 32, input) -} - -func TwoUint322AsciiToMqtt(input can.Frame) ([]byte, error) { - return NUintM2AsciiToMqtt(2, 32, input) -} - -func EightUint82AsciiToCan(input []byte) (can.Frame, error) { - return NUintM2AsciiToCan(8, 8, input) -} - -func EightUint82AsciiToMqtt(input can.Frame) ([]byte, error) { - return NUintM2AsciiToMqtt(8, 8, input) -} - -func FourUint82AsciiToCan(input []byte) (can.Frame, error) { - return NUintM2AsciiToCan(4, 8, input) -} - -func FourUint82AsciiToMqtt(input can.Frame) ([]byte, error) { - return NUintM2AsciiToMqtt(4, 8, input) -} - -func FourUint162AsciiToCan(input []byte) (can.Frame, error) { - return NUintM2AsciiToCan(4, 16, input) +func (u2a Uint2Ascii) String() string { + instanceString := "" + if u2a.instances > 1 { + instanceString = fmt.Sprintf("%d", u2a.instances) + } + return fmt.Sprintf("%suint%d2ascii", instanceString, u2a.bits) } -func FourUint162AsciiToMqtt(input can.Frame) ([]byte, error) { - return NUintM2AsciiToMqtt(4, 16, input) +func NewUint2Ascii(instances, bits uint) (Uint2Ascii, error) { + if !(bits == 8 || bits == 16 || bits == 32 || bits == 64) { + return Uint2Ascii{}, errors.New(fmt.Sprintf("bitsize %d not supported, please choose one of 8, 16. 32 or 64\n", bits)) + } + if bits*instances > 64 { + return Uint2Ascii{}, errors.New(fmt.Sprintf("%d instances of %d bit size would not fit into a 8 byte CAN-Frame. %d exceeds 64 bits.\n", instances, bits, instances*bits)) + } + return Uint2Ascii{instances, bits}, nil } -// NUintM2AsciiToCan is the generic approach to convert numberAmount occurrences of numbers with numberWidth bits size. -// Allowed values for numberAmount are 1-8. -// Allowed values for numberWidth are 8, 16, 32 or 64 -// numberAmount*numberWidth shall not be larger than 64 +// ToCan is the generic approach to convert instances occurrences of numbers with bits bits size. +// Allowed values for instances are 1-8. +// Allowed values for bits are 8, 16, 32 or 64 +// instances*bits must not be larger than 64 // input has to contain the data that shall be converted. The input is split at whitespaces, the amount of fields has -// to match numberAmount. -// If the amount of fields matches, each field is converted to an uint of size numberWidth. The results are then added to the CAN-frame. -func NUintM2AsciiToCan(numberAmount, numberWidth uint, input []byte) (can.Frame, error) { - if !(numberWidth == 8 || numberWidth == 16 || numberWidth == 32 || numberWidth == 64) { - - return can.Frame{}, errors.New(fmt.Sprintf("numberWitdh %d uknown please choose one of 8, 16. 32 or 64\n", numberWidth)) - - } - if numberWidth*numberAmount > 64 { - return can.Frame{}, errors.New(fmt.Sprintf("%d number of %d bit width would not fit into a 8 byte CAN-Frame %d exceeds 64 bits.\n", numberAmount, numberWidth, numberAmount*numberWidth)) - } +// to match instances. +// If the amount of fields matches, each field is converted to an uint of size bits. The results are then added to the CAN-frame. +func (u2a Uint2Ascii) ToCan(input []byte) (can.Frame, error) { splitInput := strings.Fields(string(input)) - if uint(len(splitInput)) != numberAmount { - return can.Frame{}, errors.New(fmt.Sprintf("input does not contain exactly %d numbers seperated by whitespace", numberAmount)) + if uint(len(splitInput)) != u2a.instances { + return can.Frame{}, errors.New(fmt.Sprintf("input does not contain exactly %d numbers seperated by whitespace", u2a.instances)) } var ret can.Frame - ret.Length = uint8((numberAmount * numberWidth) >> 3) - bytePerNumber := numberWidth >> 3 - for i := uint(0); i < numberAmount; i++ { - res, err := strconv.ParseUint(splitInput[i], 10, int(numberWidth)) + ret.Length = uint8((u2a.instances * u2a.bits) >> 3) + bytePerNumber := u2a.bits >> 3 + for i := uint(0); i < u2a.instances; i++ { + res, err := strconv.ParseUint(splitInput[i], 10, int(u2a.bits)) if err != nil { return can.Frame{}, errors.New(fmt.Sprintf("Error while converting string %d: %s, %s", i, splitInput[i], err)) } - switch numberWidth { + switch u2a.bits { case 64: binary.LittleEndian.PutUint64(ret.Data[i*bytePerNumber:(i+1)*bytePerNumber], res) case 32: @@ -115,29 +68,23 @@ func NUintM2AsciiToCan(numberAmount, numberWidth uint, input []byte) (can.Frame, return ret, nil } -// NUintM2AsciiToMqtt is the generic approach to convert numberAmount occurrences of numbers with numberWidth bits size. -// Allowed values for numberAmount are 1-8. -// Allowed values for numberWidth are 8, 16, 32 or 64 -// numberAmount*numberWidth shall not be larger than 64 +// ToMqtt is the generic approach to convert instances occurrences of numbers with bits bits size. +// Allowed values for instances are 1-8. +// Allowed values for bits are 8, 16, 32 or 64 +// instances*bits shall not be larger than 64 // input has to Contain the Data that shall be converted. The Size of the CAN-Frame has to fit the expected size. // If we have for example 1 amount of 32-Bits numbers the CAN-Frame size input.Length has to be 4 (bytes). -// If the size fits, the Data is split up in numberAmount pieces and are then processed to a string representation +// If the size fits, the Data is split up in instances pieces and are then processed to a string representation // via strconv.FormatUint. // The successful return value is a byte-slice that represents the converted strings joined with a space between them. -func NUintM2AsciiToMqtt(numberAmount, numberWidth uint, input can.Frame) ([]byte, error) { - if !(numberWidth == 8 || numberWidth == 16 || numberWidth == 32 || numberWidth == 64) { - return []byte{}, errors.New(fmt.Sprintf("numberWitdh %d uknown please choose one of 8, 16. 32 or 64\n", numberWidth)) - } - if numberWidth*numberAmount > 64 { - return []byte{}, errors.New(fmt.Sprintf("%d number of %d bit width would not fit into a 8 byte CAN-Frame %d exceeds 64 bits.\n", numberAmount, numberWidth, numberAmount*numberWidth)) - } - if input.Length != uint8((numberWidth*numberAmount)>>3) { - return []byte{}, errors.New(fmt.Sprintf("Input is of wrong length: %d, expected %d because of %d numbers of %d-bits.", input.Length, (numberAmount*numberWidth)>>3, numberAmount, numberWidth)) +func (u2a Uint2Ascii) ToMqtt(input can.Frame) ([]byte, error) { + if input.Length != uint8((u2a.bits*u2a.instances)>>3) { + return []byte{}, errors.New(fmt.Sprintf("Input is of wrong length: %d, expected %d because of %d numbers of %d-bits.", input.Length, (u2a.instances*u2a.bits)>>3, u2a.instances, u2a.bits)) } var returnStrings []string - bytePerNumber := numberWidth >> 3 - for i := uint(0); i < numberAmount; i++ { - switch numberWidth { + bytePerNumber := u2a.bits >> 3 + for i := uint(0); i < u2a.instances; i++ { + switch u2a.bits { case 64: returnStrings = append(returnStrings, strconv.FormatUint(binary.LittleEndian.Uint64(input.Data[i*bytePerNumber:(i+1)*bytePerNumber]), 10)) case 32: @@ -149,4 +96,4 @@ func NUintM2AsciiToMqtt(numberAmount, numberWidth uint, input can.Frame) ([]byte } } return []byte(strings.Join(returnStrings, " ")), nil -} +} \ No newline at end of file From 13840d99ab3378d0da20c00caa2419473da86355 Mon Sep 17 00:00:00 2001 From: Malte Muench Date: Sun, 28 Jul 2024 11:47:23 +0200 Subject: [PATCH 11/16] Use new interface for creation of convertmodes --- src/main.go | 211 +++++++++-------------------------------------- src/receiving.go | 14 ++-- src/types.go | 3 - 3 files changed, 46 insertions(+), 182 deletions(-) diff --git a/src/main.go b/src/main.go index 8d93486..5f2d40c 100644 --- a/src/main.go +++ b/src/main.go @@ -16,6 +16,7 @@ import ( var ( pairFromID map[uint32]*can2mqtt // c2m pair (lookup from ID) pairFromTopic map[string]*can2mqtt // c2m pair (lookup from Topic) + convertModeFromString map[string]ConvertMode debugLog bool canInterface, mqttConnection, configFile string version = "dev" @@ -63,6 +64,32 @@ func readC2MPFromFile(filename string) { r.FieldsPerRecord = 3 pairFromID = make(map[uint32]*can2mqtt) pairFromTopic = make(map[string]*can2mqtt) + convertModeFromString = make(map[string]ConvertMode) + + // initialize all convertModes + convertModeFromString["none"] = convertfunctions.None{} + convertModeFromString["16bool2ascii"] = convertfunctions.SixteenBool2Ascii{} + convertModeFromString["pixelbin2ascii"] = convertfunctions.PixelBin2Ascii{} + convertModeFromString["bytecolor2colorcode"] = convertfunctions.ByteColor2ColorCode{} + convertModeFromString["mymode"] = convertfunctions.MyMode{} + // Dynamically create int and uint convertmodes + for _, bits := range []uint{8, 16, 32, 64} { + for _, instances := range []uint{1, 2, 4, 8} { + if bits*instances <= 64 { + // int + cmi, _ := convertfunctions.NewInt2Ascii(instances, bits) + convertModeFromString[cmi.String()] = cmi + // uint + cmu, _ := convertfunctions.NewUint2Ascii(instances, bits) + convertModeFromString[cmu.String()] = cmu + } + } + } + if debugLog { + for _, cm := range convertModeFromString { + slog.Debug("convertmode initialized", "convertmode", cm) + } + } for { record, err := r.Read() // Stop at EOF. @@ -90,176 +117,16 @@ func readC2MPFromFile(filename string) { slog.Warn("skipping line duplicate topic", "topic", topic, "line", line) continue } - switch convMode { - case "16bool2ascii": - pairFromID[canID] = &can2mqtt{ - canId: canID, - convMethod: convMode, - mqttTopic: topic, - toCan: convertfunctions.SixteenBool2AsciiToCan, - toMqtt: convertfunctions.SixteenBool2AsciiToMqtt, - } - case "uint82ascii": - pairFromID[canID] = &can2mqtt{ - canId: canID, - convMethod: convMode, - mqttTopic: topic, - toCan: convertfunctions.Uint82AsciiToCan, - toMqtt: convertfunctions.Uint82AsciiToMqtt, - } - case "uint162ascii": - pairFromID[canID] = &can2mqtt{ - canId: canID, - convMethod: convMode, - mqttTopic: topic, - toCan: convertfunctions.Uint162AsciiToCan, - toMqtt: convertfunctions.Uint162AsciiToMqtt, - } - case "uint322ascii": - pairFromID[canID] = &can2mqtt{ - canId: canID, - convMethod: convMode, - mqttTopic: topic, - toCan: convertfunctions.Uint322AsciiToCan, - toMqtt: convertfunctions.Uint322AsciiToMqtt, - } - case "uint642ascii": - pairFromID[canID] = &can2mqtt{ - canId: canID, - convMethod: convMode, - mqttTopic: topic, - toCan: convertfunctions.Uint642AsciiToCan, - toMqtt: convertfunctions.Uint642AsciiToMqtt, - } - case "2uint322ascii": - pairFromID[canID] = &can2mqtt{ - canId: canID, - convMethod: convMode, - mqttTopic: topic, - toCan: convertfunctions.TwoUint322AsciiToCan, - toMqtt: convertfunctions.TwoUint322AsciiToMqtt, - } - case "4uint162ascii": - pairFromID[canID] = &can2mqtt{ - canId: canID, - convMethod: convMode, - mqttTopic: topic, - toCan: convertfunctions.FourUint162AsciiToCan, - toMqtt: convertfunctions.FourUint162AsciiToMqtt, - } - case "4uint82ascii": - pairFromID[canID] = &can2mqtt{ - canId: canID, - convMethod: convMode, - mqttTopic: topic, - toCan: convertfunctions.FourUint82AsciiToCan, - toMqtt: convertfunctions.FourUint82AsciiToMqtt, - } - case "8uint82ascii": - pairFromID[canID] = &can2mqtt{ - canId: canID, - convMethod: convMode, - mqttTopic: topic, - toCan: convertfunctions.EightUint82AsciiToCan, - toMqtt: convertfunctions.EightUint82AsciiToMqtt, - } - // Int methods come here now - case "int82ascii": - pairFromID[canID] = &can2mqtt{ - canId: canID, - convMethod: convMode, - mqttTopic: topic, - toCan: convertfunctions.Int82AsciiToCan, - toMqtt: convertfunctions.Int82AsciiToMqtt, - } - case "int162ascii": - pairFromID[canID] = &can2mqtt{ - canId: canID, - convMethod: convMode, - mqttTopic: topic, - toCan: convertfunctions.Int162AsciiToCan, - toMqtt: convertfunctions.Int162AsciiToMqtt, - } - case "int322ascii": - pairFromID[canID] = &can2mqtt{ - canId: canID, - convMethod: convMode, - mqttTopic: topic, - toCan: convertfunctions.Int322AsciiToCan, - toMqtt: convertfunctions.Int322AsciiToMqtt, - } - case "int642ascii": - pairFromID[canID] = &can2mqtt{ - canId: canID, - convMethod: convMode, - mqttTopic: topic, - toCan: convertfunctions.Int642AsciiToCan, - toMqtt: convertfunctions.Int642AsciiToMqtt, - } - case "2int322ascii": - pairFromID[canID] = &can2mqtt{ - canId: canID, - convMethod: convMode, - mqttTopic: topic, - toCan: convertfunctions.TwoInt322AsciiToCan, - toMqtt: convertfunctions.TwoInt322AsciiToMqtt, - } - case "4int162ascii": - pairFromID[canID] = &can2mqtt{ - canId: canID, - convMethod: convMode, - mqttTopic: topic, - toCan: convertfunctions.FourInt162AsciiToCan, - toMqtt: convertfunctions.FourInt162AsciiToMqtt, - } - case "4int82ascii": - pairFromID[canID] = &can2mqtt{ - canId: canID, - convMethod: convMode, - mqttTopic: topic, - toCan: convertfunctions.FourInt82AsciiToCan, - toMqtt: convertfunctions.FourInt82AsciiToMqtt, - } - case "8int82ascii": - pairFromID[canID] = &can2mqtt{ - canId: canID, - convMethod: convMode, - mqttTopic: topic, - toCan: convertfunctions.EightInt82AsciiToCan, - toMqtt: convertfunctions.EightInt82AsciiToMqtt, - } - case "bytecolor2colorcode": - pairFromID[canID] = &can2mqtt{ - canId: canID, - convMethod: convMode, - mqttTopic: topic, - toCan: convertfunctions.ByteColor2ColorCodeToCan, - toMqtt: convertfunctions.ByteColor2ColorCodeToMqtt, - } - case "pixelbin2ascii": - pairFromID[canID] = &can2mqtt{ - canId: canID, - convMethod: convMode, - mqttTopic: topic, - toCan: convertfunctions.PixelBin2AsciiToCan, - toMqtt: convertfunctions.PixelBin2AsciiToMqtt, - } - case "mymode": - pairFromID[canID] = &can2mqtt{ - canId: canID, - convMethod: convMode, - mqttTopic: topic, - toCan: convertfunctions.MyModeToCan, - toMqtt: convertfunctions.MyModeToMqtt, - } - default: - pairFromID[canID] = &can2mqtt{ - canId: canID, - convMethod: convMode, - mqttTopic: topic, - toCan: convertfunctions.NoneToCan, - toMqtt: convertfunctions.NoneToMqtt, - } + + if convertModeFromString[convMode] == nil { + slog.Warn("skipping line, unsupported convertMode ", "convertMode", convMode, "line", line) + continue + } + + pairFromID[canID] = &can2mqtt{ + canId: canID, + convertMode: convertModeFromString[convMode], + mqttTopic: topic, } pairFromTopic[topic] = pairFromID[canID] mqttSubscribe(topic) // TODO move to append function @@ -267,7 +134,7 @@ func readC2MPFromFile(filename string) { } for _, c2mp := range pairFromID { - slog.Debug("extracted pair", "id", c2mp.canId, "convertmode", c2mp.convMethod, "topic", c2mp.mqttTopic) + slog.Debug("extracted pair", "id", c2mp.canId, "convertmode", c2mp.convertMode, "topic", c2mp.mqttTopic) } } @@ -282,4 +149,4 @@ func isTopicInSlice(mqttTopic string) bool { // get the corresponding topic for an ID func getTopicFromId(canId uint32) string { return pairFromID[canId].mqttTopic -} +} \ No newline at end of file diff --git a/src/receiving.go b/src/receiving.go index ae94b3e..97d4cb4 100644 --- a/src/receiving.go +++ b/src/receiving.go @@ -14,15 +14,15 @@ func handleCAN(cf can.Frame) { slog.Debug("received CANFrame", "id", cf.ID, "len", cf.Length, "data", cf.Data) // Only do conversions when necessary if dirMode != 2 { - mqttPayload, err := pairFromID[cf.ID].toMqtt(cf) + mqttPayload, err := pairFromID[cf.ID].convertMode.ToMqtt(cf) if err != nil { - slog.Warn("conversion to MQTT message unsuccessful", "convertmode", pairFromID[cf.ID].convMethod, "error", err) + slog.Warn("conversion to MQTT message unsuccessful", "convertmode", pairFromID[cf.ID].convertMode, "error", err) return } topic := getTopicFromId(cf.ID) mqttPublish(topic, mqttPayload) // this is the most common log-message, craft with care... - slog.Info("CAN -> MQTT", "ID", cf.ID, "len", cf.Length, "data", cf.Data, "convertmode", pairFromID[cf.ID].convMethod, "topic", topic, "message", mqttPayload) + slog.Info("CAN -> MQTT", "ID", cf.ID, "len", cf.Length, "data", cf.Data, "convertmode", pairFromID[cf.ID].convertMode, "topic", topic, "message", mqttPayload) } } @@ -34,13 +34,13 @@ func handleMQTT(_ MQTT.Client, msg MQTT.Message) { slog.Debug("received message", "topic", msg.Topic(), "payload", msg.Payload()) if dirMode != 1 { - cf, err := pairFromTopic[msg.Topic()].toCan(msg.Payload()) + cf, err := pairFromTopic[msg.Topic()].convertMode.ToCan(msg.Payload()) if err != nil { - slog.Warn("conversion to CAN-Frame unsuccessful", "convertmode", pairFromTopic[msg.Topic()].convMethod, "error", err) + slog.Warn("conversion to CAN-Frame unsuccessful", "convertmode", pairFromTopic[msg.Topic()].convertMode, "error", err) return } cf.ID = pairFromTopic[msg.Topic()].canId canPublish(cf) - slog.Info("CAN <- MQTT", "ID", cf.ID, "len", cf.Length, "data", cf.Data, "convertmode", pairFromTopic[msg.Topic()].convMethod, "topic", msg.Topic(), "message", msg.Payload()) + slog.Info("CAN <- MQTT", "ID", cf.ID, "len", cf.Length, "data", cf.Data, "convertmode", pairFromTopic[msg.Topic()].convertMode, "topic", msg.Topic(), "message", msg.Payload()) } -} +} \ No newline at end of file diff --git a/src/types.go b/src/types.go index e203e31..e257d48 100644 --- a/src/types.go +++ b/src/types.go @@ -24,9 +24,6 @@ type ConvertMode interface { // conversion method and MQTT-Topic. type can2mqtt struct { canId uint32 - convMethod string - toCan convertToCan - toMqtt convertToMqtt convertMode ConvertMode mqttTopic string } \ No newline at end of file From 5abb3cfab0e7075bafcb1e140943ea91b68e0fef Mon Sep 17 00:00:00 2001 From: Malte Muench Date: Sun, 28 Jul 2024 11:47:56 +0200 Subject: [PATCH 12/16] Remove old unused types --- src/types.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/types.go b/src/types.go index e257d48..32078a4 100644 --- a/src/types.go +++ b/src/types.go @@ -5,9 +5,6 @@ import ( "github.com/brutella/can" ) -type convertToCan func(input []byte) (can.Frame, error) -type convertToMqtt func(input can.Frame) ([]byte, error) - // ConvertMode is the interface that defines the two methods necessary // to handle MQTT-Messages (ToMqtt) as well as CAN-Frames(ToCan). It also includes fmt.Stringer // to make types that implement it print their human-readable convertmode, as it From 67c8f2c3d2020d1310752a12a2ddb80fb42dd026 Mon Sep 17 00:00:00 2001 From: Malte Muench Date: Sun, 28 Jul 2024 12:04:52 +0200 Subject: [PATCH 13/16] Bundle all int and uint convertmodes in one row Signed-off-by: Malte Muench --- README.md | 32 ++++++++------------------------ 1 file changed, 8 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index fb13001..197edee 100644 --- a/README.md +++ b/README.md @@ -54,30 +54,14 @@ Explanation for the 1st Line: For example our Doorstatus is published on the CAN ## convert-modes Here they are: -| convertmode | description | -|-----------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `none` | does not convert anything. It just takes a bunch of bytes and hands it over to the other side. If you want to send strings, this will be your choice. If you have a mqtt payload that is longer than eight bytes, only the first eight bytes will be send via CAN. | -| `bytecolor2colorcode` | Converts an bytearray of 3 bytes to hexadecimal colorcode | -| `pixelbin2ascii` | This mode was designed to address colorized pixels. MQTT-wise you can insert a string like "<0-255> #RRGGBB" which will be converted to 4 byte on the CAN-BUS the first byte will be the number of the LED 0-255 and bytes 1, 2, 3 are the color of red, green and blue. | -| `16bool2ascii` | Interprets two bytes can-wise and publishes them as 16 boolean values to mqtt | -| *uint* | | -| `uint82ascii` | one uint8 in the CAN-Frame to one uint8 as string in the mqtt payload | -| `4uint82ascii` | four uint8 in the CAN-Frame to four uint8 as string seperated by spaces in the mqtt payload. | -| `8uint82ascii` | eight uint8 in the CAN-Frame to eight uint8 as string seperated by spaces in the mqtt payload. | -| `uint162ascii` | one uint16 in the CAN-Frame to one uint16 as string in the mqtt payload | -| `4uint162ascii` | four uint16 in the CAN-Frame to four uint16 as string seperated by spaces in the mqtt payload. | -| `uint322ascii` | one uint32 in the CAN-Frame to one uint32 as string in the mqtt payload | -| `2uint322ascii` | two uint32 in the CAN-Frame to two uint32 as string seperated by spaces in the mqtt payload. | -| `uint642ascii` | one uint64 in the CAN-Frame to one uint64 as string in the mqtt payload | -| *int* | | -| `int82ascii` | one int8 in the CAN-Frame to one int8 as string in the mqtt payload | -| `4int82ascii` | four int8 in the CAN-Frame to four int8 as string seperated by spaces in the mqtt payload. | -| `8int82ascii` | eight int8 in the CAN-Frame to eight int8 as string seperated by spaces in the mqtt payload. | -| `int162ascii` | one int16 in the CAN-Frame to one int16 as string in the mqtt payload | -| `4int162ascii` | four int16 in the CAN-Frame to four int16 as string seperated by spaces in the mqtt payload. | -| `int322ascii` | one int32 in the CAN-Frame to one int32 as string in the mqtt payload | -| `2int322ascii` | two int32 in the CAN-Frame to two int32 as string seperated by spaces in the mqtt payload. | -| `int642ascii` | one int64 in the CAN-Frame to one int64 as string in the mqtt payload | +| convertmode | description | +|-----------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `none` | does not convert anything. It just takes a bunch of bytes and hands it over to the other side. If you want to send strings, this will be your choice. If you have a mqtt payload that is longer than eight bytes, only the first eight bytes will be send via CAN. | +| `bytecolor2colorcode` | Converts an bytearray of 3 bytes to hexadecimal colorcode | +| `pixelbin2ascii` | This mode was designed to address colorized pixels. MQTT-wise you can insert a string like "<0-255> #RRGGBB" which will be converted to 4 byte on the CAN-BUS the first byte will be the number of the LED 0-255 and bytes 1, 2, 3 are the color of red, green and blue. | +| `16bool2ascii` | Interprets two bytes can-wise and publishes them as 16 boolean values to mqtt | +| `{i}[u]int{b}2ascii` | `i` is the amount of instances of numbers in the CAN-Frame/the MQTT-message. Valid instance amounts are currently 1,2,4 and 8. Although other combinations are possible. Might be added in the future, if the need arises. `b` is the size of each number in bits. Supported values are 8,16,32 and 64. You can use either one unsigned or signed integers. This are all possible combinations: `int82ascii`, `2int82ascii`, `4int82ascii`, `8int82ascii`, `int162ascii`, `2int162ascii`, `4int162ascii`, `int322ascii`, `2int322ascii`, `int642ascii`, `uint82ascii`, `2uint82ascii`, `4uint82ascii`, `8uint82ascii`, `uint162ascii`, `2uint162ascii`, `4uint162ascii`, `uint322ascii`, `2uint322ascii`, `uint642ascii` | +| ## Unidirectional Mode From d6ee08c1d24c5777d5672d4eed13b742f67eccf8 Mon Sep 17 00:00:00 2001 From: Malte Muench Date: Sun, 28 Jul 2024 23:53:17 +0200 Subject: [PATCH 14/16] Renamings, shorter, hopefully better understandable --- src/{canbus.go => can.go} | 0 .../bytecolor2colorcode.go | 2 +- .../int2ascii.go | 2 +- .../mymode.go | 2 +- src/{convertfunctions => convertmode}/none.go | 2 +- .../none_test.go | 4 ++-- .../pixelbin2ascii.go | 2 +- .../sixteenbool2ascii.go | 2 +- .../uint2ascii.go | 2 +- src/main.go | 18 +++++++++--------- src/{receiving.go => receive.go} | 0 11 files changed, 18 insertions(+), 18 deletions(-) rename src/{canbus.go => can.go} (100%) rename src/{convertfunctions => convertmode}/bytecolor2colorcode.go (97%) rename src/{convertfunctions => convertmode}/int2ascii.go (99%) rename src/{convertfunctions => convertmode}/mymode.go (98%) rename src/{convertfunctions => convertmode}/none.go (96%) rename src/{convertfunctions => convertmode}/none_test.go (99%) rename src/{convertfunctions => convertmode}/pixelbin2ascii.go (98%) rename src/{convertfunctions => convertmode}/sixteenbool2ascii.go (97%) rename src/{convertfunctions => convertmode}/uint2ascii.go (99%) rename src/{receiving.go => receive.go} (100%) diff --git a/src/canbus.go b/src/can.go similarity index 100% rename from src/canbus.go rename to src/can.go diff --git a/src/convertfunctions/bytecolor2colorcode.go b/src/convertmode/bytecolor2colorcode.go similarity index 97% rename from src/convertfunctions/bytecolor2colorcode.go rename to src/convertmode/bytecolor2colorcode.go index c402ef2..f2b44d9 100644 --- a/src/convertfunctions/bytecolor2colorcode.go +++ b/src/convertmode/bytecolor2colorcode.go @@ -1,4 +1,4 @@ -package convertfunctions +package convertmode import ( "encoding/hex" diff --git a/src/convertfunctions/int2ascii.go b/src/convertmode/int2ascii.go similarity index 99% rename from src/convertfunctions/int2ascii.go rename to src/convertmode/int2ascii.go index 3e8e11b..8f2d409 100644 --- a/src/convertfunctions/int2ascii.go +++ b/src/convertmode/int2ascii.go @@ -1,4 +1,4 @@ -package convertfunctions +package convertmode import ( "encoding/binary" diff --git a/src/convertfunctions/mymode.go b/src/convertmode/mymode.go similarity index 98% rename from src/convertfunctions/mymode.go rename to src/convertmode/mymode.go index 8123651..4ead25c 100644 --- a/src/convertfunctions/mymode.go +++ b/src/convertmode/mymode.go @@ -1,4 +1,4 @@ -package convertfunctions +package convertmode import ( "errors" diff --git a/src/convertfunctions/none.go b/src/convertmode/none.go similarity index 96% rename from src/convertfunctions/none.go rename to src/convertmode/none.go index 60fef6b..a5ca032 100644 --- a/src/convertfunctions/none.go +++ b/src/convertmode/none.go @@ -1,4 +1,4 @@ -package convertfunctions +package convertmode import "github.com/brutella/can" diff --git a/src/convertfunctions/none_test.go b/src/convertmode/none_test.go similarity index 99% rename from src/convertfunctions/none_test.go rename to src/convertmode/none_test.go index 5e8a215..c6d91a3 100644 --- a/src/convertfunctions/none_test.go +++ b/src/convertmode/none_test.go @@ -1,4 +1,4 @@ -package convertfunctions +package convertmode import ( "github.com/brutella/can" @@ -195,4 +195,4 @@ func FuzzNoneToMqtt(f *testing.F) { } }) -} +} \ No newline at end of file diff --git a/src/convertfunctions/pixelbin2ascii.go b/src/convertmode/pixelbin2ascii.go similarity index 98% rename from src/convertfunctions/pixelbin2ascii.go rename to src/convertmode/pixelbin2ascii.go index f3e4d42..0190fb3 100644 --- a/src/convertfunctions/pixelbin2ascii.go +++ b/src/convertmode/pixelbin2ascii.go @@ -1,4 +1,4 @@ -package convertfunctions +package convertmode import ( "encoding/hex" diff --git a/src/convertfunctions/sixteenbool2ascii.go b/src/convertmode/sixteenbool2ascii.go similarity index 97% rename from src/convertfunctions/sixteenbool2ascii.go rename to src/convertmode/sixteenbool2ascii.go index 03117d2..8322f80 100644 --- a/src/convertfunctions/sixteenbool2ascii.go +++ b/src/convertmode/sixteenbool2ascii.go @@ -1,4 +1,4 @@ -package convertfunctions +package convertmode import ( "errors" diff --git a/src/convertfunctions/uint2ascii.go b/src/convertmode/uint2ascii.go similarity index 99% rename from src/convertfunctions/uint2ascii.go rename to src/convertmode/uint2ascii.go index eabdaf4..80ff872 100644 --- a/src/convertfunctions/uint2ascii.go +++ b/src/convertmode/uint2ascii.go @@ -1,4 +1,4 @@ -package convertfunctions +package convertmode import ( "encoding/binary" diff --git a/src/main.go b/src/main.go index 5f2d40c..b55f12c 100644 --- a/src/main.go +++ b/src/main.go @@ -4,7 +4,7 @@ import ( "bufio" // Reader "encoding/csv" // CSV Management "flag" - "github.com/c3re/can2mqtt/convertfunctions" + "github.com/c3re/can2mqtt/convertmode" "io" // EOF const "log" // error management "log/slog" @@ -67,20 +67,20 @@ func readC2MPFromFile(filename string) { convertModeFromString = make(map[string]ConvertMode) // initialize all convertModes - convertModeFromString["none"] = convertfunctions.None{} - convertModeFromString["16bool2ascii"] = convertfunctions.SixteenBool2Ascii{} - convertModeFromString["pixelbin2ascii"] = convertfunctions.PixelBin2Ascii{} - convertModeFromString["bytecolor2colorcode"] = convertfunctions.ByteColor2ColorCode{} - convertModeFromString["mymode"] = convertfunctions.MyMode{} - // Dynamically create int and uint convertmodes + convertModeFromString["none"] = convertmode.None{} + convertModeFromString["16bool2ascii"] = convertmode.SixteenBool2Ascii{} + convertModeFromString["pixelbin2ascii"] = convertmode.PixelBin2Ascii{} + convertModeFromString["bytecolor2colorcode"] = convertmode.ByteColor2ColorCode{} + convertModeFromString["mymode"] = convertmode.MyMode{} + // Dynamically create int and uint convertmode for _, bits := range []uint{8, 16, 32, 64} { for _, instances := range []uint{1, 2, 4, 8} { if bits*instances <= 64 { // int - cmi, _ := convertfunctions.NewInt2Ascii(instances, bits) + cmi, _ := convertmode.NewInt2Ascii(instances, bits) convertModeFromString[cmi.String()] = cmi // uint - cmu, _ := convertfunctions.NewUint2Ascii(instances, bits) + cmu, _ := convertmode.NewUint2Ascii(instances, bits) convertModeFromString[cmu.String()] = cmu } } diff --git a/src/receiving.go b/src/receive.go similarity index 100% rename from src/receiving.go rename to src/receive.go From 9b99ac62bc7d77b18ff03ab61c5336e935df2f4c Mon Sep 17 00:00:00 2001 From: Malte Muench Date: Mon, 29 Jul 2024 11:34:55 +0200 Subject: [PATCH 15/16] Use String() of convertmodes to register convertmode --- src/main.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main.go b/src/main.go index b55f12c..0fa99aa 100644 --- a/src/main.go +++ b/src/main.go @@ -67,12 +67,12 @@ func readC2MPFromFile(filename string) { convertModeFromString = make(map[string]ConvertMode) // initialize all convertModes - convertModeFromString["none"] = convertmode.None{} - convertModeFromString["16bool2ascii"] = convertmode.SixteenBool2Ascii{} - convertModeFromString["pixelbin2ascii"] = convertmode.PixelBin2Ascii{} - convertModeFromString["bytecolor2colorcode"] = convertmode.ByteColor2ColorCode{} - convertModeFromString["mymode"] = convertmode.MyMode{} - // Dynamically create int and uint convertmode + convertModeFromString[convertmode.None{}.String()] = convertmode.None{} + convertModeFromString[convertmode.SixteenBool2Ascii{}.String()] = convertmode.SixteenBool2Ascii{} + convertModeFromString[convertmode.PixelBin2Ascii{}.String()] = convertmode.PixelBin2Ascii{} + convertModeFromString[convertmode.ByteColor2ColorCode{}.String()] = convertmode.ByteColor2ColorCode{} + convertModeFromString[convertmode.MyMode{}.String()] = convertmode.MyMode{} + // Dynamically create int and uint convertmodes for _, bits := range []uint{8, 16, 32, 64} { for _, instances := range []uint{1, 2, 4, 8} { if bits*instances <= 64 { From bc1da65d0eb06b12dda3c3442162d60d7ae0a2de Mon Sep 17 00:00:00 2001 From: Malte Muench Date: Mon, 29 Jul 2024 11:35:12 +0200 Subject: [PATCH 16/16] Update README --- README.md | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 197edee..abc49f5 100644 --- a/README.md +++ b/README.md @@ -85,13 +85,20 @@ sudo ip link set vcan0 up Now you can use your new interface `vcan0` as socket-can interface with can2mqtt. ## Debugging -To debug can2mqtts behaviour you need to be able to send and receive CAN frames and MQTT messages. For MQTT I recommend [mosquitto](https://mosquitto.org/) with its `mosquitto_pub` and `mosquitto_sub` commands. For CAN i recommend [can-utils]() with its tools `cansend` and `candump`. -## Add a convert-Mode +To debug the behaviour of can2mqtt you need to be able to send and receive CAN frames and MQTT messages. For MQTT I recommend [mosquitto](https://mosquitto.org/) with its `mosquitto_pub` and `mosquitto_sub` commands. For CAN i recommend [can-utils]() with its tools `cansend` and `candump`. +## Add a convert-Mode If you want to add a convert-Mode think about a name. This is the name that you can later refer to when you want to -use your convert-Mode in the `can2mqtt.csv` config-file. In this example the name of the new convert-Mode is `"mymode"`. -Next, add the new convert-Mode in the [switch-case block in `src/main.go`](./src/main.go#L247). In the new case you select your convertMode, in this case "convertfunctions.MyMode{}". This struct implements two functions. One is called when a MQTT-message is received and the other one when a CAN-frame is -received. Change the contents of those functions to the behaviour that you seek for. Mockup code for `(_ MyMode) ToCan` and -`(_ MyMode) ToMqtt` can be found in [mymode.go](./src/mymode.go). +use your convert-Mode in the `can2mqtt.csv` config-file. Now, use the file `src/convertmode/mymode.go` as a template for your own convertmode. Copy that file to `src/convertmode/.go`. Now change all occurrences of "MyMode" with your preferred Name (Lets say `YourNewMode` in this example). Note that it has to start with an upper-case letter, so that it is usable outside of this package. Next you have to write three functions (implement the `ConvertMode` interface): +1. A conversion method from CAN -> MQTT: `ToMqtt(input can.Frame) ([]byte, error)` +2. A conversion method from MQTT -> CAN: `ToCan(input []byte) (can.Frame, error)` +3. A `String() string` method that reports the name of that convertmode. This method is used in some log-messages + +Your almost done, the last step is to "register" your new convertmode. To do so add the following line to [`src/main.go#L72`](./src/main.go#L72) +```go +convertModeFromString[convertmode.{}.String()] = convertmode.{} +``` + +Now you can use your new convertmode in your `can2mqtt.csv` config File. Use the string that your return in the `String()` function as the name of the convertmode. In the `mymode.go` code this is `"mymode"`. Good luck & happy hacking ✌ \ No newline at end of file