Subversion Repositories HomeAutomation

Rev

Rev 857 | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | SVN | RSS feed

Rev 857 Rev 869
1
############################################################################
1
############################################################################
2
#    Copyright (C) 2008 by Mattias Runge   #
2
#    Copyright (C) 2008 by Mattias Runge   #
3
#    mattias@runge.se   #
3
#    mattias@runge.se   #
4
#                                                                          #
4
#                                                                          #
5
#    This program is free software; you can redistribute it and#or modify  #
5
#    This program is free software; you can redistribute it and#or modify  #
6
#    it under the terms of the GNU General Public License as published by  #
6
#    it under the terms of the GNU General Public License as published by  #
7
#    the Free Software Foundation; either version 2 of the License, or     #
7
#    the Free Software Foundation; either version 2 of the License, or     #
8
#    (at your option) any later version.                                   #
8
#    (at your option) any later version.                                   #
9
#                                                                          #
9
#                                                                          #
10
#    This program is distributed in the hope that it will be useful,       #
10
#    This program is distributed in the hope that it will be useful,       #
11
#    but WITHOUT ANY WARRANTY; without even the implied warranty of        #
11
#    but WITHOUT ANY WARRANTY; without even the implied warranty of        #
12
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         #
12
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         #
13
#    GNU General Public License for more details.                          #
13
#    GNU General Public License for more details.                          #
14
#                                                                          #
14
#                                                                          #
15
#    You should have received a copy of the GNU General Public License     #
15
#    You should have received a copy of the GNU General Public License     #
16
#    along with this program; if not, write to the                         #
16
#    along with this program; if not, write to the                         #
17
#    Free Software Foundation, Inc.,                                       #
17
#    Free Software Foundation, Inc.,                                       #
18
#    59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             #
18
#    59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             #
19
############################################################################
19
############################################################################
20
import os
20
import os
21
import sys
21
import sys
22
import asyncore
22
import asyncore
23
import string
23
import string
24
import socket
24
import socket
25
import Queue
25
import Queue
26
import threading
26
import threading
27
import time
27
import time
28
import ServiceManager
28
import ServiceManager
29
import CanModuleTypes
29
import CanModuleTypes
30
import Util
30
import Util
31
import Settings
31
import Settings
32
 
32
 
33
gBackendCan = False
33
gBackendCan = False
34
 
34
 
35
def GetBackendCan():
35
def GetBackendCan():
36
    global gBackendCan
36
    global gBackendCan
37
    if gBackendCan == False:
37
    if gBackendCan == False:
38
        gBackendCan = BackendCan(Settings.Settings["CANDAEMON_ADDRESS"], int(Settings.Settings["CANDAEMON_PORT"]))
38
        gBackendCan = BackendCan(Settings.Settings["CANDAEMON_ADDRESS"], int(Settings.Settings["CANDAEMON_PORT"]))
39
    return gBackendCan
39
    return gBackendCan
40
 
40
 
41
 
41
 
42
 
42
 
43
def ConvertHardwareIdToString(hwid):
43
def ConvertHardwareIdToString(hwid):
44
    return str("%X" % hwid[0]).zfill(2) + str("%X" % hwid[1]).zfill(2) + str("%X" % hwid[2]).zfill(2) + str("%X" % hwid[3]).zfill(2)
44
    return str("%X" % hwid[0]).zfill(2) + str("%X" % hwid[1]).zfill(2) + str("%X" % hwid[2]).zfill(2) + str("%X" % hwid[3]).zfill(2)
45
 
45
 
46
class CanMessage:
46
class CanMessage:
47
    def __init__(self):
47
    def __init__(self):
48
        self.ClassName = 0
48
        self.ClassName = 0
49
        self.Direction = 0
49
        self.Direction = 0
50
        self.ModuleType = 0
50
        self.ModuleType = 0
51
        self.ModuleId = 0
51
        self.ModuleId = 0
52
        self.Command = 0
52
        self.Command = 0
53
        self.Data = []
53
        self.Data = []
54
       
54
       
55
    def Parse(self, msg):
55
    def Parse(self, msg):
56
        parts = msg.split(' ')
56
        parts = msg.split(' ')
57
 
57
 
58
        id_bits = Util.hex2bin(parts[1]);
58
        id_bits = Util.hex2bin(parts[1]);
59
       
59
       
60
        self.ClassName = int(id_bits[4:7], 2)
60
        self.ClassName = int(id_bits[4:7], 2)
61
        self.Direction = int(id_bits[7:8], 2)
61
        self.Direction = int(id_bits[7:8], 2)
62
        self.ModuleType = int(id_bits[8:16], 2)
62
        self.ModuleType = int(id_bits[8:16], 2)
63
        self.ModuleId = int(id_bits[16:24], 2)
63
        self.ModuleId = int(id_bits[16:24], 2)
64
        self.Command = int(id_bits[24:32], 2)
64
        self.Command = int(id_bits[24:32], 2)
65
 
65
 
66
        count = 4
66
        count = 4
67
        while count < len(parts):
67
        while count < len(parts):
68
            self.Data.append(int(Util.hex2bin(parts[count]), 2))
68
            self.Data.append(int(Util.hex2bin(parts[count]), 2))
69
            count += 1
69
            count += 1
70
           
70
           
71
    def Compile(self):
71
    def Compile(self):
72
        bClassName = Util.int2bin(int(self.ClassName)).zfill(4)
72
        bClassName = Util.int2bin(int(self.ClassName)).zfill(4)
73
        bDirection = Util.int2bin(int(self.Direction)).zfill(1)
73
        bDirection = Util.int2bin(int(self.Direction)).zfill(1)
74
        bModuleType = Util.int2bin(int(self.ModuleType)).zfill(8)
74
        bModuleType = Util.int2bin(int(self.ModuleType)).zfill(8)
75
        bModuleId = Util.int2bin(int(self.ModuleId)).zfill(8)
75
        bModuleId = Util.int2bin(int(self.ModuleId)).zfill(8)
76
        bCommand = Util.int2bin(int(self.Command)).zfill(8)
76
        bCommand = Util.int2bin(int(self.Command)).zfill(8)
77
       
77
       
78
        id_bits = "000" + bClassName + bDirection + bModuleType + bModuleId + bCommand
78
        id_bits = "000" + bClassName + bDirection + bModuleType + bModuleId + bCommand
79
       
79
       
80
        id = Util.bin2hex(id_bits)
80
        id = Util.bin2hex(id_bits)
81
       
81
       
82
        bytes = ""
82
        bytes = ""
83
        for byte in self.Data:
83
        for byte in self.Data:
84
            bytes += Util.bin2hex(Util.int2bin(byte).zfill(8)) + " "
84
            bytes += Util.bin2hex(Util.int2bin(byte).zfill(8)) + " "
85
       
85
       
86
        cmd = "PKT " + id + " 1 0 " + bytes
86
        cmd = "PKT " + id + " 1 0 " + bytes
87
        return cmd.strip(" ")
87
        return cmd.strip(" ")
88
 
88
 
89
class CanNode:
89
class CanNode:
90
    HardwareId = [0,0,0,0]
90
    HardwareId = [0,0,0,0]
91
    LastOnline = 0
91
    LastOnline = 0
92
    Services = {}
92
    Services = {}
93
   
93
   
94
    def __init__(self, hwid):
94
    def __init__(self, hwid):
95
        self.Services = {}
95
        self.Services = {}
96
        self.HardwareId = hwid
96
        self.HardwareId = hwid
97
        self.UpdateLastOnline()
97
        self.UpdateLastOnline()
98
        self.canBackend = GetBackendCan()
98
        self.canBackend = GetBackendCan()
99
        self.serviceMan = ServiceManager.GetServiceManager()
99
        self.serviceMan = ServiceManager.GetServiceManager()
100
        self.AskForModules()
100
        self.AskForModules()
101
   
101
   
102
    def AskForModules(self):
102
    def AskForModules(self):
103
        canMsg = CanMessage()
103
        canMsg = CanMessage()
104
        canMsg.ClassName = 11 # 0x0B CAN_CLASS_MODULE_NMT
104
        canMsg.ClassName = 11 # 0x0B CAN_CLASS_MODULE_NMT
105
        canMsg.ModuleType = 0
105
        canMsg.ModuleType = 0
106
        canMsg.ModuleId = 0
106
        canMsg.ModuleId = 0
107
        canMsg.Direction = 0
107
        canMsg.Direction = 0
108
        canMsg.Command = 0 # 0x00 CAN_CMD_MODULE_NMT_LIST
108
        canMsg.Command = 0 # 0x00 CAN_CMD_MODULE_NMT_LIST
109
       
109
       
110
        canMsg.Data = self.HardwareId
110
        canMsg.Data = self.HardwareId
111
       
111
       
112
        print "CAN: " + self.GetHardwareIdString() + ": Asking for modules..."
112
        print "CAN: " + self.GetHardwareIdString() + ": Asking for modules..."
113
       
113
       
114
        self.canBackend.QueueMsg(canMsg.Compile())
114
        self.canBackend.QueueMsg(canMsg.Compile())
115
   
115
   
116
    def AddModule(self, ModuleType, ModuleId):
116
    def AddModule(self, ModuleType, ModuleId):
117
 
117
 
118
        try:
118
        try:
119
            serviceType = CanModuleTypes.Types[ModuleType]
119
            serviceType = CanModuleTypes.Types[ModuleType]
120
            serviceId = "CAN_" + str(ModuleType) + "_" + str(ModuleId)
120
            serviceId = "CAN_" + str(ModuleType) + "_" + str(ModuleId)
121
 
121
 
122
            if not self.serviceMan.HasService(serviceId, serviceType):
122
            if not self.serviceMan.HasService(serviceId, serviceType):
123
                self.serviceMan.AddService(serviceId, serviceType)
123
                self.serviceMan.AddService(serviceId, serviceType)
124
               
124
               
125
                print "CAN: " + self.GetHardwareIdString() + ": Added " + serviceType + " service with module id " + hex(ModuleId)
125
                print "CAN: " + self.GetHardwareIdString() + ": Added " + serviceType + " service with module id " + hex(ModuleId)
126
            else:
126
            else:
127
                print "CAN: " + self.GetHardwareIdString() + ": Service " + serviceType + " already started, module id " + hex(ModuleId)
127
                print "CAN: " + self.GetHardwareIdString() + ": Service " + serviceType + " already started, module id " + hex(ModuleId)
128
 
128
 
129
            service = self.serviceMan.GetService(serviceId, serviceType)
129
            service = self.serviceMan.GetService(serviceId, serviceType)
130
           
130
           
131
            service.ModuleType = ModuleType
131
            service.ModuleType = ModuleType
132
            service.ModuleId = ModuleId
132
            service.ModuleId = ModuleId
133
            service.SetOnline()
133
            service.SetOnline()
134
 
134
 
135
            self.Services[str(ModuleType) + "_" + str(ModuleId)] = service;
135
            self.Services[str(ModuleType) + "_" + str(ModuleId)] = service;
136
        except (KeyboardInterrupt, SystemExit):
136
        except (KeyboardInterrupt, SystemExit):
137
            raise
137
            raise
138
        except KeyError:
138
        except KeyError:
139
            print "CAN: " + self.GetHardwareIdString() + ": Unknown module with id " + hex(ModuleId) + ", type " + hex(ModuleType)
139
            print "CAN: " + self.GetHardwareIdString() + ": Unknown module with id " + hex(ModuleId) + ", type " + hex(ModuleType)
140
           
140
           
141
   
141
   
142
    def UpdateLastOnline(self):
142
    def UpdateLastOnline(self):
143
        self.LastOnline = time.time()
143
        self.LastOnline = time.time()
144
        for key in self.Services:
144
        for key in self.Services:
145
            self.Services[key].SetOnline()
145
            self.Services[key].SetOnline()
146
 
146
 
147
    def CheckOnline(self):
147
    def CheckOnline(self):
148
        if self.LastOnline+20 < time.time():
148
        if self.LastOnline+20 < time.time():
149
            self.SetServicesOffline()
149
            self.SetServicesOffline()
150
 
150
 
151
    def SetServicesOffline(self):
151
    def SetServicesOffline(self):
152
        print "CAN: " + self.GetHardwareIdString() + ": Node is offline, stopping services"
152
        print "CAN: " + self.GetHardwareIdString() + ": Node is offline, stopping services"
153
        for key in self.Services:
153
        for key in self.Services:
154
            self.Services[key].SetOffline()
154
            self.Services[key].SetOffline()
155
 
155
 
156
    def GetHardwareIdString(self):
156
    def GetHardwareIdString(self):
157
        return ConvertHardwareIdToString(self.HardwareId)
157
        return ConvertHardwareIdToString(self.HardwareId)
158
       
158
       
159
 
159
 
160
 
160
 
161
class BackendCan (asyncore.dispatcher, threading.Thread):
161
class BackendCan (asyncore.dispatcher, threading.Thread):
162
   
162
   
163
    nodes = {}
163
    nodes = {}
164
    msgCallbacks = []
164
    msgCallbacks = []
165
   
165
   
166
    def __init__(self, serverHost, serverPort):
166
    def __init__(self, serverHost, serverPort):
167
        self.nodes = {}
167
        self.nodes = {}
168
        self.msgCallbacks = []
168
        self.msgCallbacks = []
169
 
169
 
170
        threading.Thread.__init__(self)
170
        threading.Thread.__init__(self)
171
       
171
       
172
        asyncore.dispatcher.__init__(self)
172
        asyncore.dispatcher.__init__(self)
173
       
173
       
174
        self.serviceMan = ServiceManager.GetServiceManager()
174
        self.serviceMan = ServiceManager.GetServiceManager()
175
       
175
       
176
        self.nodesLock = threading.Lock()
176
        self.nodesLock = threading.Lock()
177
 
177
 
178
        self.queue = Queue.Queue()
178
        self.queue = Queue.Queue()
179
        self.queueLock = threading.Lock()
179
        self.queueLock = threading.Lock()
180
        self.connected = False
180
        self.connected = False
181
       
181
       
182
        self.serverHost = serverHost;
182
        self.serverHost = serverHost;
183
        self.serverPort = serverPort;
183
        self.serverPort = serverPort;
184
 
184
 
185
        self.timer = threading.Timer(20.0, self.CheckNodes)
185
        self.timer = threading.Timer(20.0, self.CheckNodes)
186
 
186
 
187
        self.Connect()
187
        self.Connect()
188
 
188
 
189
        self.setDaemon(True)
189
        self.setDaemon(True)
190
 
190
 
191
        self.start()
191
        self.start()
192
 
192
 
193
    def Connect(self):
193
    def Connect(self):
194
        try:
194
        try:
195
            print "CAN: Trying to connect to canDaemon at " + self.serverHost + ":" + str(self.serverPort)
195
            print "CAN: Trying to connect to canDaemon at " + self.serverHost + ":" + str(self.serverPort)
196
 
196
 
197
            self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
197
            self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
198
 
198
 
199
            self.connect((self.serverHost, self.serverPort))
199
            self.connect((self.serverHost, self.serverPort))
200
       
200
       
201
        except (KeyboardInterrupt, SystemExit):
201
        except (KeyboardInterrupt, SystemExit):
202
            raise
202
            raise
203
        except socket.gaierror, e:
203
        except socket.gaierror, e:
204
            print "CAN: Address-related error connecting to server: %s" % e
204
            print "CAN: Address-related error connecting to server: %s" % e
205
            self.handle_close()
205
            self.handle_close()
206
        except socket.error, e:
206
        except socket.error, e:
207
            print "CAN: Connection error: %s" % e
207
            print "CAN: Connection error: %s" % e
208
            self.handle_close()
208
            self.handle_close()
209
 
209
 
210
    def CheckNodes(self):
210
    def CheckNodes(self):
211
        try:
211
        try:
212
            print "CAN: Checking if nodes are offline"
212
            print "CAN: Checking if nodes are offline"
213
 
213
 
214
            self.nodesLock.acquire()
214
            self.nodesLock.acquire()
215
 
215
 
216
            for key in self.nodes:
216
            for key in self.nodes:
217
                self.nodes[key].CheckOnline()
217
                self.nodes[key].CheckOnline()
218
 
218
 
219
            self.nodesLock.release()
219
            self.nodesLock.release()
220
 
220
 
221
            self.StartTimer()
221
            self.StartTimer()
222
        except (KeyboardInterrupt, SystemExit):
222
        except (KeyboardInterrupt, SystemExit):
223
            raise
223
            raise
224
 
224
 
225
    def SetServicesOffline(self):
225
    def SetServicesOffline(self):
226
        self.nodesLock.acquire()
226
        self.nodesLock.acquire()
227
 
227
 
228
        for key in self.nodes:
228
        for key in self.nodes:
229
            self.nodes[key].SetServicesOffline()
229
            self.nodes[key].SetServicesOffline()
230
 
230
 
231
        self.nodesLock.release()
231
        self.nodesLock.release()
232
 
232
 
233
    def StartTimer(self):
233
    def StartTimer(self):
234
       
234
       
235
        self.timer.start()
235
        self.timer.start()
236
 
236
 
237
    def handle_connect(self):
237
    def handle_connect(self):
238
        pass
238
        pass
239
 
239
 
240
    def handle_close(self):
240
    def handle_close(self):
241
        self.connected = False
241
        self.connected = False
242
        print "CAN: " + "Connection to canDaemon closed"
242
        print "CAN: " + "Connection to canDaemon closed"
243
        self.close()
243
        self.close()
244
        self.SetServicesOffline()
244
        self.SetServicesOffline()
245
        self.timer.cancel()
245
        self.timer.cancel()
246
 
246
 
247
        print "CAN: Waiting for 10 seconds..."
247
        print "CAN: Waiting for 10 seconds..."
248
        timer = threading.Timer(10.0, self.Connect)
248
        timer = threading.Timer(10.0, self.Connect)
249
        timer.start()
249
        timer.start()
250
        timer.join()
250
        timer.join()
251
       
251
       
252
    def run(self):
252
    def run(self):
253
        #self.log('run')
253
        #self.log('run')
254
        asyncore.loop(1)
254
        asyncore.loop(1)
255
       
255
       
256
    def writable(self):
256
    def writable(self):
257
        self.queueLock.acquire()
257
        self.queueLock.acquire()
258
        state = not self.queue.empty()
258
        state = not self.queue.empty()
259
        #self.log("writable: " + str(state))
259
        #self.log("writable: " + str(state))
260
        self.queueLock.release()
260
        self.queueLock.release()
261
        return state
261
        return state
262
   
262
   
263
    def handle_write(self):
263
    def handle_write(self):
264
        self.queueLock.acquire()
264
        self.queueLock.acquire()
265
       
265
       
266
        while not self.queue.empty():
266
        while not self.queue.empty():
267
            msg = self.queue.get()
267
            msg = self.queue.get()
268
            self.send(msg + "\n")
268
            self.send(msg + "\n")
269
            #self.log("SENT: " + msg)
269
            #self.log("SENT: " + msg)
270
            self.queue.task_done()
270
            self.queue.task_done()
271
            time.sleep(0.0001)
271
            time.sleep(0.0001)
272
           
272
           
273
        self.queueLock.release()
273
        self.queueLock.release()
274
       
274
       
275
    def handle_read(self):
275
    def handle_read(self):
276
        try:
276
        try:
277
            more = self.recv(1024)
277
            more = self.recv(1024)
278
 
278
 
279
            if not self.connected:
279
            if not self.connected:
280
                self.connected = True
280
                self.connected = True
281
                print "CAN: " + "Connected to canDaemon"
281
                print "CAN: " + "Connected to canDaemon"
282
 
282
 
283
                self.StartTimer()
283
                self.StartTimer()
284
 
284
 
285
            if not more:
285
            if not more:
286
                self.handle_close()
286
                self.handle_close()
287
           
287
           
288
            recvList = more.split("\n")
288
            recvList = more.split("\n")
289
       
289
       
290
            for msg in recvList:
290
            for msg in recvList:
291
                self.handle_msg(msg)
291
                self.handle_msg(msg)
292
 
292
 
293
        except (KeyboardInterrupt, SystemExit):
293
        except (KeyboardInterrupt, SystemExit):
294
            raise
294
            raise
295
        except socket.error, e:
295
        except socket.error, e:
296
            print "CAN: Error receiving data: %s" % e
296
            print "CAN: Error receiving data: %s" % e
297
            self.handle_close()
297
            self.handle_close()
298
           
298
           
299
    def handle_msg(self, msg):
299
    def handle_msg(self, msg):
300
        if len(msg) > 0:
300
        if len(msg) > 0:
301
            #self.log("RECV: " + msg)
301
            #self.log("RECV: " + msg)
302
           
302
           
303
            canMsg = CanMessage()
303
            canMsg = CanMessage()
304
            canMsg.Parse(msg)
304
            canMsg.Parse(msg)
305
           
305
           
306
            if canMsg.ClassName == 0: # 0x00 CAN_NMT
306
            if canMsg.ClassName == 0: # 0x00 CAN_NMT
307
                if canMsg.ModuleType == 44: # 0x2C CAN_NMT_HEARTBEAT
307
                if canMsg.ModuleType == 44: # 0x2C CAN_NMT_HEARTBEAT
308
                    self.ProcessHeartbeat(canMsg.Data)
308
                    self.ProcessHeartbeat(canMsg.Data)
309
            else:
309
            else:
310
                if canMsg.Command == 00: # 0x00 CAN_CMD_MODULE_NMT_LIST
310
                if canMsg.Command == 00: # 0x00 CAN_CMD_MODULE_NMT_LIST
311
                    self.ProcessModuleList(canMsg)
311
                    self.ProcessModuleList(canMsg)
312
                else:
312
                else:
313
                    for callback in self.msgCallbacks:
313
                    for callback in self.msgCallbacks:
314
                        callback(canMsg)
314
                        callback(canMsg)
315
           
315
           
316
    def ProcessModuleList(self, canMsg):
316
    def ProcessModuleList(self, canMsg):
317
        HardwareIdString = ConvertHardwareIdToString(canMsg.Data)
317
        HardwareIdString = ConvertHardwareIdToString(canMsg.Data)
318
       
318
       
319
        self.nodesLock.acquire()
319
        self.nodesLock.acquire()
320
 
320
 
321
        if self.nodes.has_key(HardwareIdString):
321
        if self.nodes.has_key(HardwareIdString):
322
            #print "CAN: " + "Received module info from " + HardwareIdString
322
            #print "CAN: " + "Received module info from " + HardwareIdString
323
            self.nodes[HardwareIdString].AddModule(canMsg.ModuleType, canMsg.ModuleId)
323
            self.nodes[HardwareIdString].AddModule(canMsg.ModuleType, canMsg.ModuleId)
324
        else:
324
        else:
325
            print "CAN: " + "Received module info from " + HardwareIdString + " but that node is not online, ignoring"
325
            print "CAN: " + "Received module info from " + HardwareIdString + " but that node is not online, ignoring"
326
 
326
 
327
        self.nodesLock.release()
327
        self.nodesLock.release()
328
   
328
   
329
    def ProcessHeartbeat(self, HardwareId):
329
    def ProcessHeartbeat(self, HardwareId):
330
        HardwareIdString = ConvertHardwareIdToString(HardwareId)
330
        HardwareIdString = ConvertHardwareIdToString(HardwareId)
331
       
331
       
332
        print "CAN: " + HardwareIdString + ": Received heartbeat"
332
        print "CAN: " + HardwareIdString + ": Received heartbeat"
333
 
333
 
334
        self.nodesLock.acquire()
334
        self.nodesLock.acquire()
335
 
335
 
336
        if self.nodes.has_key(HardwareIdString):
336
        if self.nodes.has_key(HardwareIdString):
337
            self.nodes[HardwareIdString].UpdateLastOnline()
337
            self.nodes[HardwareIdString].UpdateLastOnline()
338
        else:
338
        else:
339
            self.nodes[HardwareIdString] = CanNode(HardwareId)
339
            self.nodes[HardwareIdString] = CanNode(HardwareId)
340
 
340
 
341
        self.nodesLock.release()
341
        self.nodesLock.release()
342
   
342
   
343
    def QueueMsg(self, msg):
343
    def QueueMsg(self, msg):
344
        if self.connected:
344
        if self.connected:
345
            self.queueLock.acquire()
345
            self.queueLock.acquire()
346
            self.queue.put(msg)
346
            self.queue.put(msg)
347
            self.queueLock.release()
347
            self.queueLock.release()
348
            self.handle_write()
348
            self.handle_write()
349
        else:
349
        else:
350
            self.queueLock.acquire()
350
            self.queueLock.acquire()
351
            self.queue.put(msg)
351
            self.queue.put(msg)
352
            self.queueLock.release()
352
            self.queueLock.release()
353
       
353
       
354
    def AddCallback(self, callback):
354
    def AddCallback(self, callback):
355
        self.msgCallbacks.append(callback)
355
        self.msgCallbacks.append(callback)
356
 
356