Use SO_REUSEADDR.
[zanchey/dispense2.git] / virtualvend / vvend.py
1 #!/usr/bin/env python
2
3 import sys
4 import socket
5 import string
6
7 try:
8   import pygtk
9   #tell pyGTK, if possible, that we want GTKv2
10   pygtk.require("2.0")
11 except:
12   #Some distributions come with GTK2, but not pyGTK
13   pass
14
15 try:
16   import gtk
17   import gtk.glade
18 except:
19   print "You need to install pyGTK or GTKv2 ",
20   print "or set your PYTHONPATH correctly."
21   print "try: export PYTHONPATH=",
22   print "/usr/local/lib/python2.2/site-packages/"
23   sys.exit(1)
24
25 #now we have both gtk and gtk.glade imported
26 #Also, we know we are running GTK v2
27
28 class appgui:
29   def __init__(self):
30     """
31     In this init we are going to display the main
32     serverinfo window
33     """
34     gladefile="vvend.glade"
35     windowname="vvend"
36     self.wTree=gtk.glade.XML (gladefile,windowname)
37     # we only have two callbacks to register, but
38     # you could register any number, or use a
39     # special class that automatically
40     # registers all callbacks. If you wanted to pass
41     # an argument, you would use a tuple like this:
42     # dic = { "on button1_clicked" : 
43     #         (self.button1_clicked, arg1,arg2) , ...
44     
45     dic = { 
46         "on_button1_clicked" : self.keypad_clicked,
47         "on_button2_clicked" : self.keypad_clicked,
48         "on_button3_clicked" : self.keypad_clicked,
49         "on_button4_clicked" : self.keypad_clicked,
50         "on_button5_clicked" : self.keypad_clicked,
51         "on_button6_clicked" : self.keypad_clicked,
52         "on_button7_clicked" : self.keypad_clicked,
53         "on_button8_clicked" : self.keypad_clicked,
54         "on_button9_clicked" : self.keypad_clicked,
55         "on_button10_clicked" : self.keypad_clicked,
56         "on_button11_clicked" : self.keypad_clicked,
57         "on_vvend_destroy_event" : self.quit,
58         "on_vvend_delete_event" : self.quit }
59     self.wTree.signal_autoconnect (dic)
60     display = self.wTree.get_widget("label1")
61     display.set_text("*5N4CK0RZ*")
62
63     # vending machine password set here
64     self.vendpw = "AAAAAAAAAAAAAAAA"
65
66     self.messageid = None
67
68     #s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
69     #s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
70 #
71 #    listenhost=""
72 #    listenport=5150
73 #    s.bind((listenhost, listenport))
74 #    # only one connection
75 #    s.listen(1)
76 #    s.setblocking(1)
77 #    #GDK->gtk.gdk in GTK V 2.0
78 #    id=gtk.input_add(s, gtk.gdk.INPUT_READ, self.handleNewConnection)
79
80     self.server()
81
82
83     return
84
85     def __del__(self):
86         try:
87             self.sock.close()
88             self.sock.shutdown()
89         except:
90             pass
91
92 #####CALLBACKS
93   def keypad_clicked(self,widget):
94     key = widget.get_label()
95     if key == 'RESET':
96         key = '11'
97     else:
98         key = '0'+key
99     self.do_send('2'+key+' keypress\n')
100
101   def handleNewConnection(self,source,condition):
102     #source is a socket in GTK v 1 and a fd in version 2
103     conn, addr = source.accept()
104     sys.stdout.write(conn.recv(1))
105     conn.send("bing\n")
106     return gtk.TRUE
107
108 # from http://www.pythonbrasil.com.br/moin.cgi/MonitorandoSocketsComPygtk
109
110
111   def send(self, data=None, widget=None):
112      text = self.entry.get_text()
113      self.do_send(text)
114      self.entry.set_text('')
115
116      #
117
118   def do_send(self, data):
119
120      # envia ''data'' para todos os clientes conectados
121
122      for addr, (conn, tag) in self.clients.iteritems():
123             conn.send(data)
124
125   def server(self):
126
127      # inicializa o servidor
128      port = 5150
129
130      self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
131      self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
132      self.sock.bind(('localhost', port))
133      self.sock.listen(0)
134      print "listening on ", port
135
136      # 
137      #
138
139      self.server_tag = gtk.input_add(self.sock, gtk.gdk.INPUT_READ, self.accept)
140
141      # mantemos uma lista dos clientes conectados
142
143      self.clients = {}
144
145   def accept(self, source, condition):
146
147      #
148      # esperando para ser aceito
149
150      conn, addr = source.accept()
151      self.insert("%s:%s conectado\n" % addr)
152
153      # insere o cliente na lista e registra o método self.write como 
154      # callback para quando existirem dados esperando para serem lidos
155
156      self.do_prompt()
157      self.clients[addr] = (conn, gtk.input_add(conn, gtk.gdk.INPUT_READ, self.write))
158
159   def write(self, source, condition):
160
161      # método chamado quando um cliente envia dados 
162
163      data = source.recv(1024)
164      if data.strip() == 'bye' or not len(data):
165
166             # se o cliente enviar um ''bye'', desconecte-o :)
167
168             source.close()
169             for addr, (conn, tag) in self.clients.iteritems():
170                 if source is conn:
171                     gtk.input_remove(tag)
172                     self.insert('%s:%s desconectado\n' % addr)
173                     del self.clients[addr]
174                     
175                     self.server_tag = gtk.input_add(self.sock, gtk.gdk.INPUT_READ, self.accept)
176                     break
177      elif data.strip() == 'quit':
178         self.quit()
179      else:
180             for (addr, port), (conn, tag) in self.clients.iteritems():
181                 if source is conn:
182                     self.insert('%s:%s >>> %s\n'%(addr, port, data.strip()))
183                     self.handle_command(data.strip())
184                     break
185      
186      return gtk.TRUE
187
188   def insert(self, data):
189      statusbar = self.wTree.get_widget("statusbar1")
190      if self.messageid:
191         statusbar.remove(1, self.messageid)
192      self.messageid=statusbar.push(1,data)
193
194   def quit(self, *args):
195      sys.stdout.write("quiting...\n")
196      gtk.input_remove(self.server_tag)
197      for addr, (conn, tag) in self.clients.iteritems():
198             gtk.input_remove(tag)
199             conn.close()
200      self.sock.close()
201
202      gtk.mainquit()
203      sys.stdout.write("quit!\n")
204
205   def do_prompt(self):
206     self.do_send("# ")
207
208   def do_help(self):
209     help = """
210
211 Valid commands are:
212  ABOUT         ROM information
213  B[S][nn]      beep [synchronously] for a duration nn (optional)
214  C[S][nn]      silence [synchronously] for a duration nn (optional)
215  Dxxxxxxxxxx   show a message on the display
216  ECHO {ON|OFF} turn echo on or off
217  GETROM        download the ROM source code using xmodem
218  H[...]        this help screen
219 *JUMPxxxx      jumps to a subroutine at location xxxx
220 *PEEKxxxx      returns the value of the byte at location xxxx
221 *POKExxxxyy    sets the value of location xxxx to yy
222  PING          pongs
223  S[...]        query all internal switch states
224 +Vnn           vend an item
225 +VALL          vend all items
226 *Wxxxxxxxxxxxx set a new password for authenticated vends. xxx=16 chars
227                password will be converted to uppercase
228
229 Very few functions are available when the machine is in standalone 
230 mode (DIP SW 1 is set)
231 + denotes that this item requires authentication if DIP SW 2 is set
232 * denotes that DIP SW 3 must be set to use these
233 Commands starting with # are ignored (comments)
234 """
235     self.do_send(help)
236   
237   def do_about(self):
238     about = """
239
240 The Virtual Vending Machine Company
241
242 Mark Tearle, June 2004
243 """
244     self.do_send(about)
245
246   def do_vend_all(self):
247      for i in range(11,99):
248         self.do_send("101 Vending "+i+"\n")
249         self.do_send("153 Home sensors failing\n")
250      self.do_send("102 Vend all motors complete\n")
251   
252   def do_vend(self,command):
253      fail = None
254      if fail:
255         self.do_send("153 Home sensors failing\n")
256      else:
257         self.insert("Vending ",command)
258         self.do_send("100 Vend successful\n")
259     
260   def do_display(self,string):
261      display = self.wTree.get_widget("label1")
262      display.set_text("%10.10s" % (string))
263      self.do_send('300 Written\n')
264
265   def do_beep(self,command):
266      sys.stdout.write("\a")
267
268   def do_silence(self,command):
269      pass
270
271   def do_switches(self):
272      self.do_send("600 3F 3F\n")
273
274   def do_pong(self):
275      self.do_send("000 PONG!\n")
276
277   def handle_command(self, command):
278      command = string.upper(command)
279      print command
280      if string.find(command, "HELP",0) == 0:
281         self.do_help()
282      elif string.find(command, "ABOUT",0) == 0:
283         self.do_about()
284      elif string.find(command, "PING",0) == 0:
285         self.do_pong()
286      elif string.find(command, "VALL",0) == 0:
287         self.do_vend_all()
288      elif string.find(command, "V",0) == 0:
289         self.do_vend(command)
290      elif string.find(command, "B",0) == 0:
291         self.do_beep(command)
292      elif string.find(command, "C",0) == 0:
293         self.do_silence(command)
294      elif string.find(command, "S",0) == 0:
295         self.do_switches()
296      elif string.find(command, "D",0) == 0:
297         self.do_display(command[1:])
298      self.do_prompt()
299         
300
301 # we start the app like this...
302 app=appgui()
303 gtk.mainloop()
304
305
306 # notes
307 # http://www.async.com.br/faq/pygtk/index.py?req=show&file=faq20.011.htp

UCC git Repository :: git.ucc.asn.au