Package ckbot :: Module multiprotocol
[hide private]
[frames] | no frames]

Source Code for Module ckbot.multiprotocol

  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   
9 -class NidMapper( object ):
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
21 - def __init__( self, bits=8 ):
22 self.tbl = {} 23 self.itbl = {} 24 self.bitShift = bits 25 self.mask = (1L<<bits)-1
26
27 - def iterowners( self ):
28 """Iterate over all the node owners""" 29 return self.tbl.itervalues()
30
31 - def addOwner( self, owner, oid=None ):
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
49 - def mapX2I( self, nid ):
50 """Map external node ID into an owner and internal node ID""" 51 nid = int(nid) 52 oid = nid >> self.bitShift 53 # This looks for the owner; will fail with KeyError if owner ID unknown 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
65 -class MultiProtocol( AbstractProtocol ):
66 - def __init__(self,*subs):
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
80 - def addSubProtocols(self, *subs):
81 """ 82 Add one or more sub-protocols 83 """ 84 for sub in subs: 85 self.nim.addOwner( sub )
86
87 - def off( self ):
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
112 - def update( self ):
113 """Update all sub-protocols and collect heartbeats""" 114 hb = {} 115 # Loop over all protocols contained in this one 116 for p in self.nim.iterowners(): 117 p.update() 118 # Collect heartbeats from the protocols 119 for inid,val in p.heartbeats.iteritems(): 120 xnid = self.nim.mapI2X(p,inid) 121 hb[xnid] = val 122 self.heartbeats = hb
123
124 - def hintNodes( self, nodes ):
125 """Hint the existence of specified NIDs""" 126 tbl = {} 127 # Collect nodes by owner 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 # Hint the relevant nodes to each sub-protocol 135 for p,nids in tbl.iteritems(): 136 p.hintNodes(nids)
137
138 - def generatePNA( self, nid ):
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 # Unit test 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