1 from __future__
import division, absolute_import
5 from twistedActor
import DeviceSet, log, LinkCommands, UserCmd, expandUserCmd
9 from tcc.msg import formatAxisCmdStateAndErr
10 from .axisDevice
import AxisStatus, AxisDevice
12 __all__ = [
"AxisDeviceSet"]
19 DefaultTimeLim = AxisDevice.DefaultTimeLim
20 DefaultInitTimeLim = AxisDevice.DefaultInitTimeLim
21 SlotList = (
"az",
"alt",
"rot")
23 """!Construct an AxisDeviceSet
25 @param[in] actor actor (instance of twistedActor.BaseActor);
26 items used include writeToUsers, queueStatus, obj, inst and axeLim
27 @param[in] devList az, alt and rot device; any can be None if it does not exist
29 @throw RuntimeError if len(devList) != 3
31 DeviceSet.__init__(self,
35 connStateKeyword =
"axisConnState",
40 def connect(self, slotList=None, userCmd=None, timeLim=DefaultInitTimeLim):
41 """Connect the specified axis controllers
43 Overridden because connecting can be slow, presumably due to the multiplexor
45 return DeviceSet.connect(self, slotList=slotList, userCmd=userCmd, timeLim=timeLim)
47 def connectOrInit(self, slotList=None, userCmd=None, timeLim=DefaultInitTimeLim):
48 """!Connect and initialize axis controllers
50 Specified axis controllers that are already connected are simply initialized.
52 @param[in] slotList collection of slot names, or None for all filled slots
53 @param[in] userCmd user command whose set state is set to Done or Failed when all device commands are done;
54 if None a new UserCmd is created and returned
55 @param[in] timeLim time limit for init and connect command (sec)
56 uses a longer than default time limit because connecting can be slow due to the multiplexor
57 @return userCmd: the supplied userCmd or a newly created UserCmd
60 expSlotList = self.expandSlotList(slotList)
61 connSlotList = [slot
for slot
in expSlotList
if self.get(slot).isConnected]
62 notConnSlotList = list(set(expSlotList) - set(connSlotList))
63 initUserCmd = self.
init(connSlotList, timeLim=timeLim)
64 connUserCmd = self.
connect(notConnSlotList, timeLim=timeLim)
67 LinkCommands(userCmd, (initUserCmd, connUserCmd))
70 def disconnect(self, slotList=None, userCmd=None, timeLim=DefaultInitTimeLim):
71 """Disconnect the specified axis controllers
73 Overridden because disconnecting can be slow
75 return DeviceSet.disconnect(self, slotList=slotList, userCmd=userCmd, timeLim=timeLim)
77 def drift(self, slotList=None, userCmd=None, timeLim=DefaultTimeLim):
78 """!Execute a DRIFT command and update self.actor.obj
80 If drift fails for any axes then initialize all axes and update self.actor.obj
82 @param[in] slotList collection of slot names, or None for all filled slots
83 @param[in] userCmd user command whose set state is set to Done or Failed when all device commands are done;
84 if None a new UserCmd is created and returned
85 @param[in] timeLim time limit for command (sec)
86 @return userCmd: the supplied userCmd or a newly created UserCmd
88 @throw RuntimeError if slotList has empty or non-existent slots
90 return self.startCmd(cmdStrOrList=
"DRIFT", slotList=slotList, userCmd=userCmd, callFunc=self.
_initAllOnFailure, timeLim=timeLim)
92 def init(self, slotList=None, userCmd=None, timeLim=DefaultInitTimeLim):
93 """!Initialize the axes controllers
95 @param[in] slotList collection of slot names, or None for all filled slots
96 @param[in] userCmd user command whose set state is set to Done or Failed when all device commands are done;
97 if None a new UserCmd is created and returned
98 @param[in] timeLim time limit for init and connect command (sec)
99 @return userCmd: the supplied userCmd or a newly created UserCmd
102 slotList = self.expandSlotList(slotList)
103 userCmd = expandUserCmd(userCmd)
107 for slot
in slotList:
109 subCmdList.append(dev.init(timeLim=timeLim))
110 LinkCommands(userCmd, subCmdList)
113 def movePVT(self, pvtList, userCmd=None, timeLim=DefaultTimeLim):
114 """!Execute a MOVE Pos Vel Time or MOVE command for each axis
116 If any axis fails, INIT that axis and update self.actor.obj
118 As soon as the MOVE Pos Vel Time command is sent, schedule a status command.
120 @param[in] pvtList list of PVTs or PVAJTs, one per axis; isfinite() must be false for nonexistent axes
121 and any axis you don't want moved
122 @param[in] userCmd user command whose set state is set to Done or Failed when all device commands are done;
123 if None a new UserCmd is created and returned
124 @param[in] timeLim time limit for command (sec)
125 @return userCmd: the supplied userCmd or a newly created UserCmd
127 if len(pvtList) != len(self):
128 raise RuntimeError(
"pvtList must have %d items; pvtList=%s" % (len(self), pvtList))
131 for pvt, (slot, dev)
in itertools.izip(pvtList, self._slotDevDict.iteritems()):
134 cmdDict[slot] =
"MOVE %0.7f %0.7f %0.5f" % (pvt.pos, pvt.vel,
secInDayFromDate(pvt.t))
136 cmdDict[slot] =
"MOVE"
137 self.actor.queueStatus(StatusDelay)
138 return self.startCmdDict(cmdDict=cmdDict, userCmd=userCmd, timeLim=timeLim)
140 def plusMovePV(self, pvtList, userCmd=None, timeLim=DefaultTimeLim):
141 """!Execute a +MOVE Pos Vel or MOVE command for each axis
143 If any axis fails, INIT that axis and update self.actor.obj
145 As soon as the +MOVE Pos Vel command is sent, schedule a status command.
147 @param[in] pvtList list of PVTs or PVAJTs, one per axis; isfinite() must be false for nonexistent axes
148 and any axis you don't want moved
149 @param[in] userCmd user command whose set state is set to Done or Failed when all device commands are done;
150 if None a new UserCmd is created and returned
151 @param[in] timeLim time limit for command (sec)
152 @return userCmd: the supplied userCmd or a newly created UserCmd
154 if len(pvtList) != len(self):
155 raise RuntimeError(
"pvtList must have %d items; pvtList=%s" % (len(self), pvtList))
158 for pvt, (slot, dev)
in itertools.izip(pvtList, self._slotDevDict.iteritems()):
161 cmdDict[slot] =
"+MOVE %0.7f %0.7f" % (pvt.pos, pvt.vel)
163 cmdDict[slot] =
"MOVE"
164 self.actor.queueStatus(StatusDelay)
165 return self.startCmdDict(cmdDict=cmdDict, userCmd=userCmd, timeLim=timeLim)
167 def startSlew(self, pathList, doAbsRefCorr, userCmd=None, timeLim=DefaultTimeLim):
168 """!Execute a set if MOVE Pos Vel Time commands or one MOVE command for each axis
170 If any axis fails, INIT that axis and update self.actor.obj
172 As soon as the commands are sent, schedule a status command.
174 @param[in] pathList list of pvtLists, one per slot;
175 if pathList[slot] is an empty list, then that device is halted immediately (if it exists);
176 pathList[slot] must be an empty list if that slot has no device
177 @param[in] doAbsRefCorr apply absolute fiducial corrections during this slew?
178 @param[in] userCmd user command whose set state is set to Done or Failed when all device commands are done;
179 if None a new UserCmd is created and returned
180 @param[in] timeLim time limit for command (sec)
181 @return userCmd: the supplied userCmd or a newly created UserCmd
183 if len(pathList) != len(self):
184 raise RuntimeError(
"pathList must have %d items; pathList=%s" % (len(self), pathList))
187 for pvtList, (slot, dev)
in itertools.izip(pathList, self._slotDevDict.iteritems()):
190 cmdDict[slot] = [
"MOVE %0.7f %0.7f %0.5f" % (pvt.pos, pvt.vel,
secInDayFromDate(pvt.t))
for pvt
in pvtList]
192 cmdDict[slot] += [
"MS.ON",
"MS.OFF %0.5f" % (
secInDayFromDate(pvtList[-1].t),)]
194 cmdDict[slot] = [
"MOVE"]
195 self.actor.queueStatus(StatusDelay)
196 return self.startCmdDict(cmdDict=cmdDict, userCmd=userCmd, timeLim=timeLim)
198 def status(self, slotList=None, userCmd=None, timeLim=DefaultTimeLim):
199 """!Execute a STATUS command and update self.actor.obj
201 @param[in] slotList collection of slot names, or None for all filled and connected slots
202 @param[in] userCmd user command whose set state is set to Done or Failed when all device commands are done;
203 if None a new UserCmd is created and returned
204 @param[in] timeLim time limit for command (sec)
205 @return userCmd: the supplied userCmd or a newly created UserCmd
207 @throw RuntimeError if slotList has empty or non-existent slots
209 self.showConnState(userCmd=userCmd)
210 slotList = self.expandSlotList(slotList, connOnly=
True)
211 self.actor.queueStatus()
213 return self.startCmd(cmdStrOrList=
"STATUS", slotList=slotList, userCmd=userCmd, timeLim=timeLim)
215 def stop(self, slotList=None, userCmd=None, timeLim=DefaultTimeLim):
216 """!Halt one or more axes and udpate self.actor.obj
218 If any axis fails, INIT that axis and update self.actor.obj
220 @param[in] slotList collection of slot names, or None for all filled slots
221 @param[in] userCmd user command whose set state is set to Done or Failed when all device commands are done;
222 if None a new UserCmd is created and returned
223 @param[in] timeLim time limit for command (sec)
224 @return userCmd: the supplied userCmd or a newly created UserCmd
226 @throw RuntimeError if slotList has empty or non-existent slots
230 self.
_setObjForInit(slotList=self.expandSlotList(slotList), halted=
False)
231 return self.startCmd(cmdStrOrList=
"MOVE", slotList=slotList, userCmd=userCmd, timeLim=timeLim)
233 def _addDevCallbacks(self, dev, slot):
234 """!Called when adding a device
236 Called after device is registered in slot dictionary, but possibly before it is connected.
237 Use to add callbacks to the device.
239 @param[in] dev device that has been removed
240 @param[in] slot slot the device occupied
242 DeviceSet._addDevCallbacks(self, dev, slot)
247 def _removeDevCallbacks(self, dev, slot):
248 """!Called when removing a device
250 Called after the device is deregistered from the slot dictionarhy, but before it is disconnected.
251 Use this to remove all callbacks from the device and clear information about it.
253 @param[in] dev device that has been removed
254 @param[in] slot slot the device occupied
256 DeviceSet._removeDevCallbacks(self, dev, slot)
257 dev.driftCallFunc =
None
258 dev.initCallFunc =
None
259 dev.statusCallFunc =
None
261 ind = self.getIndex(slot)
262 currTAI = tcc.base.tai()
263 self.actor.obj.actMount[ind].invalidate(currTAI)
264 self.actor.obj.axisStatusWord[ind] = 0
265 self.actor.obj.axisStatusTime[ind] = currTAI
267 def _initAllOnFailure(self, cmdInfo):
268 """!Callback for a command that, if it fails, all axes should be initialized
270 This will work with any command, though I suspect only the DRIFT command is sufficiently dangerous
271 to justify initializing all axes if DRIFT fails for one axis. One might argument that MOVE P V T
272 also qualifies, but at least the axis controller will halt an axis once time runs outo n a MOVE P V T,
273 wheras it won't halt for DRIFT until the axis reaches a limit.
275 @param[in] cmdInfo info for device command, an instance of twistedActor.DevCmdInfo
277 if cmdInfo.devCmd.didFail:
278 if not cmdInfo.userCmd.isDone:
279 cmdInfo.userCmd.setState(
280 cmdInfo.userCmd.Failed,
281 textMsg =
"%s %s failed, initializing ALL axes: %s" % \
282 (cmdInfo.devCmd.dev.name, cmdInfo.devCmd.cmdStr, cmdInfo.devCmd.getMsg()),
284 log.error(
"%s %s failed; initializing ALL axes" % (self, cmdInfo.devCmd))
287 def _driftCallback(self, dev):
288 """!Callback when device parses reply from DRIFT
290 This callback does nothing unless this device is currently in use;
291 that is why this function is in AxisDeviceSet instead of AxisDevice.
294 slot = self.slotFromDevName(dev.name)
298 ind = self.getIndex(slot)
299 self.actor.obj.actMount[ind] = dev.status.pvt
300 log.info(
"%s._driftCallback set self.actor.obj.actMount[%s] = %s" % (self, ind, self.actor.obj.actMount[ind]))
302 def _setObjForInit(self, slotList, halted=True):
303 """!Set command state, error code, and target mount for each axis
306 @param[in] slotList: list of device slots for which to update state
307 @param[in] halted: bool, if True set axes to halted, else set to Halting
310 axisState = tcc.base.AxisState_Halted
if halted
else tcc.base.AxisState_Halting
311 for slot
in slotList:
312 ind = self.getIndex(slot)
315 if self.actor.obj.axisCmdState[ind] != tcc.base.AxisState_Halted:
317 self.actor.obj.axisCmdState[ind] = axisState
318 self.actor.obj.axisErrCode[ind] = tcc.base.AxisErr_HaltRequested
319 self.actor.obj.targetMount[ind].invalidate(tcc.base.tai())
322 self.actor.writeToUsers(msgCode=msgCode, msgStr=msgStr)
324 def _initCallback(self, dev):
325 """!Callback when a device sends INIT or MOVE
327 This callback does nothing unless this device is currently in use;
328 that is why this function is in AxisDeviceSet instead of AxisDevice.
331 slot = self.slotFromDevName(dev.name)
335 self.actor.queueStatus(1)
337 def _statusCallback(self, dev):
338 """!Callback when device parses new status
340 This callback does nothing unless this device is currently in use;
341 that is why this function is in AxisDeviceSet instead of AxisDevice.
344 slot = self.slotFromDevName(dev.name)
348 ind = self.getIndex(slot)
349 self.actor.obj.actMount[ind] = dev.status.pvt
350 self.actor.obj.axisStatusWord[ind] = dev.status.statusWord
351 self.actor.obj.axisStatusTime[ind] = tcc.base.tai()
358 if userCmd
or prevRepStatus.wordOrDTimeChanged(dev.status):
359 msgCode, msgStr = dev.status.formatStatus(name=slot, axeLim=self.actor.axeLim)
361 self.actor.writeToUsers(msgCode, msgStr, cmd=userCmd)
def connectOrInit
Connect and initialize axis controllers.
def startSlew
Execute a set if MOVE Pos Vel Time commands or one MOVE command for each axis.
def _setObjForInit
Set command state, error code, and target mount for each axis in devList.
def status
Execute a STATUS command and update self.actor.obj.
def stop
Halt one or more axes and udpate self.actor.obj.
def init
Initialize the axes controllers.
def _initAllOnFailure
Callback for a command that, if it fails, all axes should be initialized.
def _statusCallback
Callback when device parses new status.
def _initCallback
Callback when a device sends INIT or MOVE.
def __init__
Construct an AxisDeviceSet.
def _driftCallback
Callback when device parses reply from DRIFT.
def drift
Execute a DRIFT command and update self.actor.obj.
def movePVT
Execute a MOVE Pos Vel Time or MOVE command for each axis.
def secInDayFromDate
Convert a date to seconds in the day (losing day information)
def plusMovePV
Execute a +MOVE Pos Vel or MOVE command for each axis.