1#!/usr/bin/python
2
3# Copyright (C) 2012 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#       http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17import sys
18import numpy as np
19import scipy as sp
20import socket
21import struct
22sys.path.append(sys.path[0] + "/processing")
23from consts import *
24
25builtinFunctions = [
26    "echo", # send back whatever is received
27    "intsum", # returns int64 + int64
28]
29
30CMD_HEADER    = 0x0
31CMD_TERMINATE = 0x1
32CMD_FUNCTION  = 0x2
33CMD_AUDIO_MONO = 0x4
34CMD_AUDIO_STEREO = 0x5
35CMD_INT64 = 0x8
36CMD_DOUBLE = 0x9
37CMD_RESULT = 0x10
38
39def echo(inputData, inputTypes):
40    output = []
41    print "echo received ", inputData
42    output.append(RESULT_OK)
43    output.append(inputData)
44    output.append(inputTypes)
45    return output
46
47def intsum(inputData, inputTypes):
48    output = []
49    output.append(RESULT_OK)
50    sum = inputData[0] + inputData[1]
51    print "intsum sum is ", sum
52    outputData = []
53    outputData.append(sum)
54    outputTypes = []
55    outputTypes.append(TYPE_I64)
56    output.append(outputData)
57    output.append(outputTypes)
58    return output
59
60
61class CommandHandler(object):
62
63    def __init__(self, conn):
64        self.conn = conn
65    def __del__(self):
66        self.conn.close()
67    def run(self):
68        header = self.readI32()
69        if header == CMD_TERMINATE:
70            print "terminate cmd, will exit"
71            sys.exit(0)
72        nParam = 0
73        if header == CMD_HEADER:
74            nParam = self.readI32()
75            if nParam < 1:
76                protocolError("wrong number of params")
77            cmdFunction = self.readI32()
78            if cmdFunction != CMD_FUNCTION:
79                protocolError("not function")
80            nameLen = self.readI32()
81            self.functionName = self.readRaw(nameLen)
82            print "Processing function:", self.functionName
83            inputData = []
84            inputTypes = []
85            for i in range(nParam - 1):
86                cmd = self.readI32()
87                if (cmd == CMD_AUDIO_STEREO) or (cmd == CMD_AUDIO_MONO):
88                    dataLen = self.readI32()
89                    data = self.readI16Array(dataLen / 2)
90                    inputData.append(data)
91                    if (cmd == CMD_AUDIO_STEREO):
92                        inputTypes.append(TYPE_STEREO)
93                    else:
94                        inputTypes.append(TYPE_MONO)
95                    print i, "-th input received audio data ", dataLen, cmd
96                elif cmd == CMD_INT64:
97                    i64 = self.readI64()
98                    inputData.append(i64)
99                    inputTypes.append(TYPE_I64)
100                elif cmd == CMD_DOUBLE:
101                    val = self.readDouble()
102                    inputData.append(val)
103                    inputTypes.append(TYPE_DOUBLE)
104                else:
105                    self.protocolError("unknown command " + str(cmd))
106            print "inputTypes ", inputTypes
107            # length 3 list
108            # output[0]: int, execution result, RESULT_XXX values
109            # output[1]: output data list
110            # output[2]: output type list
111            output = []
112            if not self.functionName in builtinFunctions:
113                mod = __import__(self.functionName)
114                output = getattr(mod, self.functionName)(inputData, inputTypes)
115            else:
116                output = globals()[self.functionName](inputData, inputTypes)
117            nOutputParams = len(output[1])
118            self.sendI32(CMD_HEADER)
119            self.sendI32(nOutputParams + 1) # 1 for result
120            self.sendI32(CMD_RESULT)
121            self.sendI32(output[0])
122            outputData = output[1]
123            outputTypes = output[2]
124            print "outputTypes ", outputTypes
125            for i in range(nOutputParams):
126                if (outputTypes[i] == TYPE_I64):
127                    self.sendI32(CMD_INT64)
128                    self.sendI64(outputData[i])
129                elif (outputTypes[i] == TYPE_DOUBLE):
130                    self.sendI32(CMD_DOUBLE)
131                    self.sendDouble(outputData[i])
132                elif (outputTypes[i] == TYPE_STEREO):
133                    self.sendI32(CMD_AUDIO_STEREO)
134                    self.sendI32(len(outputData[i]) * 2)
135                    self.sendI16Array(outputData[i])
136                elif (outputTypes[i] == TYPE_MONO):
137                    self.sendI32(CMD_AUDIO_MONO)
138                    self.sendI32(len(outputData[i]) * 2)
139                    self.sendI16Array(outputData[i])
140                else:
141                    print "unknown type ", outputTypes[i], \
142                        " returned from funcion ", self.functionName
143                    sys.exit(1)
144
145    def readRaw(self, length):
146        result = []
147        totalRead = 0
148        while totalRead < length:
149            raw = self.conn.recv(length - totalRead)
150            justRead = len(raw)
151            if justRead == 0: # socket closed
152                sys.exit(1)
153            totalRead += justRead
154            result.append(raw)
155        return ''.join(result)
156
157    def readI32(self):
158        raw = self.readRaw(4)
159        i32 = struct.unpack("<i", raw)
160        return i32[0]
161
162    def readI64(self):
163        raw = self.readRaw(8)
164        i64 = struct.unpack("<q", raw)
165        return i64[0]
166
167    def readDouble(self):
168        raw = self.readRaw(8)
169        val = struct.unpack("<d", raw)
170        return val[0]
171
172    def readI16Array(self, length):
173        raw = self.readRaw(length * 2)
174        data = np.fromstring(raw, dtype=np.int16)
175        return data
176
177    def sendI32(self, i32):
178        raw = struct.pack("<i", i32)
179        self.sendRaw(raw)
180
181    def sendI64(self, i64):
182        raw = struct.pack("<q", i64)
183        self.sendRaw(raw)
184
185    def sendDouble(self, val):
186        raw = struct.pack("<d", val)
187        self.sendRaw(raw)
188
189    def sendI16Array(self, arry):
190        raw = arry.tostring()
191        self.sendRaw(raw)
192
193    def sendRaw(self, rawString):
194        totalSent = 0
195        stringLen = len(rawString)
196        while totalSent < stringLen:
197            sent = self.conn.send(rawString[totalSent:])
198            totalSent += sent
199
200    def protocolError(self, message):
201        print message
202        sys.exit(1)
203
204
205if __name__=="__main__":
206    HOST = "localhost"
207    PORT = 15010
208    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
209    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
210    s.bind((HOST, PORT))
211    s.listen(1)
212
213    conn, addr = s.accept()
214    print "client connected"
215    # close the server socket to allow other instance to run
216    s.close()
217    handler = CommandHandler(conn)
218    while 1:
219        handler.run()
220