def startCmd(self, cmdStr, userCmd=None, callFunc=None, timeLim=0.): log.info("%s.startCmd(cmdStr=%s, userCmd=%s, callFunc=%s)"%(self, cmdStr, str(userCmd), str(callFunc))) # print("%s.startCmd(cmdStr=%s, userCmd=%s, callFunc=%s)"%(self, cmdStr, str(userCmd), str(callFunc))) # if not self.isConnected: # userCmd.setState(userCmd.Failed, "filter wheel device not connected") # else: return ActorDevice.startCmd(self, cmdStr=cmdStr, userCmd=userCmd, callFunc=callFunc, timeLim=timeLim)
def handleReply(self, replyStr): """Handle a line of output from the device. @param[in] replyStr the reply, minus any terminating \n """ log.info("%s.handleReply(replyStr=%s)" % (self, replyStr)) replyStr = replyStr.strip() # print(replyStr, self.currExeDevCmd.cmdStr) if not replyStr: return if self.currExeDevCmd.isDone: # ignore unsolicited output? log.info("%s usolicited reply: %s for done command %s" % (self, replyStr, str(self.currExeDevCmd))) self.writeToUsers("i", "%s usolicited reply: %s for done command %s" % (self, replyStr, str(self.currExeDevCmd))) return if "error 15" in replyStr.lower(): self.writeToUsers("w", "Mitutoyo Error 15, not in counting state (was it power cycled?). Homing necessary.") self.encPos = [None]*6 elif "error" in replyStr.lower(): # some other error? self.writeToUsers("w", "Mitutoyo EV counter Error output: " + replyStr) if self.currExeDevCmd.cmdStr == READ_ENC: # all encoders values have been read # set command done self.setEncValue(replyStr) # was this the 6th value read? if so we are done if replyStr.startswith(ENCVAL_PREFIX+"%i"%6): self.currExeDevCmd.setState(self.currExeDevCmd.Done) if self.currExeDevCmd.cmdStr in [COUNTING_STATE, ZERO_SET, DISPLAY_CURR]: if replyStr == SUCESS: # successful set into counting state self.currExeDevCmd.setState(self.currExeDevCmd.Done)
def target(self, ra, dec, doHA, userCmd=None): """Set coordinates for a slew. @param[in] ra: right ascension decimal degrees @param[in] dec: declination decimal degrees @param[in] doHA: if True, use degrees in hour angle rather than ra. @param[in] userCmd: a twistedActor BaseCommand. """ log.info("%s.slew(userCmd=%s, ra=%.2f, dec=%.2f)" % (self, userCmd, ra, dec)) userCmd = expandUserCmd(userCmd) if not self.conn.isConnected: userCmd.setState(userCmd.Failed, "Not Connected to TCS") return userCmd if doHA: enterRa = "HAD %.8f"%ra else: enterRa = "RAD %.8f"%ra enterDec = "DECD %.8f"%dec enterEpoch = "MP %.2f"%2000 # LCO: HACK should coords always be 2000? devCmdList = [DevCmd(cmdStr=cmdStr) for cmdStr in [enterRa, enterDec, enterEpoch]]#, cmdSlew]] # set userCmd done only when each device command finishes # AND the pending slew is also done. # when the last dev cmd is done (the slew), set axis cmd statue to slewing LinkCommands(userCmd, devCmdList) #LCO: HACK don't wait for a slew to finish + [self.waitSlewCmd]) for devCmd in devCmdList: self.queueDevCmd(devCmd) statusStr = self.status.getStatusStr() if statusStr: self.writeToUsers("i", statusStr, userCmd) return userCmd
def handleReply(self, replyStr): """Handle a line of output from the device. Called whenever the device outputs a new line of data. @param[in] replyStr the reply, minus any terminating \n Tasks include: - Parse the reply - Manage the pending commands - Parse status to update the model parameters """ log.info("%s read %r, currCmdStr: %s" % (self, replyStr, self.currDevCmdStr)) # print("%s read %r, currCmdStr: %s" % (self, replyStr, self.currDevCmdStr)) replyStr = replyStr.strip() if not replyStr: return if self.currExeDevCmd.isDone: # ignore unsolicited ouput return if "error" in replyStr.lower(): # error self.writeToUsers("w", "Error in M2 reply: %s, current cmd: %s"%(replyStr, self.currExeDevCmd.cmdStr)) # if this was a speed command, set it if self.currDevCmdStr.lower() == "speed": self.status.speed = float(replyStr) elif self.currDevCmdStr.lower() == "status": self.processStatus(replyStr) # only one line is ever returned after a request # so if we got one, then the request is done self.currExeDevCmd.setState(self.currExeDevCmd.Done)
def handleReply(self, replyStr): """Handle a line of output from the device. Called whenever the device outputs a new line of data. @param[in] replyStr the reply, minus any terminating \n Tasks include: - Parse the reply - Manage the pending commands - Output data to users - Parse status to update the model parameters - If a command has finished, call the appropriate command callback """ log.info("%s read %r, currCmdStr: %s" % (self, replyStr, self.currDevCmdStr)) replyStr = replyStr.strip() if replyStr == "-1": # error errorStr = "handleReply failed for %s with -1"%self.currDevCmdStr if self.waitOffsetCmd.isActive: self.waitOffsetCmd.setState(self.waitOffsetCmd.Failed, errorStr) if self.waitRotCmd.isActive: # note the clamp should still execute!!!! self.waitRotCmd.setState(self.waitRotCmd.Failed, errorStr) self.currExeDevCmd.setState(self.currExeDevCmd.Failed, errorStr) return statusField = self.status.statusFieldDict.get(self.currDevCmdStr, None) if statusField: # a piece of status was requested, parse it # how to check for error condition? parsing -1 as a float will still work. statusField.setValue(replyStr) self.currExeDevCmd.setState(self.currExeDevCmd.Done) elif replyStr == "0": # this was a command, a "0" is expected self.currExeDevCmd.setState(self.currExeDevCmd.Done) else: log.info("unexpected reply" % replyStr)
def focus(self, focusValue, offset=False, userCmd=None): """Command an offset or absolute focus move @param[in] focusValue: float, focus value in microns @param[in] offset, if true this is offset, else absolute @param[in] userCmd: a twistedActor BaseCommand WARNING!!! At APO increasing focus corresponds to decreasing M1-M2 dist. The mirror controller at LCO convention is the opposite. """ log.info("%s.focus(userCmd=%s, focusValue=%.2f, offset=%s)" % (self, userCmd, focusValue, str(bool(offset)))) # if this focus value is < 50 microns userCmd = expandUserCmd(userCmd) if offset: deltaFocus = focusValue else: deltaFocus = self.status.secFocus - focusValue if abs(deltaFocus) < MIN_FOCUS_MOVE: # should focus be cancelled or just set to done? self.writeToUsers("w", "Focus offset below threshold of < %.2f, moving anyways."%MIN_FOCUS_MOVE, userCmd) # userCmd.setState(userCmd.Done) # return userCmd # focusDir = 1 # use M2's natural coordinates # focusDir = -1 # use convention at APO return self.move(valueList=[focusValue], offset=offset, userCmd=userCmd)
def queueDevCmd(self, devCmdStr, userCmd): def queueFunc(userCmd): self.startDevCmd(devCmdStr) self.cmdQueue.addCmd(userCmd, queueFunc) log.info("%s.runCommand(userCmd=%r, devCmdStr=%r, cmdQueue: %r" % (self, userCmd, devCmdStr, self.cmdQueue))
def move(self, position, userCmd=None): """!Move to a position @param[in] postion: a float, position to move to (mm) @param[in] userCmd: a twistedActor BaseCommand """ log.info("%s.move(postion=%.6f, userCmd=%s)" % (self, position, userCmd)) userCmd=expandUserCmd(userCmd) if self.isMoving: userCmd.setState(userCmd.Failed, "Cannot move, device is busy moving") return userCmd # verify position is in range minPos, maxPos = self.status.moveRange if not minPos<=position<=maxPos: userCmd.setState(userCmd.Failed, "Move %.6f not in range [%.4f, %.4f]"%(position, minPos, maxPos)) return userCmd self.iter = 1 self.targetPos = position self.moveUserCmd = userCmd print("moving threadring to: ", self.targetPos) if not self.moveUserCmd.isActive: self.moveUserCmd.setState(self.moveUserCmd.Running) self.writeToUsers("i", "DesThreadRingPos=%.4f"%self.targetPos) moveDevCmd = self.queueDevCmd(self.getMoveCmdStr(), self.moveUserCmd) moveDevCmd.addCallback(self._moveCallback) return userCmd
def cmd_move(self, userCmd): desPos = int(userCmd.parsedCommand.parsedPositionalArgs[0]) log.info("%s.cmd_move(userCmd=%s) desPos: %i" % (self, userCmd, desPos)) # print("%s.cmd_move(userCmd=%s) desPos: %i"%(self, userCmd, desPos)) if desPos not in self.MoveRange: # raise ParseError("desPos must be one of %s for move command"%(str(self.MoveRange),)) userCmd.setState( userCmd.Failed, "desPos must be one of %s for move command" % (str(self.MoveRange), )) elif not self.status.isHomed: userCmd.setState(userCmd.Failed, "cannot command move, home filter wheel first.") elif not self.fwMoveCmd.isDone: userCmd.setState(userCmd.Failed, "filter wheel is moving") elif desPos == self.status.currentPos: userCmd.setState(userCmd.Done, "Filter currently in position") else: # get a fresh status self.getStatus() self.fwMoveCmd = userCmd if not self.fwMoveCmd.isActive: self.fwMoveCmd.setState(self.fwMoveCmd.Running) moveToFilter(desPos) self.getStatus() self.writeToUsers("i", self.status.moveStr, cmd=userCmd) return True
def queueDevCmd(self, devCmdStr, userCmd): """Add a device command to the device command queue @param[in] devCmdStr: a command string to send to the device. @param[in] userCmd: a UserCmd associated with this device (probably but not necessarily linked. Used here for writeToUsers reference. """ log.info("%s.queueDevCmd(devCmdStr=%r, cmdQueue: %r"%(self, devCmdStr, self.devCmdQueue)) # append a cmdVerb for the command queue (otherwise all get the same cmdVerb and cancel eachother) # could change the default behavior in CommandQueue? cmdVerb = devCmdStr.split()[0] assert cmdVerb in self.validCmdVerbs devCmd = DevCmd(cmdStr=devCmdStr) devCmd.cmdVerb = cmdVerb devCmd.userCmd = userCmd def queueFunc(devCmd): # when the command is ready run this # everything besides a move should return quickly devCmd.setTimeLimit(SEC_TIMEOUT) devCmd.setState(devCmd.Running) if cmdVerb == "status": # wipe status, to ensure we've # gotten a full status when done. self.status.flushStatus() self.startDevCmd(devCmd.cmdStr) self.devCmdQueue.addCmd(devCmd, queueFunc) return devCmd
def cmd_ping(self, userCmd): """! Implement the ping command @param[in] userCmd a twistedActor command with a parsedCommand attribute """ log.info("%s.cmd_ping(userCmd=%s)" % (self, str(userCmd))) # print("%s.cmd_ping(userCmd=%s)"%(self, str(userCmd))) userCmd.setState(userCmd.Done, textMsg="alive") return True
def cmd_init(self, userCmd): """! Implement the init command @param[in] userCmd a twistedActor command with a parsedCommand attribute """ log.info("%s.cmd_init(userCmd=%s)" % (self, str(userCmd))) # print("%s.cmd_init(userCmd=%s)"%(self, str(userCmd))) self.init(userCmd, getStatus=True) # userCmd.setState(userCmd.Done) return True
def init(self, userCmd=None, timeLim=3, getStatus=True): """Called automatically on startup after the connection is established. Only thing to do is query for status or connect if not connected getStatus ignored? """ log.info("%s.init(userCmd=%s, timeLim=%s, getStatus=%s)" % (self, userCmd, timeLim, getStatus)) userCmd = expandUserCmd(userCmd) self.getStatus(userCmd) # status links the userCmd return userCmd
def cmd_stopWheel(self, userCmd): log.info("%s.cmd_stop(userCmd=%s)" % (self, str(userCmd))) # print("%s.cmd_stop(userCmd=%s)"%(self, str(userCmd))) if not self.fwMoveCmd.isDone: self.fwMoveCmd.setState(self.fwMoveCmd.Failed, "stop commanded") if not self.fwHomeCmd.isDone: self.fwHomeCmd.setState(self.fwHomeCmd.Failed, "stop commanded") stop() userCmd.setState(userCmd.Done) return True
def cmd_status(self, userCmd=None, setDone=True): """! Implement the status command @param[in] userCmd a twistedActor command with a parsedCommand attribute """ log.info("%s.cmd_status(userCmd=%s)" % (self, str(userCmd))) userCmd = expandUserCmd(userCmd) self.getStatus() self.writeToUsers("i", self.status.statusStr, cmd=userCmd) if setDone: userCmd.setState(userCmd.Done) return True
def cmd_home(self, userCmd): log.info("%s.cmd_home(userCmd=%s)" % (self, str(userCmd))) # print("%s.cmd_home(userCmd=%s)"%(self, str(userCmd))) if False in [self.fwMoveCmd.isDone, self.fwHomeCmd]: userCmd.setState(userCmd.Failed, "filter wheel is moving") else: self.fwHomeCmd = userCmd if not self.fwHomeCmd.isActive: self.fwHomeCmd.setState(self.fwHomeCmd.Running) home() self.cmd_status(userCmd, setDone=False) return True
def cmd_diffuIn(self, userCmd): """Move the diffuser into the beam """ log.info("%s.cmd_diffuIn(userCmd=%s)" % (self, userCmd)) if self.status.diffuCover == 0: setDiffuserIn() time.sleep(3) self.getStatus() self.writeToUsers("i", self.status.statusStr, cmd=userCmd) userCmd.setState(userCmd.Done) return True
def startCmd(self, cmdStr, userCmd=None, callFunc=None, timeLim=0.): log.info("%s.startCmd(cmdStr=%s, userCmd=%s, callFunc=%s)" % (self, cmdStr, str(userCmd), str(callFunc))) # print("%s.startCmd(cmdStr=%s, userCmd=%s, callFunc=%s)"%(self, cmdStr, str(userCmd), str(callFunc))) # if not self.isConnected: # userCmd.setState(userCmd.Failed, "filter wheel device not connected") # else: return ActorDevice.startCmd(self, cmdStr=cmdStr, userCmd=userCmd, callFunc=callFunc, timeLim=timeLim)
def init(self, userCmd=None, timeLim=None, getStatus=False): """Called automatically on startup after the connection is established. Only thing to do is query for status or connect if not connected """ log.info("%s.init(userCmd=%s, timeLim=%s, getStatus=%s)" % (self, userCmd, timeLim, getStatus)) userCmd = expandUserCmd(userCmd) # stop, set speed, then status? stopCmd = self.queueDevCmd("stop", userCmd) speedCmd = self.queueDevCmd("speed %.4f"%self.nomSpeed, userCmd) statusCmd = self.queueDevCmd("status", userCmd) LinkCommands(userCmd, [stopCmd, speedCmd, statusCmd]) return userCmd
def init(self, userCmd=None, timeLim=None, getStatus=True): """Called automatically on startup after the connection is established. Only thing to do is query for status or connect if not connected """ log.info("%s.init(userCmd=%s, timeLim=%s, getStatus=%s)" % (self, userCmd, timeLim, getStatus)) # print("%s.init(userCmd=%s, timeLim=%s, getStatus=%s)" % (self, userCmd, timeLim, getStatus)) userCmd = expandUserCmd(userCmd) # if not self.isConnected: # return self.connect(userCmd=userCmd) # get the speed on startup # ignore getStatus flag, just do it always self.queueDevCmd("speed") return self.getStatus(userCmd=userCmd)
def init(self, userCmd=None, getStatus=True, timeLim=DefaultTimeLim): """! Initialize all devices, and get status if wanted @param[in] userCmd a UserCmd or None @param[in] getStatus if true, query all devices for status @param[in] timeLim """ userCmd = expandUserCmd(userCmd) log.info("%s.init(userCmd=%s, timeLim=%s, getStatus=%s)" % (self, userCmd, timeLim, getStatus)) if getStatus: self.cmd_status(userCmd) # sets done else: userCmd.setState(userCmd.Done) return userCmd
def startDevCmd(self, devCmdStr): """ @param[in] devCmdStr a line of text to send to the device """ devCmdStr = devCmdStr.lower() log.info("%s.startDevCmd(%r)" % (self, devCmdStr)) try: if self.conn.isConnected: log.info("%s writing %r" % (self, devCmdStr)) self.conn.writeLine(devCmdStr) else: self.currExeDevCmd.setState(self.currExeDevCmd.Failed, "Not connected") except Exception as e: self.currExeDevCmd.setState(self.currExeDevCmd.Failed, textMsg=strFromException(e))
def init(self, userCmd=None, timeLim=None, getStatus=True): """Called automatically on startup after the connection is established. Only thing to do is query for status or connect if not connected """ log.info("%s.init(userCmd=%s, timeLim=%s, getStatus=%s)" % (self, userCmd, timeLim, getStatus)) # print("%s.init(userCmd=%s, timeLim=%s, getStatus=%s)" % (self, userCmd, timeLim, getStatus)) userCmd = expandUserCmd(userCmd) # if not self.isConnected: # # time lim handled by lco.deviceCmd # return self.connect(userCmd=userCmd) if getStatus: return self.getStatus(userCmd=userCmd) else: userCmd.setState(userCmd.Done) return userCmd
def startDevCmd(self, devCmd): """ @param[in] devCmd a dev command """ log.info("%s.startDevCmd(%r)" % (self, devCmd.cmdStr)) #print("%s.startDevCmd(%r)" % (self, devCmd.cmdStr)) try: if self.conn.isConnected: log.info("%s writing %r" % (self, devCmd.cmdStr)) devCmd.setState(devCmd.Running) self.conn.writeLine(devCmd.cmdStr) else: self.currExeDevCmd.setState(self.currExeDevCmd.Failed, "Not connected") except Exception as e: self.currExeDevCmd.setState(self.currExeDevCmd.Failed, textMsg=strFromException(e))
def queueDevCmd(self, devCmd): """Add a device command to the device command queue @param[in] devCmd: a twistedActor DevCmd. """ log.info("%s.queueDevCmd(devCmd=%r, devCmdStr=%r, cmdQueue: %r"%(self, devCmd, devCmd.cmdStr, self.devCmdQueue)) # append a cmdVerb for the command queue (other wise all get the same cmdVerb and cancel eachother) # could change the default behavior in CommandQueue? devCmd.cmdVerb = devCmd.cmdStr def queueFunc(devCmd): # all tcs commands return immediately so set a short timeout devCmd.setTimeLimit(SEC_TIMEOUT) devCmd.setState(devCmd.Running) self.startDevCmd(devCmd.cmdStr) self.devCmdQueue.addCmd(devCmd, queueFunc)
def startDevCmd(self, devCmdStr): """ @param[in] devCmdStr a line of text to send to the device """ log.info("%s.startDevCmd(%r)" % (self, devCmdStr)) try: if self.conn.isConnected: log.info("%s writing %r" % (self, devCmdStr)) self.conn.writeLine(devCmdStr) if devCmdStr == "init": self.waitingForInitEcho = True self.currDevCmdStr = devCmdStr else: self.currExeCmd.setState(self.currExeCmd.Failed, "Not connected") except Exception as e: self.currExeCmd.setState(self.currExeCmd.Failed, textMsg=strFromException(e))
def getStatus(self, userCmd=None): """Return current telescope status. Continuously poll. """ log.info("%s.getStatus(userCmd=%s)" % (self, userCmd)) # logging this will flood the log # print("%s.getStatus(userCmd=%s)" % (self, userCmd)) userCmd = expandUserCmd(userCmd) if not self.conn.isConnected: userCmd.setState(userCmd.Failed, "Not Connected to M2") return userCmd self._statusTimer.cancel() # incase a status is pending userCmd = expandUserCmd(userCmd) # userCmd.addCallback(self._statusCallback) # gather list of status elements to get statusCmd = self.queueDevCmd("status", userCmd) LinkCommands(userCmd, [statusCmd]) return userCmd
def queueDevCmd(self, devCmdStr, userCmd): """Add a device command to the device command queue @param[in] devCmdStr: a command string to send to the device. @param[in] userCmd: a UserCmd associated with this device (probably but not necessarily linked. Used here for writeToUsers reference. """ log.info("%s.queueDevCmd(devCmdStr=%r, cmdQueue: %r"%(self, devCmdStr, self.devCmdQueue)) #print("%s.queueDevCmd(devCmdStr=%r, cmdQueue: %r"%(self, devCmdStr, self.devCmdQueue)) # append a cmdVerb for the command queue (otherwise all get the same cmdVerb and cancel eachother) # could change the default behavior in CommandQueue? devCmd = DevCmd(cmdStr=devCmdStr) devCmd.userCmd = userCmd devCmd.cmdVerb = devCmdStr self.devCmdQueue.addCmd(devCmd, self.startDevCmd) return devCmd
def parseAndDispatchCmd(self, cmd): """Dispatch the user command, parse the cmd string and append the result as a parsedCommand attribute on cmd @param[in] cmd user command (a twistedActor.UserCmd) """ log.info("%s.parseAndDispatchCmd cmdBody=%r"%(self, cmd.cmdBody)) if not cmd.cmdBody: # echo to show alive self.writeToOneUser(":", "", cmd=cmd) return parsedCommand = arcticCommandSet.parse(cmd.cmdBody) # append the parsedCommand to the cmd object, and send along cmd.parsedCommand = parsedCommand return Actor.parseAndDispatchCmd(self, cmd)
def queueDevCmd(self, cmdStr, userCmd=None): """Add a device command to the device command queue @param[in] cmdStr, string to send to the device. """ log.info("%s.queueDevCmd(cmdStr=%r, cmdQueue: %r"%(self, cmdStr, self.devCmdQueue)) # print("%s.queueDevCmd(devCmd=%r, devCmdStr=%r, cmdQueue: %r"%(self, devCmd, devCmd.cmdStr, self.devCmdQueue)) # append a cmdVerb for the command queue (other wise all get the same cmdVerb and cancel eachother) # could change the default behavior in CommandQueue? userCmd = expandUserCmd(userCmd) devCmd = DevCmd(cmdStr) devCmd.cmdVerb = cmdStr.split()[0] devCmd.userCmd = userCmd def queueFunc(devCmd): self.startDevCmd(devCmd) self.devCmdQueue.addCmd(devCmd, queueFunc) return devCmd
def move(self, valueList, offset=False, userCmd=None): """Command an offset or absolute orientation move @param[in] valueList: list of 1 to 5 values specifying pistion(um), tiltx("), tilty("), transx(um), transy(um) @param[in] offset, if true this is offset, else absolute @param[in] userCmd: a twistedActor BaseCommand Note: increasing distance eg pistion means increasing spacing between primary and secondary mirrors. """ log.info("%s.move(userCmd=%s, valueList=%s, offset=%s)" % (self, userCmd, str(valueList), str(bool(offset)))) userCmd = expandUserCmd(userCmd) if not self.waitMoveCmd.isDone: userCmd.setState(userCmd.Failed, "Mirror currently moving") return userCmd if not 1<=len(valueList)<=5: userCmd.setState(userCmd.Failed, "Must specify 1 to 5 numbers for a move") return userCmd self.waitMoveCmd = UserCmd() self.waitMoveCmd.userCmd = userCmd # for write to users self.status.desOrientation = self.status.orientation[:] if offset: # if offset is specified, offset from current value for ii, value in enumerate(valueList): self.status.desOrientation[ii] += value else: # if absolute is wanted, overwrite all those # specified for ii, value in enumerate(valueList): self.status.desOrientation[ii] = value cmdType = "offset" if offset else "move" strValList = " ".join(["%.2f"%val for val in valueList]) cmdStr = "%s %s"%(cmdType, strValList) moveCmd = self.queueDevCmd(cmdStr, userCmd) statusCmd = self.queueDevCmd("status", userCmd) # status immediately to see moving state # determine total time for move # just use focus distance as proxy (ignore) galilOverHead = 2 # galil take roughly 2 secs to boot up. extraOverHead = 2 # self.status._moveTimeTotal = self.getTimeForMove() timeout = self.status._moveTimeTotal+galilOverHead+extraOverHead userCmd.setTimeLimit(timeout) LinkCommands(userCmd, [moveCmd, statusCmd, self.waitMoveCmd]) return userCmd
def doExpose(self, userCmd, expType, expTime, basename=None, comment=None): """!Begin a camera exposure @param[in] userCmd: a twistedActor UserCmd instance @param[in] expType: string, one of object, flat, dark, bias @param[in] expTime: float, exposure time. """ if not self.exposeCmd.isDone: userCmd.setState(userCmd.Failed, "cannot start new exposure, self.exposeCmd not done") return if self.pollTimer.isActive: userCmd.setState(userCmd.Failed, "cannot start new exposure, self.pollTimer is active - bug") return self.exposeCmd = userCmd expTypeEnum = ExpTypeDict.get(expType) if basename: # save location was specified expName = basename else: # wasn't specified choose a default place/name if not os.path.exists(self.imageDir): os.makedirs(self.imageDir) expName = os.path.abspath("%s_%d.fits" % (expType, self.expNum)) expName = "%s_%d.fits" % (expType, self.expNum) expName = os.path.join(self.imageDir, expName) # print "startExposure(%r, %r, %r)" % (expTime, expTypeEnum, expName) self.expStartTime = datetime.datetime.now() log.info("startExposure(%r, %r, %r)" % (expTime, expTypeEnum, expName)) self.expName = expName self.comment = comment self.expType = expType self.expTime = expTime self.readingFlag = False try: self.camera.startExposure(expTime, expTypeEnum, expName) self.writeToUsers("i", self.exposureStateKW, self.exposeCmd) if expType.lower() in ["object", "flat"]: self.writeToUsers("i", "shutter=open") # fake shutter self.expNum += 1 self.pollCamera() except RuntimeError as e: self.exposeCmd.setState(self.exposeCmd.Failed, str(e)) # note, check what state the exposure kw is in after a runtime error here? self.exposeCleanup()
def startDevCmd(self, devCmdStr): """ @param[in] devCmdStr a line of text to send to the device """ devCmdStr = devCmdStr.upper() # lco uses all upper case log.info("%s.startDevCmd(%r)" % (self, devCmdStr)) try: if self.conn.isConnected: log.info("%s writing %r" % (self, devCmdStr)) if CMDOFF.upper() == devCmdStr: self.waitOffsetCmd.setState(self.waitOffsetCmd.Running) elif "CIR" in devCmdStr: self.waitRotCmd.setState(self.waitRotCmd.Running) self.conn.writeLine(devCmdStr) else: self.currExeDevCmd.setState(self.currExeDevCmd.Failed, "Not connected to TCS") except Exception as e: self.currExeDevCmd.setState(self.currExeDevCmd.Failed, textMsg=strFromException(e))
def slewOffset(self, ra, dec, userCmd=None): """Offset telescope in right ascension and declination. @param[in] ra: right ascension in decimal degrees @param[in] dec: declination in decimal degrees @param[in] userCmd a twistedActor BaseCommand @todo, consolidate similar code with self.target? """ log.info("%s.slewOffset(userCmd=%s, ra=%.6f, dec=%.6f)" % (self, userCmd, ra, dec)) userCmd = expandUserCmd(userCmd) # zero the delta computation so the offset isn't marked done immediately self.status.previousDec = ForceSlew self.status.previousRA = ForceSlew if not self.conn.isConnected: userCmd.setState(userCmd.Failed, "Not Connected to TCS") return userCmd if not self.waitOffsetCmd.isDone: self.waitOffsetCmd.setState(self.waitOffsetCmd.Cancelled, "Superseded by new offset") waitOffsetCmd = UserCmd() self.waitOffsetCmd = waitOffsetCmd enterRa = "OFRA %.8f"%(ra*ArcSecPerDeg) enterDec = "OFDC %.8f"%(dec*ArcSecPerDeg) #lcohack devCmdList = [DevCmd(cmdStr=cmdStr) for cmdStr in [enterRa, enterDec, CMDOFF]] # set userCmd done only when each device command finishes # AND the pending slew is also done. # set an offset done after 6 seconds no matter what def setWaitOffsetCmdDone(aWaitingOffsetCmd): print("wait offset command state", aWaitingOffsetCmd.state) if not aWaitingOffsetCmd.isDone: print("Wait offset timed out!!!!") self.writeToUsers("w", "Text=OFFSET SET DONE ON TIMER.") aWaitingOffsetCmd.setState(aWaitingOffsetCmd.Done, "offset set done on a timer") self.waitOffsetTimer.start(8, setWaitOffsetCmdDone, waitOffsetCmd) # self.waitOffsetCmd.setTimeLimit(6) LinkCommands(userCmd, devCmdList + [self.waitOffsetCmd]) for devCmd in devCmdList: self.queueDevCmd(devCmd) statusStr = self.status.getStatusStr() if statusStr: self.writeToUsers("i", statusStr, userCmd) return userCmd
def home(self, userCmd=None): log.info("%s.home(userCmd=%s)" % (self, userCmd)) setCountCmd = self.measScaleDev.setCountState() def finishHome(_statusCmd): if _statusCmd.didFail: userCmd.setState(userCmd.Failed, "status failed.") elif _statusCmd.isDone: # assert that the encoders are really reading # zeros if not numpy.all(numpy.abs(self.measScaleDev.encPos) < 0.001): userCmd.setState(userCmd.Failed, "zeros failed to set: %s"%str(self.measScaleDev.encPos)) else: userCmd.setState(userCmd.Done) def getStatus(_zeroEncCmd): if _zeroEncCmd.didFail: userCmd.setState(userCmd.Failed, "Encoder zero failed.") elif _zeroEncCmd.isDone: statusCmd = self.getStatus() statusCmd.addCallback(finishHome) def zeroEncoders(_moveCmd): if _moveCmd.didFail: userCmd.setState(userCmd.Failed, "Failed to move scaling ring to home position") elif _moveCmd.isDone: # zero the encoders in 1 second (give the ring a chance to stop) def zeroEm(): zeroEncCmd = self.measScaleDev.setZero() zeroEncCmd.addCallback(getStatus) reactor.callLater(1., zeroEm) def moveThreadRing(_setCountCmd): if _setCountCmd.didFail: userCmd.setState(userCmd.Failed, "Failed to set Mitutoyo EV counter into counting state") elif _setCountCmd.isDone: moveCmd = self.queueDevCmd("move %.6f"%self.measScaleDev.zeroPoint, userCmd) moveCmd.addCallback(zeroEncoders) setCountCmd.addCallback(moveThreadRing)
def startDevCmd(self, devCmd): """ @param[in] devCmdStr a line of text to send to the device """ devCmdStr = devCmd.cmdStr.lower() # m2 uses all lower case log.info("%s.startDevCmd(%r)" % (self, devCmdStr)) try: if self.conn.isConnected: log.info("%s writing %r" % (self, devCmdStr)) # set move command to running now. Bug if set earlier race condition # with status if "move" in devCmdStr.lower() or "offset" in devCmdStr.lower(): self.waitMoveCmd.setState(self.waitMoveCmd.Running) self.status.state = Moving self.writeToUsers("i", self.status.secStateStr(), devCmd.userCmd) # if "galil" in devCmdStr.lower(): # self.waitGalilCmd.setState(self.waitGalilCmd.Running) self.conn.writeLine(devCmdStr) else: self.currExeDevCmd.setState(self.currExeDevCmd.Failed, "Not connected to M2") except Exception as e: self.currExeDevCmd.setState(self.currExeDevCmd.Failed, textMsg=strFromException(e))
def handleReply(self, replyStr): """Handle a line of output from the device. Called whenever the device outputs a new line of data. @param[in] replyStr the reply, minus any terminating \n Tasks include: - Parse the reply - Manage the pending commands - Output data to users - Parse status to update the model parameters - If a command has finished, call the appropriate command callback """ log.info("%s read %r, currExeCmd: %r" % (self, replyStr, self.currExeCmd)) # print("%s read %r, currExeCmd: %r, currDevCmdStr: %s" % (self, replyStr, self.currExeCmd, self.currDevCmdStr)) if self.currExeCmd.isDone: log.info("Ignoring unsolicited output from Galil: %s " % replyStr) return gotOK = False if replyStr == "OK": replyStr = "" gotOK = True elif replyStr.endswith(" OK"): gotOK = True replyStr = replyStr[:-3] # handle things specially for init if self.waitingForInitEcho: if replyStr != "init": return else: # saw init echo self.waitingForInitEcho = False replyStr = "" if replyStr and replyStr != self.currDevCmdStr: # must be status parse and set accordingly self.parseStatusLine(replyStr) if gotOK: self.currExeCmd.setState(self.currExeCmd.Done)
def getStatus(self, userCmd=None): """Return current telescope status. Continuously poll. """ log.info("%s.getStatus(userCmd=%s)" % (self, userCmd)) # logging this will flood the log userCmd = expandUserCmd(userCmd) if not self.conn.isConnected: userCmd.setState(userCmd.Failed, "Not Connected to TCS") return userCmd self._statusTimer.cancel() # incase a status is pending userCmd = expandUserCmd(userCmd) userCmd.addCallback(self._statusCallback) # record the present RA, DEC (for determining when offsets are done) # self.status.previousRA = self.status.statusFieldDict["ra"].value # self.status.previousDec = self.status.statusFieldDict["dec"].value if self.status.statusFieldDict["mpos"].value is None: self.status.previousRA, self.status.previousDec = None, None else: self.status.previousRA = self.status.statusFieldDict["mpos"].value[0] self.status.previousDec = self.status.statusFieldDict["mpos"].value[1] # gather list of status elements to get devCmdList = [DevCmd(cmdStr=cmdVerb) for cmdVerb in self.status.statusFieldDict.keys()] LinkCommands(userCmd, devCmdList) for devCmd in devCmdList: self.queueDevCmd(devCmd) if not self.status.isClamped or self.rotDelay: # rotator is moving, get status frequently pollTime = PollTimeRot elif self.isSlewing: # slewing, get status kinda frequently pollTime = PollTimeSlew elif self.isTracking: # tracking, get status less frequently pollTime = PollTimeTrack else: # idle, get status infrequently (as things shouldn't be changing fast) pollTime = PollTimeIdle self._statusTimer.start(pollTime, self.getStatus) return userCmd
def pollCamera(self): """Begin continuously polling the camera for exposure status, write the image when ready. """ expState = self.camera.getExposureState() if expState.state == arctic.Reading and not self.readingFlag: self.readingFlag = True self.writeToUsers("i", "shutter=closed") # fake shutter # self.startReadTime = time.time() self.writeToUsers("i", self.exposureStateKW, self.exposeCmd) if expState.state == arctic.ImageRead: log.info("saving image: exposure %s"%self.expName) self.camera.saveImage() # saveImage sets camera exp state to idle # write headers self.writeHeaders() # clean up log.info("exposure %s complete"%self.expName) self.exposeCleanup() elif expState.state == arctic.Idle: log.warn("pollCamera() called but exposure state is idle. Should be harmless, but why did it happen?") self.exposeCleanup() else: # if the camera is not idle continue polling self.pollTimer.start(0.05, self.pollCamera)
def init(self, userCmd=None, getStatus=True, timeLim=DefaultTimeLim): """! Initialize all devices, and get status if wanted @param[in] userCmd a UserCmd or None @param[in] getStatus if true, query all devices for status @param[in] timeLim """ userCmd = expandUserCmd(userCmd) log.info("%s.init(userCmd=%s, timeLim=%s, getStatus=%s)" % (self, userCmd, timeLim, getStatus)) # initialize camera self.setCamera() subCmdList = [] # connect (if not connected) and initialize devices filterDevCmd = expandUserCmd(None) shutterDevCmd = expandUserCmd(None) subCmdList.append(filterDevCmd) subCmdList.append(shutterDevCmd) if not self.filterWheelDev.isConnected: self.filterWheelDev.connect(userCmd=filterDevCmd) else: self.filterWheelDev.init(userCmd=filterDevCmd) if not self.shutterDev.isConnected: self.shutterDev.connect(userCmd=shutterDevCmd) else: self.shutterDev.init(userCmd=shutterDevCmd) if getStatus: # get status only when the conn/initialization is done def getStatus(foo): if userCmd.isDone: self.getStatus() userCmd.addCallback(getStatus) LinkCommands(mainCmd=userCmd, subCmdList=subCmdList) if not self.exposeCmd.isDone: self.exposeCmd.setState(self.exposeCmd.Failed, "currently running exposure killed via init") self.exposeCleanup() return userCmd
def testSimplestCase(self): logMsg = "I was just logged" log.info(logMsg) loggedInfo = self.getLogInfo(self.logFilePath) self.assertEqual(len(loggedInfo), 1) # only one line in log self.assertEqual(loggedInfo[0][1], logMsg)
def handleReply(self, reply): """!Called each time a reply comes through the line """ # print "%s.handleReply(reply=%r)" % (self, reply) log.info("%s read %r" % (self, reply))