1 """
2 FILE: multiprotocol.py defines the MultiProtocol class, a class that maps
3 multiple Protocol instances under a single umbrella, allowing multiple robot
4 communication busses and protocols to be used from a single user application
5 """
6
7 from ckmodule import AbstractProtocol, AbstractBus
8
10 """class NidMapper implements a mapping between "external" and "internal"
11 node ID values. The internal node ID values are associated with owners,
12 which are typically Protocol instances.
13
14 IMPLEMENTATION:
15
16 The current implementation is silly -- it gives each owner an address
17 space of a given number of bits (default 8). External node ID values
18 consist of owner ID bits followed by node ID bits
19 """
20
22 self.tbl = {}
23 self.itbl = {}
24 self.bitShift = bits
25 self.mask = (1L<<bits)-1
26
28 """Iterate over all the node owners"""
29 return self.tbl.itervalues()
30
32 """
33 Add a new node owner
34
35 INPUT:
36 owner -- hashable -- "owner" of a node range; must be hashable
37 oid -- int -- owner ID to use, or None to have one generated
38 If an owner ID is re-used, an exception will be raised
39 """
40 if oid is None:
41 oid = len(self.tbl)+1
42 else:
43 oid = int(oid)
44 if self.tbl.has_key(oid):
45 raise KeyError("Owner ID %d re-uses existing ID" % oid )
46 self.tbl[oid] = owner
47 self.itbl[hash(owner)] = oid
48
50 """Map external node ID into an owner and internal node ID"""
51 nid = int(nid)
52 oid = nid >> self.bitShift
53
54 owner = self.tbl[oid]
55 inid = (nid & self.mask)
56 return owner, inid
57
58 - def mapI2X( self, owner, nid ):
59 """Map owner and node ID into external node ID"""
60 nid = int(nid)
61 oh = hash(owner)
62 assert nid == (nid & self.mask), "Node ID is within address range"
63 return (self.itbl[oh]<<self.bitShift) | nid
64
67 """
68 Create a protocol which multiplexes multiple sub-protocols
69
70 For convenience, these sub-protocols can be listed sequentially
71 in the constructor
72 """
73 AbstractProtocol.__init__(self)
74 self.nim = NidMapper()
75 if subs:
76 if isinstance(subs[0], AbstractBus):
77 raise ValueError("MultiProtocol() cannot accept a Bus parameter")
78 self.addSubProtocols(*subs)
79
81 """
82 Add one or more sub-protocols
83 """
84 for sub in subs:
85 self.nim.addOwner( sub )
86
88 """Broadcast the p.off() call to any supporting subs"""
89 for o in self.nim.iterowners():
90 if hasattr(o,'off') and callable(o.off):
91 o.off()
92
93 - def reset( self, *argv, **kwarg ):
94 """Broadcast the p.reset() call to any supporting subs"""
95 for o in self.nim.iterowners():
96 if hasattr(o,'reset') and callable(o.reset):
97 o.reset(*argv, **kwarg)
98
99 - def scan( self, *argv, **kwarg ):
100 """
101 Broadcast the p.scan() call to any supporting subs, then
102 collect results and map to external NIDs
103 """
104 res = []
105 for p in self.nim.iterowners():
106 if not hasattr(p,'scan') or not callable(p.scan):
107 continue
108 s = p.scan(*argv, **kwarg)
109 res.extend([ self.nim.mapI2X(p,inid) for inid in s])
110 return res
111
113 """Update all sub-protocols and collect heartbeats"""
114 hb = {}
115
116 for p in self.nim.iterowners():
117 p.update()
118
119 for inid,val in p.heartbeats.iteritems():
120 xnid = self.nim.mapI2X(p,inid)
121 hb[xnid] = val
122 self.heartbeats = hb
123
125 """Hint the existence of specified NIDs"""
126 tbl = {}
127
128 for xnid in nodes:
129 o,inid = self.nim.mapX2I(xnid)
130 if tbl.has_key(o):
131 tbl[o].append(inid)
132 else:
133 tbl[o] = [inid]
134
135 for p,nids in tbl.iteritems():
136 p.hintNodes(nids)
137
139 """
140 Generate a ProtocolNodeAdaptor for the specified nid, using the
141 protocol which owns it
142 """
143 p,inid = self.nim.mapX2I(nid)
144 return p.generatePNA(inid)
145
146 if __name__=="__main__":
147
148 import ckbot.nobus as NB
149 from ckbot.logical import Cluster
150 p1 = NB.Protocol()
151 p2 = NB.Protocol()
152 NIDS = [0x101, 0x102, 0x107, 0x202, 0x203]
153 mp = MultiProtocol(p1,p2)
154 c = Cluster(
155 arch=mp,count=len(NIDS),fillMissing=True, required=NIDS
156 )
157