summaryrefslogtreecommitdiff
path: root/host/mex/mex/node.py
blob: 15cc5c2e79464138c3ebdae282dc21e63467c52d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# mex - Messages exchange library. {{{
#
# Copyright (C) 2008 Nicolas Schodet
#
# APBTeam:
#        Web: http://apbteam.org/
#      Email: team AT apbteam DOT org
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# }}}
"""mex Node."""
import mex
from msg import Msg
import msg
import socket
from struct import pack, unpack, calcsize

class Node:

    class closed:
	"""Raised on closed connection."""
	pass

    def __init__ (self, addr = mex.DEFAULT_ADDR):
	"""Create a new Node and connect it to given Hub address."""
	self.socket = socket.socket ()
	self.socket.connect (addr)
	self.date = 0
	self.seq = 0
	self.req = None
	self.handlers = { }
	self.register (mex.DATE, lambda msg: self.handle_DATE (msg))
	self.register (mex.REQ, lambda msg: self.handle_REQ (msg))

    def wait (self, date = None):
	"""Wait forever or until a date is reached."""
	while date == None or self.date != date:
	    idle = Msg (mex.IDLE)
	    if date != None:
		idle.push ('L', date)
	    self.send (idle)
	    msg = self.recv ()
	    self.dispatch (msg)

    def send (self, msg):
	"""Send a message."""
	data = msg.data ()
	packet = pack (mex.HEADER_FMT, len (data), self.seq) + data
	self.socket.sendall (packet)

    def request (self, msg):
	"""Send a request and return response."""
	# Send request.
	req = Msg (mex.REQ)
	req.push ('B', 0)
	req.push (msg.data ())
	self.send (req)
	# Wait for response.
	rsp = self.recv ()
	while rsp.mtype != mex.RSP:
	    self.dispatch (rsp)
	    rsp = self.recv ()
	# Discard reqid.
	rsp.pop ('B')
	return Msg (rsp.pop ())

    def response (self, msg):
	"""Send a response to the currently serviced request."""
	assert self.req != None
	rsp = Msg (mex.RSP)
	rsp.push ('B', self.req)
	self.req = None
	rsp.push (msg.data ())
	self.send (rsp)

    def register (self, mtype, handler):
	"""Register an handler for the given message type."""
	assert mtype not in self.handlers
	self.handlers[mtype] = handler

    def close (self):
	"""Close connection with the Hub."""
	self.socket.close ()
	self.socket = None

    def recv (self):
	"""Receive one message."""
	head = self.socket.recv (calcsize (mex.HEADER_FMT))
	if head == '':
	    self.close ()
	    raise Node.closed
	size, self.seq = unpack (mex.HEADER_FMT, head)
	data = self.socket.recv (size)
	return Msg (data)

    def dispatch (self, msg):
	"""Call the right handler for the given message."""
	if msg.mtype in self.handlers:
	    self.handlers[msg.mtype] (msg)

    def handle_DATE (self, msg):
	"""Handle an incoming DATE."""
	self.date, = msg.pop ('L')

    def handle_REQ (self, msg):
	"""Handle an incoming REQ."""
	self.req, = msg.pop ('B')
	dec = Msg (msg.pop ())
	self.dispatch (dec)
	self.req = None