changeset 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 ce3712110055
children 0baf9538a1b6
files zbmux.py zbmux.tac
diffstat 2 files changed, 185 insertions(+), 180 deletions(-) [+]
line wrap: on
line diff
--- a/zbmux.py	Sat Jan 17 21:44:17 2009 +1030
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,180 +0,0 @@
-#
-# Mux the ZB module to TCP ports
-#
-# Copyright (c) 2009
-#      Daniel O'Connor <darius@dons.net.au>.  All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-# 1. Redistributions of source code must retain the above copyright
-#    notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-#    notice, this list of conditions and the following disclaimer in the
-#    documentation and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
-# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-# SUCH DAMAGE.
-#
-
-from twisted.internet.serialport import SerialPort
-from twisted.internet.protocol import Protocol
-from twisted.conch.telnet import TelnetTransport, TelnetBootstrapProtocol
-from twisted.conch.insults import insults
-from twisted.protocols import basic
-from twisted.internet import protocol, reactor
-from twisted.python import log
-import sys, zb, logging, logging.handlers, string
-
-portname = '/dev/cuaU0'
-baudrate = 38400
-lognamebase = '/tmp/zbmux-%d.log'
-baseport = 1080
-zbids = [1, 2]
-
-class ZBClient(insults.TerminalProtocol):
-    """Client for the TCP connection"""
-    #: Time to wait before sending pending data (seconds)
-    QUEUE_TIME = 0.1
-    
-    def connectionMade(self):
-        log.msg("Got new client")
-        self.terminal.eraseDisplay()
-        self.terminal.resetPrivateModes([])
-        
-        # Send the last whole line we've seen out
-        for l in self.factory.lastlines:
-            self.message(l + '\n')
-            
-        self.pending = ""
-        self.pendtimer = None
-        self.factory.clients.append(self)
-        
-    def connectionLost(self, reason):
-        log.msg("Lost a client")
-        self.factory.clients.remove(self)
-        
-    def keystrokeReceived(self, keyID, modifier):
-        """Got some data, add it to the pending output queue"""
-        if modifier != None:
-            print "Received unhandled modifier: %s" % (str(modifier))
-            return
-        #if keyID not in string.printable:
-        #    print "Received unhandled keyID: %r" % (keyID,)
-        #    return
-        #log.msg("Got key ->%s<-" % (keyID))
-        self.pending = self.pending + keyID
-        if self.pendtimer == None:
-            self.pendtimer = reactor.callLater(self.QUEUE_TIME, self.sendData)
-            
-    def sendData(self):
-        """Send pending data to module"""
-        #log.msg("sending " + self.pending)
-        self.factory.zbproto.sendData(self.factory.zbid, self.pending)
-        self.pending = ""
-        self.pendtimer = None
-        
-    def message(self, message):
-        """Called to write a mesage to our client"""
-        self.terminal.write(message)
-        
-class ZBFactory(protocol.ServerFactory):
-    """Factory for a ZB module
-
-Represents a remote ZB module and has zero or more clients and a log file.
-"""
-    protocol = ZBClient
-    
-    def __init__(self, zbid, lognamebase):
-        self.zbid = zbid
-        self.clients = []
-        self.tmpline = ""
-        self.lastlines = []
-
-        # Open logger
-        self.logger = logging.getLogger('Zigbee-%d' % (zbid))
-        self.logger.setLevel(logging.DEBUG)
-
-        # Add the log message handler to the logger
-        handler = logging.handlers.RotatingFileHandler(
-            lognamebase % (zbid), maxBytes = 20 * 1024, backupCount = 5)
-
-        self.logger.addHandler(handler)
-
-    def message(self, zbid, message):
-        """Called when we get a message, check it's for us - if it is log it and write to our clients"""
-        if zbid != self.zbid:
-            return
-        
-        for c in self.clients:
-            c.message(message)
-
-        # Logger is line oriented, convert from packet oriented here
-        self.tmpline = self.tmpline + message
-        tmp = self.tmpline.split('\n')
-        for l in tmp[0:-1]:
-            self.lastlines.append(l)
-            self.lastlines = self.lastlines[-5:]
-            self.logger.debug(l.replace('\n', ''))
-        self.tmpline = tmp[-1]
-
-class ZBProto(Protocol):
-    """Protocol to handle packets from the ZB module on the serial port"""
-    def __init__(self):
-        self.pkts = zb.Packets()
-        self.factories = []
-        
-    def dataReceived(self, data):
-        """Parses data from ZB module into packets, calls each factory if a RX packet is received"""
-        #log.msg("Read data " + data)
-        if self.pkts.processstr(data) > 0:
-            while len(self.pkts.pktq) > 0:
-                a = self.pkts.pktq.pop(0)
-                #log.msg("type is " + str(type(a)))
-                if type(a) == type(zb.RX_16_Bit()):
-                    #log.msg("Rx'd from %d => %s" % (a.sender, a.payloadstr))
-                    for f in self.factories:
-                        f.message(a.sender, a.payloadstr)
-                if type(a) == type(zb.TX_Status()):
-                    #log.msg("Tx status for frame %d is %s" % (a.frameid, a.statusMsg))
-                    pass
-                
-    def sendData(self, zbid, data):
-        """Sends a chunk of data to our ZB module"""
-        #log.msg("%d <= %s" % (zbid, data))
-        
-        # Chop up data into pieces the module can handle
-        maxsz = zb.TX_16_Bit.PKT_MAX_PAYLOAD
-        for i, j in zip(range(0, len(data), maxsz), range(maxsz, len(data) + maxsz, maxsz)):
-            p = zb.TX_16_Bit(zbid, data[i:j])
-            self.transport.write(p.Pack())
-            #log.msg("sent " + str(p))
-            
-if __name__ == '__main__':
-    logFile = sys.stdout
-    log.startLogging(logFile)
-
-    # ZigBee serial protocol handler
-    zbproto = ZBProto()
-    SerialPort(zbproto, portname, reactor, baudrate = 38400)
-
-    # Per-module TCP listener
-    for id in zbids:
-        f = ZBFactory(id, lognamebase)
-        f.zbproto = zbproto
-        f.protocol = lambda: TelnetTransport(TelnetBootstrapProtocol,
-                                             insults.ServerProtocol,
-                                             ZBClient)
-        zbproto.factories.append(f)
-        reactor.listenTCP(baseport + id, f)
-
-    reactor.run()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zbmux.tac	Sun Jan 18 13:32:29 2009 +1030
@@ -0,0 +1,185 @@
+#
+# Mux the ZB module to TCP ports
+#
+# Copyright (c) 2009
+#      Daniel O'Connor <darius@dons.net.au>.  All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+from twisted.application import internet, service
+from twisted.internet.serialport import SerialPort
+from twisted.internet.protocol import Protocol
+from twisted.conch.telnet import TelnetTransport, TelnetBootstrapProtocol
+from twisted.conch.insults import insults
+from twisted.protocols import basic
+from twisted.internet import protocol, reactor
+from twisted.python import log
+import sys, zb, logging, logging.handlers, string
+
+portname = '/dev/cuaU0'
+baudrate = 38400
+lognamebase = '/tmp/zbmux-%d.log'
+baseport = 1080
+zbids = [1, 2]
+
+class ZBClient(insults.TerminalProtocol):
+    """Client for the TCP connection"""
+    #: Time to wait before sending pending data (seconds)
+    QUEUE_TIME = 0.1
+    
+    def connectionMade(self):
+        log.msg("Got new client")
+        self.terminal.eraseDisplay()
+        self.terminal.resetPrivateModes([])
+        
+        # Send the last whole line we've seen out
+        for l in self.factory.lastlines:
+            self.message(l + '\n')
+            
+        self.pending = ""
+        self.pendtimer = None
+        self.factory.clients.append(self)
+        
+    def connectionLost(self, reason):
+        log.msg("Lost a client")
+        self.factory.clients.remove(self)
+        
+    def keystrokeReceived(self, keyID, modifier):
+        """Got some data, add it to the pending output queue"""
+        if modifier != None:
+            print "Received unhandled modifier: %s" % (str(modifier))
+            return
+        #if keyID not in string.printable:
+        #    print "Received unhandled keyID: %r" % (keyID,)
+        #    return
+        #log.msg("Got key ->%s<-" % (keyID))
+        self.pending = self.pending + keyID
+        if self.pendtimer == None:
+            self.pendtimer = reactor.callLater(self.QUEUE_TIME, self.sendData)
+            
+    def sendData(self):
+        """Send pending data to module"""
+        #log.msg("sending " + self.pending)
+        self.factory.zbproto.sendData(self.factory.zbid, self.pending)
+        self.pending = ""
+        self.pendtimer = None
+        
+    def message(self, message):
+        """Called to write a mesage to our client"""
+        self.terminal.write(message)
+        
+class ZBFactory(protocol.ServerFactory):
+    """Factory for a ZB module
+
+Represents a remote ZB module and has zero or more clients and a log file.
+"""
+    protocol = ZBClient
+    
+    def __init__(self, zbid, lognamebase):
+        self.zbid = zbid
+        self.clients = []
+        self.tmpline = ""
+        self.lastlines = []
+
+        # Open logger
+        self.logger = logging.getLogger('Zigbee-%d' % (zbid))
+        self.logger.setLevel(logging.DEBUG)
+
+        # Add the log message handler to the logger
+        handler = logging.handlers.RotatingFileHandler(
+            lognamebase % (zbid), maxBytes = 1 * 1024 * 1024, backupCount = 5)
+
+        self.logger.addHandler(handler)
+
+    def message(self, zbid, message):
+        """Called when we get a message, check it's for us - if it is log it and write to our clients"""
+        if zbid != self.zbid:
+            return
+        
+        for c in self.clients:
+            c.message(message)
+
+        # Logger is line oriented, convert from packet oriented here
+        self.tmpline = self.tmpline + message
+        tmp = self.tmpline.split('\n')
+        for l in tmp[0:-1]:
+            self.lastlines.append(l)
+            self.lastlines = self.lastlines[-5:]
+            self.logger.debug(l.replace('\n', ''))
+        self.tmpline = tmp[-1]
+
+class ZBProto(Protocol):
+    """Protocol to handle packets from the ZB module on the serial port"""
+    def __init__(self):
+        self.pkts = zb.Packets()
+        self.factories = []
+        
+    def dataReceived(self, data):
+        """Parses data from ZB module into packets, calls each factory if a RX packet is received"""
+        #log.msg("Read data " + data)
+        if self.pkts.processstr(data) > 0:
+            while len(self.pkts.pktq) > 0:
+                a = self.pkts.pktq.pop(0)
+                #log.msg("type is " + str(type(a)))
+                if type(a) == type(zb.RX_16_Bit()):
+                    #log.msg("Rx'd from %d => %s" % (a.sender, a.payloadstr))
+                    for f in self.factories:
+                        f.message(a.sender, a.payloadstr)
+                if type(a) == type(zb.TX_Status()):
+                    #log.msg("Tx status for frame %d is %s" % (a.frameid, a.statusMsg))
+                    pass
+                
+    def sendData(self, zbid, data):
+        """Sends a chunk of data to our ZB module"""
+        #log.msg("%d <= %s" % (zbid, data))
+        
+        # Chop up data into pieces the module can handle
+        maxsz = zb.TX_16_Bit.PKT_MAX_PAYLOAD
+        for i, j in zip(range(0, len(data), maxsz), range(maxsz, len(data) + maxsz, maxsz)):
+            p = zb.TX_16_Bit(zbid, data[i:j])
+            self.transport.write(p.Pack())
+            #log.msg("sent " + str(p))
+            
+application = service.Application('zbmux')
+
+# ZigBee serial protocol handler
+zbproto = ZBProto()
+SerialPort(zbproto, portname, reactor, baudrate = 38400)
+
+# Per-module TCP listener
+for id in zbids:
+    f = ZBFactory(id, lognamebase)
+    f.zbproto = zbproto
+    f.protocol = lambda: TelnetTransport(TelnetBootstrapProtocol,
+                                         insults.ServerProtocol,
+                                         ZBClient)
+    zbproto.factories.append(f)
+    internet.TCPServer(baseport + id, f).setServiceParent(
+        service.IServiceCollection(application))
+
+######################################################################
+#
+# These lines tell Emacs to edit this file in Python mode
+#;;; Local Variables: ***
+#;;; mode:python ***
+#;;; End: ***