-
Notifications
You must be signed in to change notification settings - Fork 2
/
utils.go
120 lines (95 loc) · 3.05 KB
/
utils.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package gola
import (
"encoding/binary"
"fmt"
"log"
"net"
"os"
"github.com/golang/protobuf/proto"
ola_rpc "github.com/nickysemenza/gola/ola_proto/rpc"
"github.com/pkg/errors"
)
//OLA doesn't do things normally like proto2 or proto3, so there is some wonky stuff happening here...
//http://docs.openlighting.org/ola/doc/latest/rpc_system.html luckily the docs explain it somewhat...
const (
protocolVersion = 1
versionMask = 0xf0000000
sizeMask = 0x0fffffff
)
//Deciphers an incoming message by unwrapping the outer ola_rpc.RpcMessage first
func decipherMessage(data []byte, whereTo proto.Message) error {
rpcMessage := new(ola_rpc.RpcMessage)
if err := proto.Unmarshal(data, rpcMessage); err != nil {
log.Fatalln("Failed to parse ola_rpc.RpcMessage resp:", err)
return err
}
switch *rpcMessage.Type {
case ola_rpc.Type_RESPONSE_FAILED:
//Buffer now probably contain an error msg
str := fmt.Sprintf("%s", rpcMessage.Buffer)
return errors.New("RESPONSE_FAILED: " + str)
}
innerBuffer := rpcMessage.GetBuffer()
if err := proto.Unmarshal(innerBuffer, whereTo); err != nil {
log.Fatalln("Failed to parse inner resp:", err)
return err
}
return nil
}
//Sends a message to an RPC function
func (c *Client) sendMessage(pb proto.Message, rpcFunction string) {
dataToSend, err := proto.Marshal(pb)
if err != nil {
log.Fatalln("couldn't marshal inner pb", err)
}
var t ola_rpc.Type
t = ola_rpc.Type_REQUEST
rpcMessage := new(ola_rpc.RpcMessage)
rpcMessage.Type = &t
rpcMessage.Id = proto.Uint32(1)
rpcMessage.Name = proto.String(rpcFunction)
rpcMessage.Buffer = dataToSend
encodedRPCMessage, err := proto.Marshal(rpcMessage)
if err != nil {
log.Fatalln("couldn't marshal outer pb", err)
}
//log.Printf("%v", rpcMessage)
//log.Printf("%v", encodedRPCMessage)
sendDataToDest(c.Conn, encodedRPCMessage)
}
//callRPCMethod calls an RPC message, unpacking the response into resp proto.Message
func (c *Client) callRPCMethod(rpcFunction string, pb proto.Message, responseMessage proto.Message) error {
c.sendMessage(pb, rpcFunction)
rsp := readData(c.Conn)
return decipherMessage(rsp, responseMessage)
}
//Reads the 4 bytes of header and then body, returns the body
func readData(conn net.Conn) []byte {
header := make([]byte, 4)
conn.Read(header)
headerValue := int(binary.LittleEndian.Uint32(header))
size := headerValue & sizeMask
//log.Printf("expecing %d bytes",size)
data := make([]byte, size)
_, err := conn.Read(data)
if err != nil {
log.Fatalln(err)
}
//log.Printf("received %d bytes",n)
return data
}
//Sends data over the connection, pre-pending it with a 4 byte header
func sendDataToDest(conn net.Conn, data []byte) {
headerContent := (protocolVersion << 28) & versionMask
headerContent |= len(data) & sizeMask
//log.Printf("header: %v", headerContent)
bs := make([]byte, 4)
binary.LittleEndian.PutUint32(bs, uint32(headerContent))
conn.Write(bs)
_, err := conn.Write(data)
if err != nil {
fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
return
}
//fmt.Println("Sent " + strconv.Itoa(n) + " bytes")
}