Download the file
  1. #!/usr/bin/python
  2.  
  3. # This program is free software: you can redistribute it and/or modify it
  4. # under the terms of the GNU Affero General Public License as published by the
  5. # Free Software Foundation, either version 3 of the License, or (at your
  6. # option) any later version.
  7. #
  8. # This program is distributed in the hope that it will be useful, but WITHOUT
  9. # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  10. # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  11. # more details.
  12. #
  13. # You should have received a copy of the GNU Affero General Public License
  14. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  15.  
  16. # Matthieu Weber, 2011
  17.  
  18.  
  19. import asyncore, socket, serial, time
  20.  
  21. MAX_MESSAGE_LENGTH = 1024
  22.  
  23.  
  24. class Client(asyncore.dispatcher):
  25.  
  26. def __init__(self, host, socket, address):
  27. asyncore.dispatcher.__init__(self, socket)
  28. self.host = host
  29. self.messages = []
  30.  
  31. def handle_read(self):
  32. client_message = self.recv(MAX_MESSAGE_LENGTH)
  33. #print "C:read '%s'" % client_message
  34. self.host.push(client_message)
  35.  
  36. def push(self, message):
  37. #print "C:push '%s'" % message
  38. self.messages.append(message)
  39.  
  40. def writable(self):
  41. return bool(self.messages)
  42.  
  43. def handle_write(self):
  44. msg = self.messages.pop(0)
  45. #print "C:write '%s'" % msg
  46. self.send(msg)
  47.  
  48. def handle_close(self):
  49. #print "C:close"
  50. self.host.remove_client(self)
  51.  
  52. class SerialChannel(asyncore.file_dispatcher):
  53. def __init__(self, server, serial_port):
  54. self.serial_port = serial_port
  55. asyncore.file_dispatcher.__init__(self, self.serial_port.fileno())
  56. self.server = server
  57. self.outdata = ""
  58. self.indata = ""
  59.  
  60. def push(self, data):
  61. #print "S:push '%s'" % data
  62. self.outdata += data
  63.  
  64. def handle_write(self):
  65. #print "S:write '%s'" % self.outdata
  66. sent = self.send(self.outdata)
  67. self.outdata = self.outdata[sent:]
  68.  
  69. def writable(self):
  70. return bool(self.outdata)
  71.  
  72. def handle_read(self):
  73. self.indata += self.recv(128)
  74. while '\n' in self.indata:
  75. idx = self.indata.find('\n')
  76. frame = self.indata[:idx+1]
  77. self.indata = self.indata[idx + 1:]
  78. #print "S:read '%s' left '%s'" % (frame, self.indata)
  79. self.server.broadcast(frame)
  80.  
  81. def handle_close(self):
  82. #print "S:close"
  83. self.server.serial_closed()
  84. self.close()
  85.  
  86. class Host(asyncore.dispatcher):
  87. def __init__(self, address=('localhost', 9876)):
  88. asyncore.dispatcher.__init__(self)
  89. self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
  90. self.set_reuse_addr()
  91. self.bind(address)
  92. self.listen(1)
  93. self.remote_clients = set()
  94. self.serial_channel = None
  95. self.serial_retry = 0.0
  96. self.connect_serial()
  97.  
  98. def handle_accept(self):
  99. socket, addr = self.accept() # For the remote client.
  100. self.remote_clients.add(Client(self, socket, addr))
  101. #print "H:accept %s" % str(addr)
  102.  
  103. def handle_read(self):
  104. pass
  105.  
  106. def readable(self):
  107. if not self.serial_channel and time.time() > self.serial_retry:
  108. self.connect_serial()
  109. return True
  110.  
  111. def push(self, message):
  112. if not self.serial_channel and time.time() > self.serial_retry:
  113. self.connect_serial()
  114. if self.serial_channel:
  115. self.serial_channel.push(message)
  116.  
  117. def broadcast(self, message):
  118. for client in self.remote_clients:
  119. #print "H:broadcast '%s' to '%s'" % (message, str(client))
  120. client.push(message)
  121.  
  122. def remove_client(self, client):
  123. client.close()
  124. self.remote_clients.remove(client)
  125. #print "H:remove client"
  126.  
  127. def connect_serial(self):
  128. try:
  129. ser = serial.Serial('/dev/ttyUSB0', 57600, timeout=0.1)
  130. self.serial_channel = SerialChannel(self, ser)
  131. #print "H:connected"
  132. except serial.serialutil.SerialException:
  133. #print "H:retry later"
  134. self.serial_retry = time.time() + 1
  135.  
  136. def serial_closed(self):
  137. self.serial_channel = None
  138. self.serial_retry = time.time() + 1
  139. #print "H:serial_closed()"
  140.  
  141.  
  142. if __name__ == '__main__':
  143. host = Host()
  144. asyncore.loop(timeout=1.0)
  145.  
  146.  
  147.  
  148.