Improved the ICCC
The ICCC was improved to increase the performance and the accuracy for floating point numbers. The efficiency of arrays is now increased caused by these optimisations. Important: The new implementation is not compatible with previous versions.
This commit is contained in:
parent
598f9b0ec0
commit
ae68804bac
@ -1,11 +1,15 @@
|
|||||||
package ICCC
|
package ICCC
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/SommerEngineering/Ocean/Log"
|
"github.com/SommerEngineering/Ocean/Log"
|
||||||
LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
"net/url"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Function to convert the HTTP data back to a message.
|
// Function to convert the HTTP data back to a message.
|
||||||
@ -49,37 +53,131 @@ func Data2Message(target interface{}, data map[string][]string) (channel, comman
|
|||||||
|
|
||||||
// Use the order of the destination type's fields:
|
// Use the order of the destination type's fields:
|
||||||
for i := 0; i < element.NumField(); i++ {
|
for i := 0; i < element.NumField(); i++ {
|
||||||
field := element.Field(i)
|
|
||||||
switch field.Kind().String() {
|
|
||||||
|
|
||||||
|
// Read the current field:
|
||||||
|
field := element.Field(i)
|
||||||
|
|
||||||
|
// Choose the right type for this field:
|
||||||
|
switch field.Kind().String() {
|
||||||
case `int64`:
|
case `int64`:
|
||||||
|
// The name of the field:
|
||||||
mapName := fmt.Sprintf(`int:%s`, elementType.Field(i).Name)
|
mapName := fmt.Sprintf(`int:%s`, elementType.Field(i).Name)
|
||||||
|
|
||||||
|
// The value of the field as string:
|
||||||
mapValue := data[mapName][0]
|
mapValue := data[mapName][0]
|
||||||
v, _ := strconv.ParseInt(mapValue, 10, 64)
|
|
||||||
field.SetInt(v)
|
// The value of the field as bytes:
|
||||||
|
bytesArray, errBase64 := base64.StdEncoding.DecodeString(mapValue)
|
||||||
|
|
||||||
|
if errBase64 != nil {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNamePARSE, `Was not able to decode the base64 data to an ICCC message.`, fmt.Sprintf("channel='%s'", channel), fmt.Sprintf("command='%s'", command), errBase64.Error())
|
||||||
|
} else {
|
||||||
|
// The destination:
|
||||||
|
var destination int64
|
||||||
|
|
||||||
|
// A reader for the bytes:
|
||||||
|
buffer := bytes.NewReader(bytesArray)
|
||||||
|
|
||||||
|
// Try to decode the bytes to an instance of the type:
|
||||||
|
errBinary := binary.Read(buffer, binary.LittleEndian, &destination)
|
||||||
|
if errBinary != nil {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNamePARSE, `Was not able to decode the binary data to the type of the ICCC message.`, fmt.Sprintf("channel='%s'", channel), fmt.Sprintf("command='%s'", command), errBinary.Error())
|
||||||
|
} else {
|
||||||
|
// Finnaly, store the value in the message:
|
||||||
|
field.SetInt(destination)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case `string`:
|
case `string`:
|
||||||
|
// The name of the field:
|
||||||
mapName := fmt.Sprintf(`str:%s`, elementType.Field(i).Name)
|
mapName := fmt.Sprintf(`str:%s`, elementType.Field(i).Name)
|
||||||
|
|
||||||
|
// The value of the field as string:
|
||||||
mapValue := data[mapName][0]
|
mapValue := data[mapName][0]
|
||||||
field.SetString(mapValue)
|
|
||||||
|
// The value of the field as bytes:
|
||||||
|
bytesArray, errBase64 := base64.StdEncoding.DecodeString(mapValue)
|
||||||
|
|
||||||
|
if errBase64 != nil {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNamePARSE, `Was not able to decode the base64 data to an ICCC message.`, fmt.Sprintf("channel='%s'", channel), fmt.Sprintf("command='%s'", command), errBase64.Error())
|
||||||
|
} else {
|
||||||
|
// Decode the bytes as string:
|
||||||
|
text := string(bytesArray)
|
||||||
|
|
||||||
|
// Decode the URL encoded string:
|
||||||
|
textFinal, errURL := url.QueryUnescape(text)
|
||||||
|
if errURL != nil {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNamePARSE, `Was not able to decode a URL encoded string.`, fmt.Sprintf("channel='%s'", channel), fmt.Sprintf("command='%s'", command), errURL.Error())
|
||||||
|
} else {
|
||||||
|
field.SetString(textFinal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case `float64`:
|
case `float64`:
|
||||||
|
// The name of the field:
|
||||||
mapName := fmt.Sprintf(`f64:%s`, elementType.Field(i).Name)
|
mapName := fmt.Sprintf(`f64:%s`, elementType.Field(i).Name)
|
||||||
|
|
||||||
|
// The value of the field as string:
|
||||||
mapValue := data[mapName][0]
|
mapValue := data[mapName][0]
|
||||||
v, _ := strconv.ParseFloat(mapValue, 64)
|
|
||||||
field.SetFloat(v)
|
// The value of the field as bytes:
|
||||||
|
bytesArray, errBase64 := base64.StdEncoding.DecodeString(mapValue)
|
||||||
|
|
||||||
|
if errBase64 != nil {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNamePARSE, `Was not able to decode the base64 data to an ICCC message.`, fmt.Sprintf("channel='%s'", channel), fmt.Sprintf("command='%s'", command), errBase64.Error())
|
||||||
|
} else {
|
||||||
|
// The destination:
|
||||||
|
var destination float64
|
||||||
|
|
||||||
|
// A reader for the bytes:
|
||||||
|
buffer := bytes.NewReader(bytesArray)
|
||||||
|
|
||||||
|
// Try to decode the bytes to an instance of the type:
|
||||||
|
errBinary := binary.Read(buffer, binary.LittleEndian, &destination)
|
||||||
|
if errBinary != nil {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNamePARSE, `Was not able to decode the binary data to the type of the ICCC message.`, fmt.Sprintf("channel='%s'", channel), fmt.Sprintf("command='%s'", command), errBinary.Error())
|
||||||
|
} else {
|
||||||
|
// Finnaly, store the value in the message:
|
||||||
|
field.SetFloat(destination)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case `bool`:
|
case `bool`:
|
||||||
|
// The name of the field:
|
||||||
mapName := fmt.Sprintf(`bool:%s`, elementType.Field(i).Name)
|
mapName := fmt.Sprintf(`bool:%s`, elementType.Field(i).Name)
|
||||||
|
|
||||||
|
// The value of the field as string:
|
||||||
mapValue := data[mapName][0]
|
mapValue := data[mapName][0]
|
||||||
v, _ := strconv.ParseBool(mapValue)
|
|
||||||
field.SetBool(v)
|
// The value of the field as bytes:
|
||||||
|
bytesArray, errBase64 := base64.StdEncoding.DecodeString(mapValue)
|
||||||
|
if errBase64 != nil {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNamePARSE, `Was not able to decode the base64 data to an ICCC message.`, fmt.Sprintf("channel='%s'", channel), fmt.Sprintf("command='%s'", command), errBase64.Error())
|
||||||
|
} else {
|
||||||
|
// Store the value:
|
||||||
|
if bytesArray[0] == 0x1 {
|
||||||
|
field.SetBool(true)
|
||||||
|
} else {
|
||||||
|
field.SetBool(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case `uint8`:
|
case `uint8`:
|
||||||
|
// The name of the field:
|
||||||
mapName := fmt.Sprintf(`ui8:%s`, elementType.Field(i).Name)
|
mapName := fmt.Sprintf(`ui8:%s`, elementType.Field(i).Name)
|
||||||
|
|
||||||
|
// The value of the field as string:
|
||||||
mapValue := data[mapName][0]
|
mapValue := data[mapName][0]
|
||||||
v, _ := strconv.ParseUint(mapValue, 16, 8)
|
|
||||||
field.SetUint(v)
|
// The value of the field as bytes:
|
||||||
|
bytesArray, errBase64 := base64.StdEncoding.DecodeString(mapValue)
|
||||||
|
|
||||||
|
if errBase64 != nil {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNamePARSE, `Was not able to decode the base64 data to an ICCC message.`, fmt.Sprintf("channel='%s'", channel), fmt.Sprintf("command='%s'", command), errBase64.Error())
|
||||||
|
} else {
|
||||||
|
// Store the value:
|
||||||
|
field.SetUint(uint64(bytesArray[0]))
|
||||||
|
}
|
||||||
|
|
||||||
// Case: Arrays...
|
// Case: Arrays...
|
||||||
case `slice`:
|
case `slice`:
|
||||||
@ -87,64 +185,147 @@ func Data2Message(target interface{}, data map[string][]string) (channel, comman
|
|||||||
sliceKind := reflect.ValueOf(sliceInterface).Type().String()
|
sliceKind := reflect.ValueOf(sliceInterface).Type().String()
|
||||||
|
|
||||||
switch sliceKind {
|
switch sliceKind {
|
||||||
case `[]uint8`: // bytes
|
case `[]uint8`: // a byte array
|
||||||
|
// The name of the field:
|
||||||
mapName := fmt.Sprintf(`ui8[]:%s`, elementType.Field(i).Name)
|
mapName := fmt.Sprintf(`ui8[]:%s`, elementType.Field(i).Name)
|
||||||
mapValues := data[mapName]
|
|
||||||
fieldLen := len(mapValues)
|
|
||||||
fieldData := make([]uint8, fieldLen, fieldLen)
|
|
||||||
for n, mapValue := range mapValues {
|
|
||||||
v, _ := strconv.ParseUint(mapValue, 16, 8)
|
|
||||||
fieldData[n] = byte(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
fieldDataValue := reflect.ValueOf(fieldData)
|
// The value of the field as string:
|
||||||
|
mapValue := data[mapName][0]
|
||||||
|
|
||||||
|
// The value of the field as bytes:
|
||||||
|
bytesArray, errBase64 := base64.StdEncoding.DecodeString(mapValue)
|
||||||
|
|
||||||
|
if errBase64 != nil {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNamePARSE, `Was not able to decode the base64 data to an ICCC message.`, fmt.Sprintf("channel='%s'", channel), fmt.Sprintf("command='%s'", command), errBase64.Error())
|
||||||
|
} else {
|
||||||
|
// Store the values in the message:
|
||||||
|
fieldDataValue := reflect.ValueOf(bytesArray)
|
||||||
field.Set(fieldDataValue)
|
field.Set(fieldDataValue)
|
||||||
|
}
|
||||||
|
|
||||||
case `[]int64`:
|
case `[]int64`:
|
||||||
|
// The name of the field:
|
||||||
mapName := fmt.Sprintf(`int[]:%s`, elementType.Field(i).Name)
|
mapName := fmt.Sprintf(`int[]:%s`, elementType.Field(i).Name)
|
||||||
mapValues := data[mapName]
|
|
||||||
fieldLen := len(mapValues)
|
|
||||||
fieldData := make([]int64, fieldLen, fieldLen)
|
|
||||||
for n, mapValue := range mapValues {
|
|
||||||
v, _ := strconv.ParseInt(mapValue, 10, 64)
|
|
||||||
fieldData[n] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
fieldDataValue := reflect.ValueOf(fieldData)
|
// The value of the field as string:
|
||||||
|
mapValue := data[mapName][0]
|
||||||
|
|
||||||
|
// The value of the field as bytes:
|
||||||
|
bytesArray, errBase64 := base64.StdEncoding.DecodeString(mapValue)
|
||||||
|
if errBase64 != nil {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNamePARSE, `Was not able to decode the base64 data to an ICCC message.`, fmt.Sprintf("channel='%s'", channel), fmt.Sprintf("command='%s'", command), errBase64.Error())
|
||||||
|
} else {
|
||||||
|
// The destination:
|
||||||
|
var destination []int64
|
||||||
|
|
||||||
|
// A reader for the bytes:
|
||||||
|
buffer := bytes.NewReader(bytesArray)
|
||||||
|
|
||||||
|
// Try to decode the bytes to an instance of the type:
|
||||||
|
errBinary := binary.Read(buffer, binary.LittleEndian, &destination)
|
||||||
|
if errBinary != nil {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNamePARSE, `Was not able to decode the binary data to the type of the ICCC message.`, fmt.Sprintf("channel='%s'", channel), fmt.Sprintf("command='%s'", command), errBinary.Error())
|
||||||
|
} else {
|
||||||
|
// Finnaly, store the value in the message:
|
||||||
|
fieldDataValue := reflect.ValueOf(destination)
|
||||||
field.Set(fieldDataValue)
|
field.Set(fieldDataValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case `[]bool`:
|
case `[]bool`:
|
||||||
|
// The name of the field:
|
||||||
mapName := fmt.Sprintf(`bool[]:%s`, elementType.Field(i).Name)
|
mapName := fmt.Sprintf(`bool[]:%s`, elementType.Field(i).Name)
|
||||||
mapValues := data[mapName]
|
|
||||||
fieldLen := len(mapValues)
|
// The value of the field as string:
|
||||||
|
mapValue := data[mapName][0]
|
||||||
|
|
||||||
|
// The value of the field as bytes:
|
||||||
|
bytesArray, errBase64 := base64.StdEncoding.DecodeString(mapValue)
|
||||||
|
if errBase64 != nil {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNamePARSE, `Was not able to decode the base64 data to an ICCC message.`, fmt.Sprintf("channel='%s'", channel), fmt.Sprintf("command='%s'", command), errBase64.Error())
|
||||||
|
} else {
|
||||||
|
fieldLen := len(bytesArray)
|
||||||
fieldData := make([]bool, fieldLen, fieldLen)
|
fieldData := make([]bool, fieldLen, fieldLen)
|
||||||
for n, mapValue := range mapValues {
|
|
||||||
v, _ := strconv.ParseBool(mapValue)
|
// Convert each byte in a bool:
|
||||||
fieldData[n] = v
|
for n, value := range bytesArray {
|
||||||
|
if value == 0x1 {
|
||||||
|
fieldData[n] = true
|
||||||
|
} else {
|
||||||
|
fieldData[n] = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Store the values in the message:
|
||||||
fieldDataValue := reflect.ValueOf(fieldData)
|
fieldDataValue := reflect.ValueOf(fieldData)
|
||||||
field.Set(fieldDataValue)
|
field.Set(fieldDataValue)
|
||||||
|
}
|
||||||
|
|
||||||
case `[]string`:
|
case `[]string`:
|
||||||
|
// The name of the field:
|
||||||
mapName := fmt.Sprintf(`str[]:%s`, elementType.Field(i).Name)
|
mapName := fmt.Sprintf(`str[]:%s`, elementType.Field(i).Name)
|
||||||
mapValues := data[mapName]
|
|
||||||
fieldDataValue := reflect.ValueOf(mapValues)
|
|
||||||
field.Set(fieldDataValue)
|
|
||||||
|
|
||||||
case `[]float64`:
|
// The value of the field as string:
|
||||||
mapName := fmt.Sprintf(`f64[]:%s`, elementType.Field(i).Name)
|
mapValue := data[mapName][0]
|
||||||
mapValues := data[mapName]
|
|
||||||
fieldLen := len(mapValues)
|
// The value of the field as bytes:
|
||||||
fieldData := make([]float64, fieldLen, fieldLen)
|
bytesArray, errBase64 := base64.StdEncoding.DecodeString(mapValue)
|
||||||
for n, mapValue := range mapValues {
|
if errBase64 != nil {
|
||||||
v, _ := strconv.ParseFloat(mapValue, 64)
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNamePARSE, `Was not able to decode the base64 data to an ICCC message.`, fmt.Sprintf("channel='%s'", channel), fmt.Sprintf("command='%s'", command), errBase64.Error())
|
||||||
fieldData[n] = v
|
} else {
|
||||||
|
// Get the URL encoded string of all values:
|
||||||
|
allStringsRAW := string(bytesArray)
|
||||||
|
|
||||||
|
// Split now the different strings:
|
||||||
|
allStrings := strings.Split(allStringsRAW, "\n")
|
||||||
|
|
||||||
|
// A place where we store the final strings:
|
||||||
|
data := make([]string, len(allStrings), len(allStrings))
|
||||||
|
|
||||||
|
// Loop over all URL encoded strings and decode it:
|
||||||
|
for n, element := range allStrings {
|
||||||
|
elementFinal, errURL := url.QueryUnescape(element)
|
||||||
|
if errURL != nil {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNamePARSE, `Was not able to decode a base64 string for a string array.`, fmt.Sprintf("channel='%s'", channel), fmt.Sprintf("command='%s'", command), errURL.Error())
|
||||||
|
} else {
|
||||||
|
data[n] = elementFinal
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fieldDataValue := reflect.ValueOf(fieldData)
|
// Store the values in the message:
|
||||||
|
fieldDataValue := reflect.ValueOf(data)
|
||||||
field.Set(fieldDataValue)
|
field.Set(fieldDataValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case `[]float64`:
|
||||||
|
// The name of the field:
|
||||||
|
mapName := fmt.Sprintf(`f64[]:%s`, elementType.Field(i).Name)
|
||||||
|
|
||||||
|
// The value of the field as string:
|
||||||
|
mapValue := data[mapName][0]
|
||||||
|
|
||||||
|
// The value of the field as bytes:
|
||||||
|
bytesArray, errBase64 := base64.StdEncoding.DecodeString(mapValue)
|
||||||
|
if errBase64 != nil {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNamePARSE, `Was not able to decode the base64 data to an ICCC message.`, fmt.Sprintf("channel='%s'", channel), fmt.Sprintf("command='%s'", command), errBase64.Error())
|
||||||
|
} else {
|
||||||
|
// The destination:
|
||||||
|
var destination []float64
|
||||||
|
|
||||||
|
// A reader for the bytes:
|
||||||
|
buffer := bytes.NewReader(bytesArray)
|
||||||
|
|
||||||
|
// Try to decode the bytes to an instance of the type:
|
||||||
|
errBinary := binary.Read(buffer, binary.LittleEndian, &destination)
|
||||||
|
if errBinary != nil {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNamePARSE, `Was not able to decode the binary data to the type of the ICCC message.`, fmt.Sprintf("channel='%s'", channel), fmt.Sprintf("command='%s'", command), errBinary.Error())
|
||||||
|
} else {
|
||||||
|
// Finnaly, store the value in the message:
|
||||||
|
fieldDataValue := reflect.ValueOf(destination)
|
||||||
|
field.Set(fieldDataValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
27
ICCC/Doc.go
27
ICCC/Doc.go
@ -7,9 +7,9 @@ To be able to marshal / parse the data back to objects, some additional informat
|
|||||||
|
|
||||||
Example 01:
|
Example 01:
|
||||||
name=str:Surname
|
name=str:Surname
|
||||||
value=Sommer
|
value=U29tbWVy
|
||||||
|
|
||||||
The HTTP form name is 'str:Surname' and the value is 'Sommer'. The 'str' is the indicator for the data type, in this case it is a string.
|
The HTTP form name is 'str:Surname' and the value is 'Sommer' as base64 encoded string. The 'str' is the indicator for the data type, in this case it is a string.
|
||||||
|
|
||||||
Known data types are:
|
Known data types are:
|
||||||
* str := string
|
* str := string
|
||||||
@ -23,18 +23,17 @@ Known data types are:
|
|||||||
* str[] := string array
|
* str[] := string array
|
||||||
* f64[] := 64 bit float array
|
* f64[] := 64 bit float array
|
||||||
|
|
||||||
Formatting of the corresponding values (each value is a string => HTTP). Plase note:
|
Formatting of the corresponding values (each value is at the end a base64 string).
|
||||||
For the arrays, the name will repeated for each value.
|
* str := the plain UTF8 string as URL encoded. These bytes are getting base64 encoded.
|
||||||
* str := the plain UTF8 string
|
* int := the little endian representation of the int. These bytes are getting base64 encoded.
|
||||||
* int := the integer e.g. '13894612'
|
* f64 := the little endian representation of the float. These bytes are getting base64 encoded.
|
||||||
* f64 := the float with nine digits e.g. 9.48 gets '9.480000000'
|
* bool := the byte 0x1 or 0x0 for true and false. These byte will be base64 encoded.
|
||||||
* bool := 'true' or 'false' (lower case)
|
* ui8 := These byte will be base64 encoded.
|
||||||
* ui8 := the byte as hexadecimal string e.g. 255 gets 'ff'
|
* ui8[] := These bytes are getting base64 encoded.
|
||||||
* ui8[] := the bytes as hexdecimal strings e.g. 0 255 0 gets ui8[]:name:00 ui8[]:name:ff ui8[]:name:00
|
* int[] := the little endian representation of the integers. These bytes are getting base64 encoded.
|
||||||
* int[] := 64 bit integer array e.g. 1 2 gets int[]:name:1 int[]:name:2
|
* bool[] := the bools are getting converted to bytes (0x1 or 0x0 for true and false). These bytes are getting base64 encoded.
|
||||||
* bool[] := a boolean array e.g. true true gets bool[]:name:true bool[]:name:true
|
* str[] := each string will be URL encoded. Afterwards, join all strings by \n. These bytes are getting base64 encoded.
|
||||||
* str[] := string array e.g. 'a' 'abc' gets str[]:name:a str[]:name:abc
|
* f64[] := the little endian representation of the floats. These bytes are getting base64 encoded.
|
||||||
* f64[] := 64 bit float array e.g. 1.1 1.2 gets f64[]:name:1.100000000 f64[]:name:1.2000000000
|
|
||||||
|
|
||||||
The format of a message is:
|
The format of a message is:
|
||||||
command=COMMAND
|
command=COMMAND
|
||||||
|
@ -1,11 +1,16 @@
|
|||||||
package ICCC
|
package ICCC
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/SommerEngineering/Ocean/Log"
|
"github.com/SommerEngineering/Ocean/Log"
|
||||||
LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
"io"
|
||||||
|
"net/url"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Function to convert an ICCC message to HTTP data.
|
// Function to convert an ICCC message to HTTP data.
|
||||||
@ -39,74 +44,133 @@ func Message2Data(channel, command string, message interface{}) (data map[string
|
|||||||
field := element.Field(i)
|
field := element.Field(i)
|
||||||
keyName := elementType.Field(i).Name
|
keyName := elementType.Field(i).Name
|
||||||
|
|
||||||
|
// A buffer for the binary representation:
|
||||||
|
buffer := new(bytes.Buffer)
|
||||||
|
|
||||||
|
// For possible errors:
|
||||||
|
var errConverter error = nil
|
||||||
|
|
||||||
|
// The key for this element:
|
||||||
|
key := ``
|
||||||
|
|
||||||
|
// Look for the right data type:
|
||||||
switch field.Kind().String() {
|
switch field.Kind().String() {
|
||||||
|
|
||||||
case `int64`:
|
case `int64`:
|
||||||
key := fmt.Sprintf(`int:%s`, keyName)
|
key = fmt.Sprintf(`int:%s`, keyName)
|
||||||
data[key] = []string{strconv.FormatInt(field.Int(), 10)}
|
|
||||||
|
// Converts the value in a byte array:
|
||||||
|
errConverter = binary.Write(buffer, binary.LittleEndian, field.Int())
|
||||||
|
|
||||||
case `string`:
|
case `string`:
|
||||||
key := fmt.Sprintf(`str:%s`, keyName)
|
key = fmt.Sprintf(`str:%s`, keyName)
|
||||||
data[key] = []string{field.String()}
|
|
||||||
|
// URL encode the string and copy its bytes to the buffer:
|
||||||
|
io.Copy(buffer, strings.NewReader(url.QueryEscape(field.String())))
|
||||||
|
|
||||||
case `float64`:
|
case `float64`:
|
||||||
key := fmt.Sprintf(`f64:%s`, keyName)
|
key = fmt.Sprintf(`f64:%s`, keyName)
|
||||||
data[key] = []string{strconv.FormatFloat(field.Float(), 'f', 9, 64)}
|
|
||||||
|
// Converts the value in a byte array:
|
||||||
|
errConverter = binary.Write(buffer, binary.LittleEndian, field.Float())
|
||||||
|
|
||||||
case `bool`:
|
case `bool`:
|
||||||
key := fmt.Sprintf(`bool:%s`, keyName)
|
key = fmt.Sprintf(`bool:%s`, keyName)
|
||||||
data[key] = []string{strconv.FormatBool(field.Bool())}
|
|
||||||
|
|
||||||
case `uint8`: // byte
|
// Directly convert the bool in a byte:
|
||||||
key := fmt.Sprintf(`ui8:%s`, keyName)
|
if field.Bool() {
|
||||||
data[key] = []string{strconv.FormatUint(field.Uint(), 16)}
|
// Case: True
|
||||||
|
buffer.WriteByte(0x1) // Write 1
|
||||||
|
} else {
|
||||||
|
// Case: False
|
||||||
|
buffer.WriteByte(0x0) // Write 0
|
||||||
|
}
|
||||||
|
|
||||||
|
case `uint8`: // a byte
|
||||||
|
key = fmt.Sprintf(`ui8:%s`, keyName)
|
||||||
|
|
||||||
|
// uint8 is a byte, thus, write it directly in the buffer:
|
||||||
|
buffer.WriteByte(byte(field.Uint()))
|
||||||
|
|
||||||
// Case: Arrays...
|
// Case: Arrays...
|
||||||
case `slice`:
|
case `slice`:
|
||||||
sliceLen := field.Len()
|
sliceLen := field.Len()
|
||||||
if sliceLen > 0 {
|
if sliceLen > 0 {
|
||||||
|
|
||||||
|
// Which kind of data is this?
|
||||||
sliceKind := field.Index(0).Kind()
|
sliceKind := field.Index(0).Kind()
|
||||||
key := ``
|
|
||||||
dataValues := make([]string, sliceLen, sliceLen)
|
// Select the right data type:
|
||||||
switch sliceKind.String() {
|
switch sliceKind.String() {
|
||||||
case `uint8`: // bytes
|
case `uint8`: // a byte array
|
||||||
key = fmt.Sprintf(`ui8[]:%s`, keyName)
|
key = fmt.Sprintf(`ui8[]:%s`, keyName)
|
||||||
values := field.Interface().([]uint8)
|
values := field.Interface().([]uint8)
|
||||||
for index, value := range values {
|
|
||||||
dataValues[index] = strconv.FormatUint(uint64(value), 16)
|
// Directly write the bytes in the buffer:
|
||||||
|
for _, val := range values {
|
||||||
|
buffer.WriteByte(byte(val))
|
||||||
}
|
}
|
||||||
|
|
||||||
case `int64`:
|
case `int64`:
|
||||||
key = fmt.Sprintf(`int[]:%s`, keyName)
|
key = fmt.Sprintf(`int[]:%s`, keyName)
|
||||||
values := field.Interface().([]int64)
|
values := field.Interface().([]int64)
|
||||||
for index, value := range values {
|
|
||||||
dataValues[index] = strconv.FormatInt(value, 10)
|
// Converts the array in a byte array:
|
||||||
}
|
errConverter = binary.Write(buffer, binary.LittleEndian, values)
|
||||||
|
|
||||||
case `bool`:
|
case `bool`:
|
||||||
key = fmt.Sprintf(`bool[]:%s`, keyName)
|
key = fmt.Sprintf(`bool[]:%s`, keyName)
|
||||||
values := field.Interface().([]bool)
|
values := field.Interface().([]bool)
|
||||||
for index, value := range values {
|
|
||||||
dataValues[index] = strconv.FormatBool(value)
|
// Cannot convert bool to bytes by using binary.Write(). Thus,
|
||||||
|
// convert it by ower own:
|
||||||
|
|
||||||
|
// Loop over all values:
|
||||||
|
for _, val := range values {
|
||||||
|
if val {
|
||||||
|
// If the value is true:
|
||||||
|
buffer.WriteByte(0x1) // Write 1
|
||||||
|
} else {
|
||||||
|
// If the value is false:
|
||||||
|
buffer.WriteByte(0x0) // Write 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case `string`:
|
case `string`:
|
||||||
key = fmt.Sprintf(`str[]:%s`, keyName)
|
key = fmt.Sprintf(`str[]:%s`, keyName)
|
||||||
values := field.Interface().([]string)
|
values := field.Interface().([]string)
|
||||||
for index, value := range values {
|
|
||||||
dataValues[index] = value
|
// Mask every string by using a URL encoding.
|
||||||
|
// This masks e.g. every new-line i.e \n, etc.
|
||||||
|
// This allows us to combine later the strings by
|
||||||
|
// using \n:
|
||||||
|
masked := make([]string, len(values))
|
||||||
|
|
||||||
|
// Loop over each string and convert it:
|
||||||
|
for n, val := range values {
|
||||||
|
masked[n] = url.QueryEscape(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Join all masked strings by using \n and copy the byte array
|
||||||
|
// representation in the buffer:
|
||||||
|
io.Copy(buffer, strings.NewReader(strings.Join(masked, "\n")))
|
||||||
|
|
||||||
case `float64`:
|
case `float64`:
|
||||||
key = fmt.Sprintf(`f64[]:%s`, keyName)
|
key = fmt.Sprintf(`f64[]:%s`, keyName)
|
||||||
values := field.Interface().([]float64)
|
values := field.Interface().([]float64)
|
||||||
for index, value := range values {
|
|
||||||
dataValues[index] = strconv.FormatFloat(value, 'f', 9, 64)
|
// Converts the array in a byte array:
|
||||||
|
errConverter = binary.Write(buffer, binary.LittleEndian, values)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data[key] = dataValues
|
if errConverter != nil {
|
||||||
}
|
// An error occurs:
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNamePARSE, `It was not possible to convert an array for an ICCC message.`, fmt.Sprintf("channel='%s'", channel), fmt.Sprintf("command='%s'", command), errConverter.Error())
|
||||||
|
} else {
|
||||||
|
// Convert the byte array to a base64 string for the transportation on wire:
|
||||||
|
data[key] = []string{base64.StdEncoding.EncodeToString(buffer.Bytes())}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user