comparison zbmux.tac @ 17:b0fc5f8da118

Rename to zbmux.tac, and use application framework. Add emacs magic so it gets edited in Python mode. Make log file rotate at 1Mb.
author darius@Inchoate
date Sun, 18 Jan 2009 13:32:29 +1030
parents zbmux.py@ce3712110055
children 0baf9538a1b6
comparison
equal deleted inserted replaced
16:ce3712110055 17:b0fc5f8da118
1 #
2 # Mux the ZB module to TCP ports
3 #
4 # Copyright (c) 2009
5 # Daniel O'Connor <darius@dons.net.au>. All rights reserved.
6 #
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions
9 # are met:
10 # 1. Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # 2. Redistributions in binary form must reproduce the above copyright
13 # notice, this list of conditions and the following disclaimer in the
14 # documentation and/or other materials provided with the distribution.
15 #
16 # THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 # ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
20 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 # SUCH DAMAGE.
27 #
28
29 from twisted.application import internet, service
30 from twisted.internet.serialport import SerialPort
31 from twisted.internet.protocol import Protocol
32 from twisted.conch.telnet import TelnetTransport, TelnetBootstrapProtocol
33 from twisted.conch.insults import insults
34 from twisted.protocols import basic
35 from twisted.internet import protocol, reactor
36 from twisted.python import log
37 import sys, zb, logging, logging.handlers, string
38
39 portname = '/dev/cuaU0'
40 baudrate = 38400
41 lognamebase = '/tmp/zbmux-%d.log'
42 baseport = 1080
43 zbids = [1, 2]
44
45 class ZBClient(insults.TerminalProtocol):
46 """Client for the TCP connection"""
47 #: Time to wait before sending pending data (seconds)
48 QUEUE_TIME = 0.1
49
50 def connectionMade(self):
51 log.msg("Got new client")
52 self.terminal.eraseDisplay()
53 self.terminal.resetPrivateModes([])
54
55 # Send the last whole line we've seen out
56 for l in self.factory.lastlines:
57 self.message(l + '\n')
58
59 self.pending = ""
60 self.pendtimer = None
61 self.factory.clients.append(self)
62
63 def connectionLost(self, reason):
64 log.msg("Lost a client")
65 self.factory.clients.remove(self)
66
67 def keystrokeReceived(self, keyID, modifier):
68 """Got some data, add it to the pending output queue"""
69 if modifier != None:
70 print "Received unhandled modifier: %s" % (str(modifier))
71 return
72 #if keyID not in string.printable:
73 # print "Received unhandled keyID: %r" % (keyID,)
74 # return
75 #log.msg("Got key ->%s<-" % (keyID))
76 self.pending = self.pending + keyID
77 if self.pendtimer == None:
78 self.pendtimer = reactor.callLater(self.QUEUE_TIME, self.sendData)
79
80 def sendData(self):
81 """Send pending data to module"""
82 #log.msg("sending " + self.pending)
83 self.factory.zbproto.sendData(self.factory.zbid, self.pending)
84 self.pending = ""
85 self.pendtimer = None
86
87 def message(self, message):
88 """Called to write a mesage to our client"""
89 self.terminal.write(message)
90
91 class ZBFactory(protocol.ServerFactory):
92 """Factory for a ZB module
93
94 Represents a remote ZB module and has zero or more clients and a log file.
95 """
96 protocol = ZBClient
97
98 def __init__(self, zbid, lognamebase):
99 self.zbid = zbid
100 self.clients = []
101 self.tmpline = ""
102 self.lastlines = []
103
104 # Open logger
105 self.logger = logging.getLogger('Zigbee-%d' % (zbid))
106 self.logger.setLevel(logging.DEBUG)
107
108 # Add the log message handler to the logger
109 handler = logging.handlers.RotatingFileHandler(
110 lognamebase % (zbid), maxBytes = 1 * 1024 * 1024, backupCount = 5)
111
112 self.logger.addHandler(handler)
113
114 def message(self, zbid, message):
115 """Called when we get a message, check it's for us - if it is log it and write to our clients"""
116 if zbid != self.zbid:
117 return
118
119 for c in self.clients:
120 c.message(message)
121
122 # Logger is line oriented, convert from packet oriented here
123 self.tmpline = self.tmpline + message
124 tmp = self.tmpline.split('\n')
125 for l in tmp[0:-1]:
126 self.lastlines.append(l)
127 self.lastlines = self.lastlines[-5:]
128 self.logger.debug(l.replace('\n', ''))
129 self.tmpline = tmp[-1]
130
131 class ZBProto(Protocol):
132 """Protocol to handle packets from the ZB module on the serial port"""
133 def __init__(self):
134 self.pkts = zb.Packets()
135 self.factories = []
136
137 def dataReceived(self, data):
138 """Parses data from ZB module into packets, calls each factory if a RX packet is received"""
139 #log.msg("Read data " + data)
140 if self.pkts.processstr(data) > 0:
141 while len(self.pkts.pktq) > 0:
142 a = self.pkts.pktq.pop(0)
143 #log.msg("type is " + str(type(a)))
144 if type(a) == type(zb.RX_16_Bit()):
145 #log.msg("Rx'd from %d => %s" % (a.sender, a.payloadstr))
146 for f in self.factories:
147 f.message(a.sender, a.payloadstr)
148 if type(a) == type(zb.TX_Status()):
149 #log.msg("Tx status for frame %d is %s" % (a.frameid, a.statusMsg))
150 pass
151
152 def sendData(self, zbid, data):
153 """Sends a chunk of data to our ZB module"""
154 #log.msg("%d <= %s" % (zbid, data))
155
156 # Chop up data into pieces the module can handle
157 maxsz = zb.TX_16_Bit.PKT_MAX_PAYLOAD
158 for i, j in zip(range(0, len(data), maxsz), range(maxsz, len(data) + maxsz, maxsz)):
159 p = zb.TX_16_Bit(zbid, data[i:j])
160 self.transport.write(p.Pack())
161 #log.msg("sent " + str(p))
162
163 application = service.Application('zbmux')
164
165 # ZigBee serial protocol handler
166 zbproto = ZBProto()
167 SerialPort(zbproto, portname, reactor, baudrate = 38400)
168
169 # Per-module TCP listener
170 for id in zbids:
171 f = ZBFactory(id, lognamebase)
172 f.zbproto = zbproto
173 f.protocol = lambda: TelnetTransport(TelnetBootstrapProtocol,
174 insults.ServerProtocol,
175 ZBClient)
176 zbproto.factories.append(f)
177 internet.TCPServer(baseport + id, f).setServiceParent(
178 service.IServiceCollection(application))
179
180 ######################################################################
181 #
182 # These lines tell Emacs to edit this file in Python mode
183 #;;; Local Variables: ***
184 #;;; mode:python ***
185 #;;; End: ***