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.
335 lines
13 KiB
Go
335 lines
13 KiB
Go
package ICCC
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/base64"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"github.com/SommerEngineering/Ocean/Log"
|
|
LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
|
"net/url"
|
|
"reflect"
|
|
"strings"
|
|
)
|
|
|
|
// Function to convert the HTTP data back to a message.
|
|
func Data2Message(target interface{}, data map[string][]string) (channel, command string, obj interface{}) {
|
|
defer func() {
|
|
if err := recover(); err != nil {
|
|
channel = ``
|
|
command = ``
|
|
obj = nil
|
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNamePARSE, fmt.Sprintf("Was not able to convert the HTTP values to a message. %s", err))
|
|
return
|
|
}
|
|
}()
|
|
|
|
if data == nil || len(data) == 0 {
|
|
channel = ``
|
|
command = ``
|
|
obj = nil
|
|
return
|
|
}
|
|
|
|
// By using reflection, determine the type:
|
|
elementType := reflect.TypeOf(target)
|
|
|
|
// Is it a pointer?
|
|
if elementType.Kind() == reflect.Ptr {
|
|
// Get the value behind the pointer:
|
|
elementType = elementType.Elem()
|
|
}
|
|
|
|
// ICCC works with structs! If this is not a struct, its an error.
|
|
if elementType.Kind() != reflect.Struct {
|
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityMiddle, LM.ImpactMiddle, LM.MessageNamePARSE, `Was not able to transform HTTP data to a message, because the given data was not a struct.`)
|
|
return
|
|
}
|
|
|
|
// By using reflection, create a new instance:
|
|
element := reflect.New(elementType).Elem()
|
|
channel = data[`channel`][0]
|
|
command = data[`command`][0]
|
|
|
|
// Use the order of the destination type's fields:
|
|
for i := 0; i < element.NumField(); i++ {
|
|
|
|
// Read the current field:
|
|
field := element.Field(i)
|
|
|
|
// Choose the right type for this field:
|
|
switch field.Kind().String() {
|
|
case `int64`:
|
|
// The name of the field:
|
|
mapName := fmt.Sprintf(`int:%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 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`:
|
|
// The name of the field:
|
|
mapName := fmt.Sprintf(`str:%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 {
|
|
// 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`:
|
|
// 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:
|
|
field.SetFloat(destination)
|
|
}
|
|
}
|
|
|
|
case `bool`:
|
|
// The name of the field:
|
|
mapName := fmt.Sprintf(`bool:%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 {
|
|
// Store the value:
|
|
if bytesArray[0] == 0x1 {
|
|
field.SetBool(true)
|
|
} else {
|
|
field.SetBool(false)
|
|
}
|
|
}
|
|
|
|
case `uint8`:
|
|
// The name of the field:
|
|
mapName := fmt.Sprintf(`ui8:%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 {
|
|
// Store the value:
|
|
field.SetUint(uint64(bytesArray[0]))
|
|
}
|
|
|
|
// Case: Arrays...
|
|
case `slice`:
|
|
sliceInterface := field.Interface()
|
|
sliceKind := reflect.ValueOf(sliceInterface).Type().String()
|
|
|
|
switch sliceKind {
|
|
case `[]uint8`: // a byte array
|
|
// The name of the field:
|
|
mapName := fmt.Sprintf(`ui8[]:%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 {
|
|
// Store the values in the message:
|
|
fieldDataValue := reflect.ValueOf(bytesArray)
|
|
field.Set(fieldDataValue)
|
|
}
|
|
|
|
case `[]int64`:
|
|
// The name of the field:
|
|
mapName := fmt.Sprintf(`int[]:%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 []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)
|
|
}
|
|
}
|
|
|
|
case `[]bool`:
|
|
// The name of the field:
|
|
mapName := fmt.Sprintf(`bool[]:%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 {
|
|
fieldLen := len(bytesArray)
|
|
fieldData := make([]bool, fieldLen, fieldLen)
|
|
|
|
// Convert each byte in a bool:
|
|
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)
|
|
field.Set(fieldDataValue)
|
|
}
|
|
|
|
case `[]string`:
|
|
// The name of the field:
|
|
mapName := fmt.Sprintf(`str[]:%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 {
|
|
// 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
|
|
}
|
|
}
|
|
|
|
// Store the values in the message:
|
|
fieldDataValue := reflect.ValueOf(data)
|
|
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)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
obj = element.Interface()
|
|
return
|
|
}
|