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
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/base64"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/SommerEngineering/Ocean/Log"
 | 
			
		||||
	LM "github.com/SommerEngineering/Ocean/Log/Meta"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// 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:
 | 
			
		||||
	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`:
 | 
			
		||||
			// 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]
 | 
			
		||||
			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`:
 | 
			
		||||
			// 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]
 | 
			
		||||
			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`:
 | 
			
		||||
			// 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]
 | 
			
		||||
			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`:
 | 
			
		||||
			// 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]
 | 
			
		||||
			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`:
 | 
			
		||||
			// 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]
 | 
			
		||||
			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 `slice`:
 | 
			
		||||
@ -87,64 +185,147 @@ func Data2Message(target interface{}, data map[string][]string) (channel, comman
 | 
			
		||||
			sliceKind := reflect.ValueOf(sliceInterface).Type().String()
 | 
			
		||||
 | 
			
		||||
			switch sliceKind {
 | 
			
		||||
			case `[]uint8`: // bytes
 | 
			
		||||
			case `[]uint8`: // a byte array
 | 
			
		||||
				// The name of the field:
 | 
			
		||||
				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)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			case `[]int64`:
 | 
			
		||||
				// The name of the field:
 | 
			
		||||
				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)
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			case `[]bool`:
 | 
			
		||||
				// The name of the field:
 | 
			
		||||
				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)
 | 
			
		||||
				for n, mapValue := range mapValues {
 | 
			
		||||
					v, _ := strconv.ParseBool(mapValue)
 | 
			
		||||
					fieldData[n] = v
 | 
			
		||||
 | 
			
		||||
					// 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)
 | 
			
		||||
				mapValues := data[mapName]
 | 
			
		||||
				fieldDataValue := reflect.ValueOf(mapValues)
 | 
			
		||||
				field.Set(fieldDataValue)
 | 
			
		||||
 | 
			
		||||
			case `[]float64`:
 | 
			
		||||
				mapName := fmt.Sprintf(`f64[]:%s`, elementType.Field(i).Name)
 | 
			
		||||
				mapValues := data[mapName]
 | 
			
		||||
				fieldLen := len(mapValues)
 | 
			
		||||
				fieldData := make([]float64, fieldLen, fieldLen)
 | 
			
		||||
				for n, mapValue := range mapValues {
 | 
			
		||||
					v, _ := strconv.ParseFloat(mapValue, 64)
 | 
			
		||||
					fieldData[n] = v
 | 
			
		||||
				// 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
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
				fieldDataValue := reflect.ValueOf(fieldData)
 | 
			
		||||
					// 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)
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										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:
 | 
			
		||||
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:
 | 
			
		||||
* str := string
 | 
			
		||||
@ -23,18 +23,17 @@ Known data types are:
 | 
			
		||||
* str[] := string array
 | 
			
		||||
* f64[] := 64 bit float array
 | 
			
		||||
 | 
			
		||||
Formatting of the corresponding values (each value is a string => HTTP). Plase note:
 | 
			
		||||
For the arrays, the name will repeated for each value.
 | 
			
		||||
* str := the plain UTF8 string
 | 
			
		||||
* int := the integer e.g. '13894612'
 | 
			
		||||
* f64 := the float with nine digits e.g. 9.48 gets '9.480000000'
 | 
			
		||||
* bool := 'true' or 'false' (lower case)
 | 
			
		||||
* ui8 := the byte as hexadecimal string e.g. 255 gets 'ff'
 | 
			
		||||
* ui8[] := the bytes as hexdecimal strings e.g. 0 255 0 gets ui8[]:name:00 ui8[]:name:ff ui8[]:name:00
 | 
			
		||||
* int[] := 64 bit integer array e.g. 1 2 gets int[]:name:1 int[]:name:2
 | 
			
		||||
* bool[] := a boolean array e.g. true true gets bool[]:name:true bool[]:name:true
 | 
			
		||||
* str[] := string array e.g. 'a' 'abc' gets str[]:name:a str[]:name:abc
 | 
			
		||||
* f64[] := 64 bit float array e.g. 1.1 1.2 gets f64[]:name:1.100000000 f64[]:name:1.2000000000
 | 
			
		||||
Formatting of the corresponding values (each value is at the end a base64 string).
 | 
			
		||||
* str := the plain UTF8 string as URL encoded. These bytes are getting base64 encoded.
 | 
			
		||||
* int := the little endian representation of the int. These bytes are getting base64 encoded.
 | 
			
		||||
* f64 := the little endian representation of the float. These bytes are getting base64 encoded.
 | 
			
		||||
* bool := the byte 0x1 or 0x0 for true and false. These byte will be base64 encoded.
 | 
			
		||||
* ui8 := These byte will be base64 encoded.
 | 
			
		||||
* ui8[] := These bytes are getting base64 encoded.
 | 
			
		||||
* int[] := the little endian representation of the integers. These bytes are getting base64 encoded.
 | 
			
		||||
* bool[] := the bools are getting converted to bytes (0x1 or 0x0 for true and false). These bytes are getting base64 encoded.
 | 
			
		||||
* str[] := each string will be URL encoded. Afterwards, join all strings by \n. These bytes are getting base64 encoded.
 | 
			
		||||
* f64[] := the little endian representation of the floats. These bytes are getting base64 encoded.
 | 
			
		||||
 | 
			
		||||
The format of a message is:
 | 
			
		||||
command=COMMAND
 | 
			
		||||
 | 
			
		||||
@ -1,11 +1,16 @@
 | 
			
		||||
package ICCC
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/base64"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/SommerEngineering/Ocean/Log"
 | 
			
		||||
	LM "github.com/SommerEngineering/Ocean/Log/Meta"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// 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)
 | 
			
		||||
		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() {
 | 
			
		||||
 | 
			
		||||
		case `int64`:
 | 
			
		||||
			key := fmt.Sprintf(`int:%s`, keyName)
 | 
			
		||||
			data[key] = []string{strconv.FormatInt(field.Int(), 10)}
 | 
			
		||||
			key = fmt.Sprintf(`int:%s`, keyName)
 | 
			
		||||
 | 
			
		||||
			// Converts the value in a byte array:
 | 
			
		||||
			errConverter = binary.Write(buffer, binary.LittleEndian, field.Int())
 | 
			
		||||
 | 
			
		||||
		case `string`:
 | 
			
		||||
			key := fmt.Sprintf(`str:%s`, keyName)
 | 
			
		||||
			data[key] = []string{field.String()}
 | 
			
		||||
			key = fmt.Sprintf(`str:%s`, keyName)
 | 
			
		||||
 | 
			
		||||
			// URL encode the string and copy its bytes to the buffer:
 | 
			
		||||
			io.Copy(buffer, strings.NewReader(url.QueryEscape(field.String())))
 | 
			
		||||
 | 
			
		||||
		case `float64`:
 | 
			
		||||
			key := fmt.Sprintf(`f64:%s`, keyName)
 | 
			
		||||
			data[key] = []string{strconv.FormatFloat(field.Float(), 'f', 9, 64)}
 | 
			
		||||
			key = fmt.Sprintf(`f64:%s`, keyName)
 | 
			
		||||
 | 
			
		||||
			// Converts the value in a byte array:
 | 
			
		||||
			errConverter = binary.Write(buffer, binary.LittleEndian, field.Float())
 | 
			
		||||
 | 
			
		||||
		case `bool`:
 | 
			
		||||
			key := fmt.Sprintf(`bool:%s`, keyName)
 | 
			
		||||
			data[key] = []string{strconv.FormatBool(field.Bool())}
 | 
			
		||||
			key = fmt.Sprintf(`bool:%s`, keyName)
 | 
			
		||||
 | 
			
		||||
		case `uint8`: // byte
 | 
			
		||||
			key := fmt.Sprintf(`ui8:%s`, keyName)
 | 
			
		||||
			data[key] = []string{strconv.FormatUint(field.Uint(), 16)}
 | 
			
		||||
			// Directly convert the bool in a byte:
 | 
			
		||||
			if field.Bool() {
 | 
			
		||||
				// 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 `slice`:
 | 
			
		||||
			sliceLen := field.Len()
 | 
			
		||||
			if sliceLen > 0 {
 | 
			
		||||
 | 
			
		||||
				// Which kind of data is this?
 | 
			
		||||
				sliceKind := field.Index(0).Kind()
 | 
			
		||||
				key := ``
 | 
			
		||||
				dataValues := make([]string, sliceLen, sliceLen)
 | 
			
		||||
 | 
			
		||||
				// Select the right data type:
 | 
			
		||||
				switch sliceKind.String() {
 | 
			
		||||
				case `uint8`: // bytes
 | 
			
		||||
				case `uint8`: // a byte array
 | 
			
		||||
					key = fmt.Sprintf(`ui8[]:%s`, keyName)
 | 
			
		||||
					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`:
 | 
			
		||||
					key = fmt.Sprintf(`int[]:%s`, keyName)
 | 
			
		||||
					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`:
 | 
			
		||||
					key = fmt.Sprintf(`bool[]:%s`, keyName)
 | 
			
		||||
					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`:
 | 
			
		||||
					key = fmt.Sprintf(`str[]:%s`, keyName)
 | 
			
		||||
					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`:
 | 
			
		||||
					key = fmt.Sprintf(`f64[]:%s`, keyName)
 | 
			
		||||
					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