DirectDeviceManager.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. """Class used to create and control VRPN devices."""
  2. from direct.showbase.DirectObject import DirectObject
  3. from panda3d.core import *
  4. from panda3d.vrpn import *
  5. ANALOG_MIN = -0.95
  6. ANALOG_MAX = 0.95
  7. ANALOG_DEADBAND = 0.125
  8. ANALOG_CENTER = 0.0
  9. class DirectDeviceManager(VrpnClient, DirectObject):
  10. def __init__(self, server = None):
  11. # Determine which server to use
  12. if server is not None:
  13. # One given as constructor argument
  14. self.server = server
  15. else:
  16. # Check config file, if that fails, use default
  17. self.server = ConfigVariableString('vrpn-server', 'spacedyne').getValue()
  18. # Create a vrpn client
  19. VrpnClient.__init__(self, self.server)
  20. def createButtons(self, device):
  21. return DirectButtons(self, device)
  22. def createAnalogs(self, device):
  23. return DirectAnalogs(self, device)
  24. def createTracker(self, device):
  25. return DirectTracker(self, device)
  26. def createDials(self, device):
  27. return DirectDials(self, device)
  28. def createTimecodeReader(self, device):
  29. return DirectTimecodeReader(self, device)
  30. class DirectButtons(ButtonNode, DirectObject):
  31. buttonCount = 0
  32. def __init__(self, vrpnClient, device):
  33. # Keep track of number of buttons created
  34. DirectButtons.buttonCount += 1
  35. # Create a new button node for the given device
  36. ButtonNode.__init__(self, vrpnClient, device)
  37. # Create a unique name for this button object
  38. self.name = 'DirectButtons-' + repr(DirectButtons.buttonCount)
  39. # Attach node to data graph
  40. try:
  41. self._base = base
  42. except:
  43. self._base = simbase
  44. self.nodePath = self._base.dataRoot.attachNewNode(self)
  45. def __getitem__(self, index):
  46. if (index < 0) or (index >= self.getNumButtons()):
  47. raise IndexError
  48. return self.getButtonState(index)
  49. def __len__(self):
  50. return self.getNumButtons()
  51. def enable(self):
  52. self.nodePath.reparentTo(self._base.dataRoot)
  53. def disable(self):
  54. self.nodePath.reparentTo(self._base.dataUnused)
  55. def getName(self):
  56. return self.name
  57. def getNodePath(self):
  58. return self.nodePath
  59. def __repr__(self):
  60. string = self.name + ': '
  61. for val in self:
  62. string = string + '%d' % val + ' '
  63. return string
  64. class DirectAnalogs(AnalogNode, DirectObject):
  65. analogCount = 0
  66. _analogDeadband = ConfigVariableDouble('vrpn-analog-deadband', ANALOG_DEADBAND)
  67. _analogMin = ConfigVariableDouble('vrpn-analog-min', ANALOG_MIN)
  68. _analogMax = ConfigVariableDouble('vrpn-analog-max', ANALOG_MAX)
  69. _analogCenter = ConfigVariableDouble('vrpn-analog-center', ANALOG_CENTER)
  70. def __init__(self, vrpnClient, device):
  71. # Keep track of number of analogs created
  72. DirectAnalogs.analogCount += 1
  73. # Create a new analog node for the given device
  74. AnalogNode.__init__(self, vrpnClient, device)
  75. # Create a unique name for this analog object
  76. self.name = 'DirectAnalogs-' + repr(DirectAnalogs.analogCount)
  77. # Attach node to data graph
  78. try:
  79. self._base = base
  80. except:
  81. self._base = simbase
  82. self.nodePath = self._base.dataRoot.attachNewNode(self)
  83. # See if any of the general analog parameters are dconfig'd
  84. self.analogDeadband = self._analogDeadband.getValue()
  85. self.analogMin = self._analogMin.getValue()
  86. self.analogMax = self._analogMax.getValue()
  87. self.analogCenter = self._analogCenter.getValue()
  88. self.analogRange = self.analogMax - self.analogMin
  89. def __getitem__(self, index):
  90. if (index < 0) or (index >= self.getNumControls()):
  91. raise IndexError
  92. return self.getControlState(index)
  93. def __len__(self):
  94. return self.getNumControls()
  95. def enable(self):
  96. self.nodePath.reparentTo(self._base.dataRoot)
  97. def disable(self):
  98. self.nodePath.reparentTo(self._base.dataUnused)
  99. def normalizeWithoutCentering(self, val, minVal = -1, maxVal = 1):
  100. #
  101. # This is the old code that doesn't incorporate the centering fix
  102. #
  103. # First record sign
  104. if val < 0:
  105. sign = -1
  106. else:
  107. sign = 1
  108. # Zero out values in deadband
  109. val = sign * max(abs(val) - self.analogDeadband, 0.0)
  110. # Clamp value between analog range min and max and scale about center
  111. val = min(max(val, self.analogMin), self.analogMax)
  112. # Normalize values to given minVal and maxVal range
  113. return (((maxVal - minVal) *
  114. ((val - self.analogMin) / float(self.analogRange))) + minVal)
  115. def normalize(self, rawValue, minVal = -1, maxVal = 1, sf = 1.0):
  116. aMax = self.analogMax
  117. aMin = self.analogMin
  118. center = self.analogCenter
  119. deadband = self.analogDeadband
  120. # Zero out values in deadband
  121. if abs(rawValue - center) <= deadband:
  122. return 0.0
  123. # Clamp value between aMin and aMax and scale around center
  124. if rawValue >= center:
  125. # Convert positive values to range 0 to 1
  126. val = min(rawValue * sf, aMax)
  127. percentVal = ((val - (center + deadband))/
  128. float(aMax - (center + deadband)))
  129. else:
  130. # Convert negative values to range -1 to 0
  131. val = max(rawValue * sf, aMin)
  132. percentVal = -((val - (center - deadband))/
  133. float(aMin - (center - deadband)))
  134. # Normalize values to given minVal and maxVal range
  135. return ((maxVal - minVal) * ((percentVal + 1)/2.0)) + minVal
  136. def normalizeChannel(self, chan, minVal = -1, maxVal = 1, sf = 1.0):
  137. try:
  138. return self.normalize(self[chan], minVal, maxVal, sfx)
  139. except IndexError:
  140. return 0.0
  141. def getName(self):
  142. return self.name
  143. def getNodePath(self):
  144. return self.nodePath
  145. def __repr__(self):
  146. string = self.name + ': '
  147. for val in self:
  148. string = string + '%.3f' % val + ' '
  149. return string
  150. class DirectTracker(TrackerNode, DirectObject):
  151. trackerCount = 0
  152. def __init__(self, vrpnClient, device):
  153. # Keep track of number of trackers created
  154. DirectTracker.trackerCount += 1
  155. # Create a new tracker node for the given device
  156. TrackerNode.__init__(self, vrpnClient, device)
  157. # Create a unique name for this tracker object
  158. self.name = 'DirectTracker-' + repr(DirectTracker.trackerCount)
  159. # Attach node to data graph
  160. try:
  161. self._base = base
  162. except:
  163. self._base = simbase
  164. self.nodePath = self._base.dataRoot.attachNewNode(self)
  165. def enable(self):
  166. self.nodePath.reparentTo(self._base.dataRoot)
  167. def disable(self):
  168. self.nodePath.reparentTo(self._base.dataUnused)
  169. def getName(self):
  170. return self.name
  171. def getNodePath(self):
  172. return self.nodePath
  173. def __repr__(self):
  174. return self.name
  175. class DirectDials(DialNode, DirectObject):
  176. dialCount = 0
  177. def __init__(self, vrpnClient, device):
  178. # Keep track of number of dials created
  179. DirectDials.dialCount += 1
  180. # Create a new dial node for the given device
  181. DialNode.__init__(self, vrpnClient, device)
  182. # Create a unique name for this dial object
  183. self.name = 'DirectDials-' + repr(DirectDials.dialCount)
  184. # Attach node to data graph
  185. try:
  186. self._base = base
  187. except:
  188. self._base = simbase
  189. self.nodePath = self._base.dataRoot.attachNewNode(self)
  190. def __getitem__(self, index):
  191. """
  192. if (index < 0) or (index >= self.getNumDials()):
  193. raise IndexError
  194. """
  195. return self.readDial(index)
  196. def __len__(self):
  197. return self.getNumDials()
  198. def enable(self):
  199. self.nodePath.reparentTo(self._base.dataRoot)
  200. def disable(self):
  201. self.nodePath.reparentTo(self._base.dataUnused)
  202. def getName(self):
  203. return self.name
  204. def getNodePath(self):
  205. return self.nodePath
  206. def __repr__(self):
  207. string = self.name + ': '
  208. for i in range(self.getNumDials()):
  209. string = string + '%.3f' % self[i] + ' '
  210. return string
  211. class DirectTimecodeReader(AnalogNode, DirectObject):
  212. timecodeReaderCount = 0
  213. def __init__(self, vrpnClient, device):
  214. # Keep track of number of timecodeReader created
  215. DirectTimecodeReader.timecodeReaderCount += 1
  216. # Create a new dial node for the given device
  217. AnalogNode.__init__(self, vrpnClient, device)
  218. # Create a unique name for this dial object
  219. self.name = ('DirectTimecodeReader-' +
  220. repr(DirectTimecodeReader.timecodeReaderCount))
  221. # Initialize components of timecode
  222. self.frames = 0
  223. self.seconds = 0
  224. self.minutes = 0
  225. self.hours = 0
  226. # Attach node to data graph
  227. try:
  228. self._base = base
  229. except:
  230. self._base = simbase
  231. self.nodePath = self._base.dataRoot.attachNewNode(self)
  232. def enable(self):
  233. self.nodePath.reparentTo(self._base.dataRoot)
  234. def disable(self):
  235. self.nodePath.reparentTo(self._base.dataUnused)
  236. def getName(self):
  237. return self.name
  238. def getNodePath(self):
  239. return self.nodePath
  240. def getTime(self):
  241. # Assume only one card, use channel 0
  242. timeBits = int(self.getControlState(0))
  243. self.frames = ((timeBits & 0xF) +
  244. (((timeBits & 0xF0) >> 4) * 10))
  245. self.seconds = (((timeBits & 0x0F00) >> 8) +
  246. (((timeBits & 0xF000) >> 12) * 10))
  247. self.minutes = (((timeBits & 0x0F0000) >> 16) +
  248. (((timeBits & 0xF00000) >> 20) * 10))
  249. self.hours = (((timeBits & 0xF000000) >> 24) +
  250. (((timeBits & 0xF0000000) >> 28) * 10))
  251. self.totalSeconds = ((self.hours * 3600) +
  252. (self.minutes * 60) +
  253. self.seconds +
  254. (self.frames / 30.0))
  255. return (self.hours, self.minutes, self.seconds, self.frames,
  256. self.totalSeconds)
  257. def __repr__(self):
  258. string = ('%s: %d:%d:%d:%d' % ((self.name,) + self.getTime()[:-1]))
  259. return string