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