comparison zbmux.py @ 12:a3ec3f2988ac

Initial commit of zbmux. This program demultiplexes packets from remote ZB modules to feed into a log file. Also allows user(s) to telnet to a port to talk to a particular module. Needs more work. eg - TCP connections should be in raw mode, not line. - There is no way to access statistics for each module.
author darius@Inchoate
date Tue, 13 Jan 2009 12:17:02 +1030
parents
children ac60a9244bdf
comparison
equal deleted inserted replaced
11:75f785a09e2e 12:a3ec3f2988ac
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.internet.serialport import SerialPort
30 from twisted.internet.protocol import Protocol
31 from twisted.protocols import basic
32 from twisted.internet import protocol, reactor
33 from twisted.python import log
34 import sys, zb, logging, logging.handlers
35
36 portname = '/dev/cuaU0'
37 baudrate = 38400
38 lognamebase = '/tmp/zbmux-%d.log'
39 baseport = 1080
40 zbids = [1, 2]
41
42 class ZBClient(basic.LineReceiver):
43 """Client for the TCP connection"""
44
45 def connectionMade(self):
46 log.msg("Got new client")
47 self.factory.clients.append(self)
48 if self.factory.lastline != None:
49 self.message(self.factory.lastline)
50
51 def connectionLost(self, reason):
52 log.msg("Lost a client")
53 self.factory.clients.remove(self)
54
55 def lineReceived(self, line):
56 """Got a line - send it to our ZB module"""
57 #log.msg("Got line " + line)
58 self.factory.proto.sendData(self.factory.zbid, line + '\n')
59
60 def message(self, message):
61 """Called to write a mesage to our client"""
62 self.transport.write(message)
63
64 class ZBFactory(protocol.ServerFactory):
65 """Factory for a ZB module
66
67 Represents a remote ZB module and has zero or more clients and a log file.
68 """
69 protocol = ZBClient
70
71 def __init__(self, zbid, proto, lognamebase):
72 self.zbid = zbid
73 self.proto = proto
74 self.clients = []
75 self.tmpline = ""
76 self.lastline = None
77
78 # Open logger
79 self.logger = logging.getLogger('Zigbee-%d' % (zbid))
80 self.logger.setLevel(logging.DEBUG)
81
82 # Add the log message handler to the logger
83 handler = logging.handlers.RotatingFileHandler(
84 lognamebase % (zbid), maxBytes = 20 * 1024, backupCount = 5)
85
86 self.logger.addHandler(handler)
87
88 def message(self, zbid, message):
89 """Called when we get a message, check it's for us - if it is log it and write to our clients"""
90 if zbid != self.zbid:
91 return
92
93 for c in self.clients:
94 c.message(message)
95
96 # Logger is line oriented, convert from packet oriented here
97 self.tmpline = self.tmpline + message
98 tmp = self.tmpline.split('\n')
99 for l in tmp[0:-1]:
100 self.lastline = l # Stores last seen line for new clients
101 self.logger.debug(l.replace('\n', ''))
102 self.tmpline = tmp[-1]
103
104 class ZBProto(Protocol):
105 """Protocol to handle packets from the ZB module on the serial port"""
106 def __init__(self):
107 self.pkts = zb.Packets()
108 self.factories = []
109
110 def dataReceived(self, data):
111 """Parses data from ZB module into packets, calls each factory if a RX packet is received"""
112 #log.msg("Read data " + data)
113 if self.pkts.processstr(data) > 0:
114 while len(self.pkts.pktq) > 0:
115 a = self.pkts.pktq.pop(0)
116 #log.msg("type is " + str(type(a)))
117 if type(a) == type(zb.RX_16_Bit()):
118 #log.msg("Rx'd from %d => %s" % (a.sender, a.payloadstr))
119 for f in self.factories:
120 f.message(a.sender, a.payloadstr)
121
122 def sendData(self, zbid, data):
123 """Sends a chunk of data to our ZB module"""
124 #log.msg("%d <= %s" % (zbid, data))
125
126 # Chop up data into pieces the module can handle
127 maxsz = 10
128 #maxsz = zb.TX_16_Bit.PKT_MAX_PAYLOAD
129 for i, j in zip(range(0, len(data), maxsz), range(maxsz, len(data) + maxsz, maxsz)):
130 p = zb.TX_16_Bit(zbid, data[i:j])
131 self.transport.write(p.Pack())
132 #log.msg("sent " + str(p))
133
134 if __name__ == '__main__':
135 logFile = sys.stdout
136 log.startLogging(logFile)
137
138 # ZigBee serial protocol handler
139 zbproto = ZBProto()
140 SerialPort(zbproto, portname, reactor, baudrate = 38400)
141
142 # Per-module TCP listener
143 for id in zbids:
144 zbfactory = ZBFactory(id, zbproto, lognamebase)
145 zbproto.factories.append(zbfactory)
146 reactor.listenTCP(baseport + id, zbfactory)
147
148 reactor.run()