2014-04-26 09:18:56 +00:00
package ICCC
2014-10-19 17:19:11 +00:00
import (
2015-07-07 07:10:03 +00:00
"bytes"
"encoding/base64"
"encoding/binary"
2014-10-19 17:19:11 +00:00
"fmt"
2015-06-21 18:18:23 +00:00
"github.com/SommerEngineering/Ocean/Log"
LM "github.com/SommerEngineering/Ocean/Log/Meta"
2015-07-07 07:10:03 +00:00
"io"
"net/url"
2014-10-19 17:19:11 +00:00
"reflect"
2015-07-07 07:10:03 +00:00
"strings"
2014-10-19 17:19:11 +00:00
)
2014-04-26 09:18:56 +00:00
2015-06-17 15:44:52 +00:00
// Function to convert an ICCC message to HTTP data.
2015-06-21 18:18:23 +00:00
func Message2Data ( channel , command string , message interface { } ) ( data map [ string ] [ ] string ) {
defer func ( ) {
if err := recover ( ) ; err != nil {
Log . LogFull ( senderName , LM . CategorySYSTEM , LM . LevelERROR , LM . SeverityUnknown , LM . ImpactUnknown , LM . MessageNamePARSE , fmt . Sprintf ( "Was not able to convert the message to HTTP values. %s" , err ) )
data = make ( map [ string ] [ ] string , 0 )
return
}
} ( )
2015-06-17 15:44:52 +00:00
// Create the map:
2014-06-08 09:35:01 +00:00
data = make ( map [ string ] [ ] string )
2015-06-17 15:44:52 +00:00
// Add the meta information:
2014-06-08 09:35:01 +00:00
data [ ` command ` ] = [ ] string { command }
data [ ` channel ` ] = [ ] string { channel }
2014-04-26 09:18:56 +00:00
if message == nil {
return
}
2015-06-17 15:44:52 +00:00
// Use reflection to determine the types:
2014-04-26 09:18:56 +00:00
element := reflect . ValueOf ( message )
elementType := element . Type ( )
2015-06-17 15:44:52 +00:00
// Iterate over all fields of the data type.
// Transform the data regarding the type.
2014-04-26 09:18:56 +00:00
for i := 0 ; i < element . NumField ( ) ; i ++ {
field := element . Field ( i )
keyName := elementType . Field ( i ) . Name
2015-07-07 07:10:03 +00:00
// 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:
2014-04-26 09:18:56 +00:00
switch field . Kind ( ) . String ( ) {
case ` int64 ` :
2015-07-07 07:10:03 +00:00
key = fmt . Sprintf ( ` int:%s ` , keyName )
// Converts the value in a byte array:
errConverter = binary . Write ( buffer , binary . LittleEndian , field . Int ( ) )
2014-04-26 09:18:56 +00:00
case ` string ` :
2015-07-07 07:10:03 +00:00
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 ( ) ) ) )
2014-04-26 09:18:56 +00:00
case ` float64 ` :
2015-07-07 07:10:03 +00:00
key = fmt . Sprintf ( ` f64:%s ` , keyName )
// Converts the value in a byte array:
errConverter = binary . Write ( buffer , binary . LittleEndian , field . Float ( ) )
2014-04-26 09:18:56 +00:00
case ` bool ` :
2015-07-07 07:10:03 +00:00
key = fmt . Sprintf ( ` bool:%s ` , keyName )
// 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 )
2014-04-26 09:18:56 +00:00
2015-07-07 07:10:03 +00:00
// uint8 is a byte, thus, write it directly in the buffer:
buffer . WriteByte ( byte ( field . Uint ( ) ) )
2014-04-26 09:18:56 +00:00
2015-06-17 15:44:52 +00:00
// Case: Arrays...
2014-04-26 09:18:56 +00:00
case ` slice ` :
sliceLen := field . Len ( )
if sliceLen > 0 {
2015-07-07 07:10:03 +00:00
// Which kind of data is this?
2014-04-26 09:18:56 +00:00
sliceKind := field . Index ( 0 ) . Kind ( )
2015-07-07 07:10:03 +00:00
// Select the right data type:
2014-04-26 09:18:56 +00:00
switch sliceKind . String ( ) {
2015-07-07 07:10:03 +00:00
case ` uint8 ` : // a byte array
2014-04-26 09:18:56 +00:00
key = fmt . Sprintf ( ` ui8[]:%s ` , keyName )
values := field . Interface ( ) . ( [ ] uint8 )
2015-07-07 07:10:03 +00:00
// Directly write the bytes in the buffer:
for _ , val := range values {
buffer . WriteByte ( byte ( val ) )
2014-04-26 09:18:56 +00:00
}
case ` int64 ` :
key = fmt . Sprintf ( ` int[]:%s ` , keyName )
values := field . Interface ( ) . ( [ ] int64 )
2015-07-07 07:10:03 +00:00
// Converts the array in a byte array:
errConverter = binary . Write ( buffer , binary . LittleEndian , values )
2014-04-26 09:18:56 +00:00
case ` bool ` :
key = fmt . Sprintf ( ` bool[]:%s ` , keyName )
values := field . Interface ( ) . ( [ ] bool )
2015-07-07 07:10:03 +00:00
// 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
}
2014-04-26 09:18:56 +00:00
}
case ` string ` :
key = fmt . Sprintf ( ` str[]:%s ` , keyName )
values := field . Interface ( ) . ( [ ] string )
2015-07-07 07:10:03 +00:00
// 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 )
2014-04-26 09:18:56 +00:00
}
2015-07-07 07:10:03 +00:00
// 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" ) ) )
2014-04-26 09:18:56 +00:00
case ` float64 ` :
key = fmt . Sprintf ( ` f64[]:%s ` , keyName )
values := field . Interface ( ) . ( [ ] float64 )
2015-07-07 07:10:03 +00:00
// Converts the array in a byte array:
errConverter = binary . Write ( buffer , binary . LittleEndian , values )
}
2014-04-26 09:18:56 +00:00
}
}
2015-07-07 07:10:03 +00:00
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 ( ) ) }
}
2014-04-26 09:18:56 +00:00
}
return
}