Package SpecClient :: Module SpecMotor
[hide private]
[frames] | no frames]

Source Code for Module SpecClient.SpecMotor

  1  #$Id: SpecMotor.py,v 1.6 2005/02/08 13:17:21 guijarro Exp $ 
  2  """SpecMotor module 
  3   
  4  This module defines the classes for motor objects 
  5   
  6  Classes: 
  7  SpecMotor -- class representing a motor in Spec 
  8  SpecMotorA -- class representing a motor in Spec, to be used with a GUI 
  9  """ 
 10   
 11  __author__ = 'Matias Guijarro' 
 12  __version__ = '1.0' 
 13   
 14  import SpecConnectionsManager 
 15  import SpecEventsDispatcher 
 16  import SpecWaitObject 
 17  import SpecCommand 
 18  import logging 
 19  import types 
 20  import math 
 21   
 22  (NOTINITIALIZED, UNUSABLE, READY, MOVESTARTED, MOVING, ONLIMIT) = (0,1,2,3,4,5) 
 23  (NOLIMIT, LOWLIMIT, HIGHLIMIT) = (0,2,4) 
 24   
25 -class SpecMotorA:
26 """SpecMotorA class"""
27 - def __init__(self, specName = None, specVersion = None, callbacks={}):
28 """Constructor 29 30 Keyword arguments: 31 specName -- name of the motor in Spec (defaults to None) 32 specVersion -- 'host:port' string representing a Spec server to connect to (defaults to None) 33 """ 34 self.motorState = NOTINITIALIZED 35 self.limit = NOLIMIT 36 self.limits = (None, None) 37 self.chanNamePrefix = '' 38 self.connection = None 39 self.__old_position = None 40 # the callbacks listed below can be set directly using the 'callbacks' keyword argument ; 41 # when the event occurs, the corresponding callback will be called automatically 42 self.__callbacks = { 43 'connected': None, 44 'disconnected': None, 45 'motorLimitsChanged': None, 46 'motorPositionChanged': None, 47 'motorStateChanged': None 48 } 49 for cb_name in self.__callbacks.iterkeys(): 50 if callable(callbacks.get(cb_name)): 51 self.__callbacks[cb_name] = SpecEventsDispatcher.callableObjectRef(callbacks[cb_name]) 52 53 if specName is not None and specVersion is not None: 54 self.connectToSpec(specName, specVersion) 55 else: 56 self.specName = None 57 self.specVersion = None
58 59
60 - def connectToSpec(self, specName, specVersion):
61 """Connect to a remote Spec 62 63 Connect to Spec and register channels of interest for the specified motor 64 65 Arguments: 66 specName -- name of the motor in Spec 67 specVersion -- 'host:port' string representing a Spec server to connect to 68 """ 69 self.specName = specName 70 self.specVersion = specVersion 71 self.chanNamePrefix = 'motor/%s/%%s' % specName 72 73 self.connection = SpecConnectionsManager.SpecConnectionsManager().getConnection(specVersion) 74 SpecEventsDispatcher.connect(self.connection, 'connected', self.__connected) 75 SpecEventsDispatcher.connect(self.connection, 'disconnected', self.__disconnected) 76 77 # 78 # register channels 79 # 80 self.connection.registerChannel(self.chanNamePrefix % 'low_limit', self._motorLimitsChanged) 81 self.connection.registerChannel(self.chanNamePrefix % 'high_limit', self._motorLimitsChanged) 82 self.connection.registerChannel(self.chanNamePrefix % 'position', self.__motorPositionChanged, dispatchMode=SpecEventsDispatcher.FIREEVENT) 83 self.connection.registerChannel(self.chanNamePrefix % 'move_done', self.motorMoveDone, dispatchMode = SpecEventsDispatcher.FIREEVENT) 84 self.connection.registerChannel(self.chanNamePrefix % 'high_lim_hit', self.__motorLimitHit) 85 self.connection.registerChannel(self.chanNamePrefix % 'low_lim_hit', self.__motorLimitHit) 86 self.connection.registerChannel(self.chanNamePrefix % 'sync_check', self.__syncQuestion) 87 self.connection.registerChannel(self.chanNamePrefix % 'unusable', self.__motorUnusable) 88 self.connection.registerChannel(self.chanNamePrefix % 'offset', self.motorOffsetChanged) 89 self.connection.registerChannel(self.chanNamePrefix % 'sign', self.signChanged) 90 #self.connection.registerChannel(self.chanNamePrefix % 'dial_position', self.dialPositionChanged) 91 92 if self.connection.isSpecConnected(): 93 self.__connected()
94 95
96 - def __connected(self):
97 """Private callback triggered by a 'connected' event from Spec.""" 98 self.connected() 99 if self.__callbacks.get("connected"): 100 cb = self.__callbacks["connected"]() 101 if cb is not None: 102 cb()
103 104
105 - def connected(self):
106 """Callback triggered by a 'connected' event from Spec 107 108 To be extended by derivated classes. 109 """ 110 pass
111 112
113 - def __disconnected(self):
114 """Private callback triggered by a 'disconnected' event from Spec 115 116 Put the motor in NOTINITIALIZED state. 117 """ 118 self.__changeMotorState(NOTINITIALIZED) 119 self.disconnected() 120 if self.__callbacks.get("disconnected"): 121 cb = self.__callbacks["disconnected"]() 122 if cb is not None: 123 cb()
124 125
126 - def disconnected(self):
127 """Callback triggered by a 'disconnected' event from Spec 128 129 To be extended by derivated classes. 130 """ 131 pass
132 133 134 #def dialPositionChanged(self, dial_position): 135 # pass 136 137
138 - def signChanged(self, sign):
140 141
142 - def motorOffsetChanged(self, offset):
144 145
146 - def _motorLimitsChanged(self):
147 self.motorLimitsChanged() 148 if self.__callbacks.get("motorLimitsChanged"): 149 cb = self.__callbacks["motorLimitsChanged"]() 150 if cb is not None: 151 cb()
152 153
154 - def motorLimitsChanged(self):
155 """Callback triggered by a 'low_limit' or a 'high_limit' channel update, 156 or when the sign or offset for motor changes 157 158 To be extended by derivated classes. 159 """ 160 pass
161 162
163 - def motorMoveDone(self, channelValue):
164 """Callback triggered when motor starts or stops moving 165 166 Change the motor state accordingly. 167 168 Arguments: 169 channelValue -- value of the channel 170 """ 171 if channelValue: 172 self.__changeMotorState(MOVING) 173 elif self.motorState == MOVING or self.motorState == MOVESTARTED or self.motorState == NOTINITIALIZED: 174 self.__changeMotorState(READY)
175 176
177 - def __motorLimitHit(self, channelValue, channelName):
178 """Private callback triggered by a 'low_lim_hit' or a 'high_lim_hit' channel update 179 180 Update the motor state accordingly. 181 182 Arguments: 183 channelValue -- value of the channel 184 channelName -- name of the channel (either 'low_lim_hit' or 'high_lim_hit') 185 """ 186 if channelValue: 187 if channelName.endswith('low_lim_hit'): 188 self.limit = self.limit | LOWLIMIT 189 self.__changeMotorState(ONLIMIT) 190 else: 191 self.limit = self.limit | HIGHLIMIT 192 self.__changeMotorState(ONLIMIT)
193 194
195 - def __motorPositionChanged(self, absolutePosition):
196 if self.__old_position is None: 197 self.__old_position = absolutePosition 198 else: 199 if math.fabs(absolutePosition - self.__old_position) > 1E-6: 200 self.__old_position = absolutePosition 201 else: 202 return 203 self.motorPositionChanged(absolutePosition) 204 if self.__callbacks.get("motorPositionChanged"): 205 cb = self.__callbacks["motorPositionChanged"]() 206 if cb is not None: 207 cb(absolutePosition)
208 209 210
211 - def motorPositionChanged(self, absolutePosition):
212 """Callback triggered by a position channel update 213 214 To be extended by derivated classes. 215 216 Arguments: 217 absolutePosition -- motor absolute position 218 """ 219 pass
220 221
222 - def setOffset(self, offset):
223 """Set the motor offset value""" 224 c = self.connection.getChannel(self.chanNamePrefix % 'offset') 225 226 c.write(offset)
227 228
229 - def getOffset(self):
230 c = self.connection.getChannel(self.chanNamePrefix % 'offset') 231 232 return c.read()
233 234
235 - def getSign(self):
236 c = self.connection.getChannel(self.chanNamePrefix % 'sign') 237 238 return c.read()
239 240
241 - def __syncQuestion(self, channelValue):
242 """Callback triggered by a 'sync_check' channel update 243 244 Call the self.syncQuestionAnswer method and reply to the sync. question. 245 246 Arguments: 247 channelValue -- value of the channel 248 """ 249 if type(channelValue) == type(''): 250 steps = channelValue.split() 251 specSteps = steps[0] 252 controllerSteps = steps[1] 253 254 a = self.syncQuestionAnswer(specSteps, controllerSteps) 255 256 if a is not None: 257 c = self.connection.getChannel(self.chanNamePrefix % 'sync_check') 258 c.write(a)
259 260
261 - def syncQuestionAnswer(self, specSteps, controllerSteps):
262 """Answer to the sync. question 263 264 Return either '1' (YES) or '0' (NO) 265 266 Arguments: 267 specSteps -- steps measured by Spec 268 controllerSteps -- steps indicated by the controller 269 """ 270 pass
271 272
273 - def __motorUnusable(self, unusable):
274 """Private callback triggered by a 'unusable' channel update 275 276 Update the motor state accordingly 277 278 Arguments: 279 unusable -- value of the channel 280 """ 281 if unusable: 282 self.__changeMotorState(UNUSABLE) 283 else: 284 self.__changeMotorState(READY)
285 286
287 - def __changeMotorState(self, state):
288 """Private method for changing the SpecMotor object's internal state 289 290 Arguments: 291 state -- the motor state 292 """ 293 self.motorState = state 294 self.motorStateChanged(state) 295 if self.__callbacks.get("motorStateChanged"): 296 cb = self.__callbacks["motorStateChanged"]() 297 if cb is not None: 298 cb(state)
299 300 301
302 - def motorStateChanged(self, state):
303 """Callback to take into account a motor state update 304 305 To be extended by derivated classes 306 307 Arguments: 308 state -- the motor state 309 """ 310 pass
311 312
313 - def move(self, absolutePosition):
314 """Move the motor to the required position 315 316 Arguments: 317 absolutePosition -- position to move to 318 """ 319 if type(absolutePosition) != types.FloatType and type(absolutePosition) != types.IntType: 320 logging.getLogger("SpecClient").error("Cannot move %s: position '%s' is not a number", self.specName, absolutePosition) 321 322 self.__changeMotorState(MOVESTARTED) 323 324 c = self.connection.getChannel(self.chanNamePrefix % 'start_one') 325 326 c.write(absolutePosition)
327 328
329 - def moveRelative(self, relativePosition):
330 self.move(self.getPosition() + relativePosition)
331 332
333 - def moveToLimit(self, limit):
334 cmdObject = SpecCommand.SpecCommandA("_mvc", self.connection) 335 336 if cmdObject.isSpecReady(): 337 if limit: 338 cmdObject(1) 339 else: 340 cmdObject(-1)
341 342
343 - def stop(self):
344 """Stop the current motor 345 346 Send an 'abort' message to the remote Spec 347 """ 348 self.connection.abort()
349 350
351 - def stopMoveToLimit(self):
352 c = self.connection.getChannel("var/_MVC_CONTINUE_MOVING") 353 c.write(0)
354 355
356 - def getParameter(self, param):
357 c = self.connection.getChannel(self.chanNamePrefix % param) 358 return c.read()
359 360
361 - def setParameter(self, param, value):
362 c = self.connection.getChannel(self.chanNamePrefix % param) 363 c.write(value)
364 365
366 - def getPosition(self):
367 """Return the current position of the motor.""" 368 c = self.connection.getChannel(self.chanNamePrefix % 'position') 369 370 return c.read()
371 372
373 - def getState(self):
374 """Return the current motor state.""" 375 return self.motorState
376 377
378 - def getLimits(self):
379 """Return a (low limit, high limit) tuple in user units.""" 380 lims = [x * self.getSign() + self.getOffset() for x in (self.connection.getChannel(self.chanNamePrefix % 'low_limit').read(), \ 381 self.connection.getChannel(self.chanNamePrefix % 'high_limit').read())] 382 return (min(lims), max(lims))
383 384
385 - def getDialPosition(self):
386 """Return the motor dial position.""" 387 c = self.connection.getChannel(self.chanNamePrefix % 'dial_position') 388 389 return c.read()
390 391
392 -class SpecMotor:
393 """Spec Motor"""
394 - def __init__(self, specName = None, specVersion = None, timeout = None):
395 """Constructor 396 397 Keyword arguments: 398 specName -- name of the motor in Spec (defaults to None) 399 specVersion -- 'host:port' string representing a Spec server to connect to (defaults to None) 400 timeout -- optional timeout for the connection (defaults to None) 401 """ 402 self.chanNamePrefix = '' 403 self.connection = None 404 405 if specName is not None and specVersion is not None: 406 self.connectToSpec(specName, specVersion, timeout) 407 else: 408 self.specName = None 409 self.specVersion = None
410 411
412 - def connectToSpec(self, specName, specVersion, timeout = None):
413 """Connect to a remote Spec 414 415 Block until Spec is connected or timeout occurs 416 417 Arguments: 418 specName -- name of the motor in Spec 419 specVersion -- 'host:port' string representing a Spec server to connect to 420 timeout -- optional timeout for the connection (defaults to None) 421 """ 422 self.specName = specName 423 self.specVersion = specVersion 424 self.chanNamePrefix = 'motor/%s/%%s' % specName 425 426 self.connection = SpecConnectionsManager.SpecConnectionsManager().getConnection(specVersion) 427 428 w = SpecWaitObject.SpecWaitObject(self.connection) 429 w.waitConnection(timeout)
430 431
432 - def unusable(self):
433 """Return whether the motor is unusable or not.""" 434 if self.connection is not None: 435 c = self.connection.getChannel(self.chanNamePrefix % 'unusable') 436 437 return c.read()
438 439
440 - def lowLimitHit(self):
441 """Return if low limit has been hit.""" 442 if self.connection is not None: 443 c = self.connection.getChannel(self.chanNamePrefix % 'low_lim_hit') 444 445 return c.read()
446 447
448 - def highLimitHit(self):
449 """Return if high limit has been hit.""" 450 if self.connection is not None: 451 c = self.connection.getChannel(self.chanNamePrefix % 'high_lim_hit') 452 453 return c.read()
454 455
456 - def move(self, absolutePosition):
457 """Move the motor 458 459 Block until the move is finished 460 461 Arguments: 462 absolutePosition -- position where to move the motor to 463 """ 464 if self.connection is not None: 465 c = self.connection.getChannel(self.chanNamePrefix % 'start_one') 466 467 c.write(absolutePosition) 468 469 w = SpecWaitObject.SpecWaitObject(self.connection) 470 w.waitChannelUpdate(self.chanNamePrefix % 'move_done', waitValue = 0) #move_done is set to 0 when move has finished
471 472
473 - def moveRelative(self, relativePosition):
474 self.move(self.getPosition() + relativePosition)
475 476
477 - def moveToLimit(self, limit):
478 if self.connection is not None: 479 cmdObject = SpecCommand.SpecCommandA("_mvc", self.connection) 480 481 if cmdObject.isSpecReady(): 482 if limit: 483 cmdObject(self.specName, 1) 484 else: 485 cmdObject(self.specName, -1)
486 487
488 - def stop(self):
489 """Stop the current motor 490 491 Send an 'abort' message to the remote Spec 492 """ 493 self.connection.abort()
494 495
496 - def stopMoveToLimit(self):
497 if self.connection is not None: 498 c = self.connection.getChannel("var/_MVC_CONTINUE_MOVING") 499 c.write(0)
500 501
502 - def getPosition(self):
503 """Return the current absolute position for the motor.""" 504 if self.connection is not None: 505 c = self.connection.getChannel(self.chanNamePrefix % 'position') 506 507 return c.read()
508 509
510 - def setOffset(self, offset):
511 """Set the motor offset value""" 512 if self.connection is not None: 513 c = self.connection.getChannel(self.chanNamePrefix % 'offset') 514 515 c.write(offset)
516 517
518 - def getOffset(self):
519 if self.connection is not None: 520 c = self.connection.getChannel(self.chanNamePrefix % 'offset') 521 522 return c.read()
523 524
525 - def getSign(self):
526 if self.connection is not None: 527 c = self.connection.getChannel(self.chanNamePrefix % 'sign') 528 529 return c.read()
530 531
532 - def getDialPosition(self):
533 if self.connection is not None: 534 c = self.connection.getChannel(self.chanNamePrefix % 'dial_position') 535 536 return c.read()
537 538
539 - def getLimits(self):
540 if self.connection is not None: 541 lims = [x * self.getSign() + self.getOffset() for x in (self.connection.getChannel(self.chanNamePrefix % 'low_limit').read(), \ 542 self.connection.getChannel(self.chanNamePrefix % 'high_limit').read())] 543 return (min(lims), max(lims))
544