def genStateKeys(self, cmd=None): ''' Generates command info statements for commmand keys Format: [commandName]_keyword = currentset_value, default_value e.g. doMangaSequence_count=1,3; doMangaSequence_dithers="NSE","NSE" doMangaSequence_expTime=900.0,900.0; doMangaSequence_ditherSeq=NSE,0 ''' cmd = self._getCmd(cmd) msg = [] for keyName, default in self.keywords.iteritems(): val = getattr(self, keyName) if type(default) == str: val = qstr(val) default = qstr(default) msg.append("%s_%s=%s,%s" % (self.name, keyName, val, default)) if msg: cmd.inform("; ".join(msg)) try: userKeys = self.getUserKeys() except: userKeys = [] cmd.warn('text="failed to fetch all keywords for %s"' % (self.name)) if userKeys: cmd.inform(";".join(userKeys))
def makeCardFromKey(cmd, keyDict, keyName, cardName, cnv=None, idx=None, comment='', onFail=None): """ Creates a pyfits Card from a Key. Does not raise exceptions. """ try: val = keyDict[keyName] except KeyError as e: errStr = "failed to fetch %s" % (keyName) cmd.warn('text=%s' % (qstr(errStr))) return makeCard(cmd, cardName, onFail, errStr) try: if idx is not None: val = val[idx] else: val = val.getValue() except Exception as e: errStr = "failed to index %s by %s from %s for %s: %s" % \ (val, idx, keyName, cardName, e) cmd.warn('text=%s' % (qstr(errStr))) return makeCard(cmd, cardName, onFail, errStr) if cnv is not None: try: val = cnv(val) except Exception as e: errStr = "failed to convert %s from %s for %s using %s: %s" % \ (val, keyName, cardName, cnv, e) cmd.warn('text=%s' % (qstr(errStr))) return makeCard(cmd, cardName, onFail, errStr) return makeCard(cmd, cardName, val, comment)
def _reloadConfiguration(self, cmd=None): logging.info("reading config file %s", self.configFile) try: newConfig = configparser.ConfigParser() newConfig.read(self.configFile) self.config = newConfig except Exception as e: if cmd: cmd.warn('text=%s' % (qstr( "failed to read the configuration file, old config untouched: %s" % (e)))) try: newConfig = instdata.InstConfig(self.name, idDict=self.idDict) except Exception as e: if cmd: cmd.fail('text=%s' % (qstr( "failed to load instdata configuration file, old config untouched: %s" % (e)))) raise self.actorConfig = newConfig self.configureLogs() try: # Call optional user hook. self.reloadConfiguration(cmd) except: pass
def version(self, cmd, doFinish=True): """ Return a version keyword. """ versionString = self.actor.versionString(cmd) if doFinish: cmd.finish('version=%s' % (qstr(versionString))) else: cmd.respond('version=%s' % (qstr(versionString)))
def sendVersionKey(self, cmd): """ Generate the version keyword in response to cmd. """ setupVersions = versions.allSetupVersions() for prodName in setupVersions.keys(): versionString = setupVersions[prodName] cmd.inform(f'version_{prodName}=%s' % qstr(versionString)) versionString = self.versionString(cmd) cmd.inform('version=%s' % (qstr(versionString)))
def lineReceived(self, cmdString): """ Called when a complete line has been read from the hub. """ cmdString = cmdString.decode('latin-1') # Telnet connections send back '\r\n'. Or worse, I fear. Try to make # those connections work just like properly formatted ones. if not self.delimiterChecked: while len(cmdString) > 0 and cmdString[-1] < ' ': self.delimiter = cmdString[-1] + self.delimiter cmdString = cmdString[:-1] self.delimiterChecked = True # Parse the header... m = self.cmdRe.match(cmdString) if not m: self.brains.bcast.warn( 'text=%s' % (qstr("cannot parse header for %s" % (cmdString)))) cmdLogger.critical('cannot parse header for: %s' % (cmdString)) return cmdDict = m.groupdict() # Look for, or create, an integer MID. rawMid = cmdDict['mid'] if rawMid == '' or rawMid == None: # Possibly self-assign a MID mid = self.mid self.mid += 1 else: try: mid = int(rawMid) except Exception as e: self.brains.bcast.warn( 'text=%s' % (qstr("command ignored: MID is not an integer in %s" % cmdString))) cmdLogger.critical('MID must be an integer: %s' % (rawMid)) return if mid >= self.mid: self.mid += 1 cmdrName = cmdDict['cmdrName'] if cmdrName == '' or cmdrName == None: cmdrName = 'self.%d' % (self.connID) # Fabricate a connection ID. # And hand it upstairs. try: cmd = Command(self.factory, cmdrName, self.connID, mid, cmdDict['cmdString']) self.brains.newCmd(cmd) except Exception as e: self.brains.bcast.fail( 'text=%s' % (qstr("cannot process command: %s (exception=%s)" % (cmdDict['cmdString'], e)))) cmdLogger.warn(tback('lineReceived', e))
def cmdHelp(self, cmd): """ Return a summary of all commands, or the complete help string for the specified commands. Also allows generating an html file. """ cmds = [] for a, cSet in list(self.actor.commandSets.items()): cmds.extend(self._vocabCmds(cSet.vocab)) fullHelp = False cmds.sort() if "cmds" in cmd.cmd.keywords: cmdRe = re.compile(cmd.cmd.keywords['cmds'].values[0]) cmds = filter(cmdRe.search, cmds) fullHelp = True if "full" in cmd.cmd.keywords: fullHelp = True pageWidth = int(cmd.cmd.keywords['pageWidth'].values[0]) if "pageWidth" in cmd.cmd.keywords else 80 html = "html" in cmd.cmd.keywords first = True for cmdName in cmds: helpList = [] for csetName, cSet in list(self.actor.commandSets.items()): if cmdName in self._vocabCmds(cSet.vocab): try: helpStr = help.help(self.actor.name, cmdName, cSet.vocab, cSet.keys, pageWidth, html, fullHelp=fullHelp) except Exception as e: helpStr = "something went wrong when building help for %s: %s" % (cmdName, e) cmd.warn('text=%s' % (qstr(helpStr))) helpList.append(helpStr) if not helpList: cmd.warn('text="I\'m afraid that I can\'t help you with %s"' % cmdName) continue if first: first = False elif fullHelp: cmd.inform('help=%s' % qstr("--------------------------------------------------")) for helpChunk in helpList: for line in helpChunk.split('\n'): cmd.inform('help=%s' % qstr(line)) cmd.finish("")
def triggerHubConnection(self): """ Send the hub a command to connect back to us. """ if not self.cmdr: self.bcast.warn('text="CANNOT ask hub to connect to us, since we do not have a connection to it yet!"') return if not self.acceptCmdrs: self.logger.warn('not triggering hub callback.') return # Look for override on where tron should connect back to. For tunnels, etc. try: advertisedSocket = self.config.get(self.name, 'hostAndPortForTron') ourHost, ourPort = advertisedSocket.split(':') except: ourAddr = self.commandSources.listeningPort.getHost() ourPort = ourAddr.port ourHost = ourAddr.host cmdStr = "startNub %s %s:%s" % (self.name, ourHost, ourPort) self.logger.info('text=%s' % (qstr("asking the hub to connect back to us with: %s" % (cmdStr)))) self.cmdr.dispatcher.executeCmd(opscore.actor.keyvar.CmdVar(actor='hub', cmdStr=cmdStr, timeLim=5.0))
def shutdown(self, why="cuz"): """ Called from above when we want to drop the connection. """ self.brains.bcast.respond( 'text=%s' % (qstr("shutting connection %d down" % (self.connID)))) actorLogger.info("CommandLink.shutdown because %s", why) self.transport.loseConnection()
def standbyOff(self, cmd): """ Drop out of standby mode and go back to full-speed.""" ctrlr = cmd.cmd.name ret = self.actor.controllers[ctrlr].stopStandby(cmd=cmd) cmd.finish('text=%r' % (qstr(ret)))
def gotoPosition(self, cmd, name, az, alt, rot): """Goto a specified alt/az/[rot] position, named 'name'.""" sopState = myGlobals.actorState cmdState = sopState.gotoPosition keywords = cmd.cmd.keywords blocked = self.isSlewingDisabled(cmd) if blocked: cmd.fail('text=%s' % (qstr('will not {0}: {1}'.format(name, blocked)))) return if 'stop' in keywords or 'abort' in keywords: self.stop_cmd(cmd, cmdState, sopState, 'gotoPosition') return if self.modifiable(cmd, cmdState): # Modify running gotoPosition command cmd.fail('text="Cannot modify {0}."'.format(name)) return cmdState.reinitialize(cmd, output=False) cmdState.set('alt', alt) cmdState.set('az', az) cmdState.set('rot', rot) activeStages = ['slew'] cmdState.setupCommand(cmd, activeStages) sopState.queues[sopActor.SLEW].put(Msg.GOTO_POSITION, cmd, replyQueue=self.replyQueue, actorState=sopState, cmdState=cmdState)
def standby(self, cmd): """ Go into standby mode, where the pump runs at a lower speed than normal. """ percent = cmd.cmd.keywords['percent'].values[0] ret = self.actor.controllers['pump'].startStandby(percent=percent, cmd=cmd) cmd.finish('text=%r' % (qstr(ret)))
def gaugeRaw(self, cmd): """ Send a raw command to a rough-side pressure gauge. """ cmd_txt = cmd.cmd.keywords['raw'].values[0] ret = self.actor.controllers['gauge'].gaugeCmd(cmd_txt, cmd=cmd) cmd.finish('text="returned %s"' % (qstr(ret)))
def sendCommandStr(self, cmdStr, cmd=None): if len(cmdStr) > 0 and cmdStr[0] != '~': cmdStr = f'~{cmdStr}' fullCmd = "%s%s" % (cmdStr, self.EOL) writeCmd = fullCmd.encode('latin-1') with self.deviceLock: if cmd is not None: cmd.debug('text=%s' % (qstr("sending %r" % fullCmd))) self.logger.debug("sending command :%r:" % (fullCmd)) try: self.device.write(writeCmd) except serial.writeTimeoutError: raise except serial.SerialException: raise except Exception: raise try: ret = self.readResponse(cmd=cmd) except EOFError: raise EOFError( f"no response from {self.name}; sent :{fullCmd}:") if ret != cmdStr: raise RuntimeError( "command echo mismatch. sent :%r: rcvd :%r:" % (cmdStr, ret)) ret = self.readResponse(cmd=cmd) return ret
def triggerHubConnection(self): """ Send the hub a command to connect back to us. """ if not self.cmdr: self.bcast.warn( 'text="CANNOT ask hub to connect to us, since we do not have a connection to it yet!"' ) return if not self.acceptCmdrs: self.logger.warn('not triggering hub callback.') return # Look for override on where tron should connect back to. For tunnels, etc. try: advertisedSocket = self.actorConfig['listen']['hostAndPortForTron'] ourHost, ourPort = advertisedSocket.split(':') except: ourAddr = self.commandSources.listeningPort.getHost() ourPort = ourAddr.port ourHost = ourAddr.host cmdStr = "startNub %s %s:%s" % (self.name, ourHost, ourPort) self.logger.info( 'text=%s' % (qstr("asking the hub to connect back to us with: %s" % (cmdStr)))) self.cmdr.dispatcher.executeCmd( opscore.actor.keyvar.CmdVar(actor='hub', cmdStr=cmdStr, timeLim=5.0))
def raw(self, cmd): """ Send a raw FEE command. """ cmdTxt = cmd.cmd.keywords['raw'].values[0] ret = self.actor.fee.sendCommandStr(cmdTxt, noTilde=(cmdTxt in {'reset'})) cmd.finish('text=%s' % (qstr('returned: %s' % (ret))))
def pcmGaugeRaw(self, cmd): """ Send a raw text command to the PCM's gauge controller. """ cmd_txt = cmd.cmd.keywords['raw'].values[0] ret = self.actor.controllers['PCM'].gaugeRawCmd(cmd_txt, cmd=cmd) cmd.finish('text="returned %s"' % (qstr(ret)))
def status(self, cmd): """Report enu status, actor version and each controller status """ cmd.inform('version=%s' % (qstr(getVersion('ics_enuActor')))) cmd.inform('actorcore=%s' % (qstr(getVersion('tron_actorcore')))) cmd.inform('actorkeys=%s' % (qstr(getVersion('ics_actorkeys')))) cmd.inform('text="monitors: %s"' % self.actor.monitors) cmd.inform('text="config id=0x%08x %r"' % (id(self.actor.config), self.actor.config.sections())) self.actor.updateStates(cmd=cmd) if 'all' in cmd.cmd.keywords: for c in self.actor.controllers: self.actor.callCommand("%s status" % c) cmd.finish(self.controllerKey())
def sunssRaw(self, cmd): """ Send a raw command to the temps controller. """ cmd_txt = cmd.cmd.keywords['raw'].values[0] ret = self.pi.sunssCmd(cmd_txt, cmd=cmd) cmd.finish('text=%s' % (qstr('returned: %s' % (ret))))
def powerFee(self, state, cmd): cmdString = "power %s fee" % (state) cmdVar = self.actor.cmdr.call(actor='xcu_%s' % self.cam, cmdStr=cmdString, forUserCmd=cmd, timeLim=5.0) if cmdVar.didFail: cmd.fail('text=%s' % (qstr('Failed to power fee %s' % (state)))) return
def mangaDither(self, cmd): """Specify a particular manga dither position for decentered guiding.""" # ra, dec, rot dithers = { 'N': { 'decenterRA': -0.417, 'decenterDec': +0.721, 'decenterRot': 0.0 }, 'S': { 'decenterRA': -0.417, 'decenterDec': -0.721, 'decenterRot': 0.0 }, 'E': { 'decenterRA': +0.833, 'decenterDec': 0., 'decenterRot': 0.0 }, 'C': { 'decenterRA': 0., 'decenterDec': 0., 'decenterRot': 0.0 } } ditherPos = cmd.cmd.keywords['ditherPos'].values[0] try: decenters = dithers[ditherPos] decenters['mangaDither'] = ditherPos except KeyError: cmd.fail("text=%s" % qstr( "Failed to parse manga dither position: %s" % ditherPos)) else: masterQueue = myGlobals.actorState.queues[guiderActor.MASTER] masterQueue.put(Msg(Msg.DECENTER, cmd=cmd, decenters=decenters))
def getRaw(self, cmd): """ Send a direct query command to the PCM's gauge controller. """ cmdCode = cmd.cmd.keywords['getRaw'].values[0] ret = self.actor.controllers['gauge'].gaugeRawQuery(cmdCode, cmd=cmd) cmd.finish('text=%s' % (qstr("returned %r" % ret)))
def tempsRaw(self, cmd): """ Send a raw command to the temps controller. """ cmd_txt = cmd.cmd.keywords['raw'].values[0] ret = self.actor.controllers['ltemps'].tempsCmd(cmd_txt, cmd=cmd) cmd.finish('text="returned %s"' % (qstr(ret)))
def lineReceived(self, cmdString): """ Called when a complete line has been read from the hub. """ cmdString = cmdString.decode('latin-1') # Telnet connections send back '\r\n'. Or worse, I fear. Try to make # those connections work just like properly formatted ones. if not self.delimiterChecked: while len(cmdString) > 0 and cmdString[-1] < ' ': self.delimiter = cmdString[-1]+self.delimiter cmdString = cmdString[:-1] self.delimiterChecked = True # Parse the header... m = self.cmdRe.match(cmdString) if not m: self.brains.bcast.warn('text=%s' % (qstr("cannot parse header for %s" % (cmdString)))) cmdLogger.critical('cannot parse header for: %s' % (cmdString)) return cmdDict = m.groupdict() # Look for, or create, an integer MID. rawMid = cmdDict['mid'] if rawMid == '' or rawMid == None: # Possibly self-assign a MID mid = self.mid self.mid += 1 else: try: mid = int(rawMid) except Exception as e: self.brains.bcast.warn('text=%s' % (qstr("command ignored: MID is not an integer in %s" % cmdString))) cmdLogger.critical('MID must be an integer: %s' % (rawMid)) return if mid >= self.mid: self.mid += 1 cmdrName = cmdDict['cmdrName'] if cmdrName == '' or cmdrName == None: cmdrName = 'self.%d' % (self.connID) # Fabricate a connection ID. # And hand it upstairs. try: cmd = Command(self.factory, cmdrName, self.connID, mid, cmdDict['cmdString']) self.brains.newCmd(cmd) except Exception as e: self.brains.bcast.fail('text=%s' % (qstr("cannot process command: %s (exception=%s)" % (cmdDict['cmdString'], e)))) cmdLogger.warn(tback('lineReceived', e))
def set(self, name, value): """Sets self.name == value. if Value is None, use the default value.""" assert name in self.keywords, qstr("%s is not in keyword list: %s" % (name, str(self.keywords))) if value is not None: setattr(self, name, value) else: setattr(self, name, self.keywords[name])
def genCommandKeys(self, cmd=None): """ Return a list of the keywords describing our command. """ cmd = self._getCmd(cmd) cmd.inform("%sStages=%s" % (self.name, ",".join([qstr(sname) \ for sname in self.allStages]))) self.genCmdStateKeys(cmd=cmd)
def _doExpose(self, cmd, expTime, expType): """ Take an exposure and save it to disk. """ image = self.actor.camera.expose(cmd, expTime, expType) filename = self.getNextFilename(cmd) pyfits.writeto(filename, image, checksum=False, clobber=True) cmd.inform("filename=%s" % (qstr(filename))) return filename, image
def makeCard(cmd, name, value, comment=''): """ Creates a pyfits Card. Does not raise exceptions. """ try: c = pyfits.Card(name, value, comment) return (name, value, comment) except: errStr = 'failed to make %s card from %s' % (name, value) cmd.warn('text=%s' % (qstr(errStr))) return ('comment', errStr, '')
def standby(self, cmd): """ Put the pump into "standby mode", which is at a lower speed than normal mode. Note that the pump must be in normal mode for this to take effect. """ percent = cmd.cmd.keywords['percent'].values[0] ret = self.actor.controllers['turbo'].startStandby(percent=percent, cmd=cmd) cmd.finish('text=%r' % (qstr(ret)))
def runActorCmd(self, cmd): try: cmdStr = cmd.rawCmd if self.cmdLog.level <= logging.DEBUG: self.cmdLog.debug('raw cmd: %s' % (cmdStr)) try: validatedCmd, cmdFuncs = self.handler.match(cmdStr) except Exception as e: cmd.fail('text=%s' % (qstr("Unmatched command: %s (exception: %s)" % (cmdStr, e)))) # tback('actor_loop', e) return if not validatedCmd: cmd.fail('text=%s' % (qstr("Unrecognized command: %s" % (cmdStr)))) return self.cmdLog.debug('< %s:%d %s' % (cmd.cmdr, cmd.mid, validatedCmd)) if len(cmdFuncs) > 1: cmd.warn('text=%s' % (qstr("command has more than one callback (%s): %s" % (cmdFuncs, validatedCmd)))) try: cmd.cmd = validatedCmd for func in cmdFuncs: func(cmd) except Exception as e: oneLiner = self.cmdTraceback(e) cmd.fail('text=%s' % (qstr("command failed: %s" % (oneLiner)))) # tback('newCmd', e) return except Exception as e: cmd.fail('text=%s' % (qstr( "completely unexpected exception when processing a new command: %s" % (e)))) try: tback('newCmdFail', e) except: pass
def setRaw(self, cmd): """ Send a direct control command to the PCM's gauge controller. """ parts = cmd.cmd.keywords['setRaw'].values[0] cmdCode, cmdValue = parts cmd.diag('text="code=%r, value=%r"' % (cmdCode, cmdValue)) ret = self.actor.controllers['PCM'].gaugeRawSet(cmdCode, cmdValue, cmd=cmd) cmd.finish('text=%s' % (qstr("returned %r" % ret)))
def runActorCmd(self, cmd): self.output_file() try: cmdStr = cmd.rawCmd self.cmdLog.debug('raw cmd: %s' % (cmdStr)) try: validatedCmd, cmdFuncs = self.handler.match(cmdStr) except Exception as e: cmd.fail('text=%s' % (qstr("Unmatched command: %s (exception: %s)" % (cmdStr, e)))) # tback('actor_loop', e) return if not validatedCmd: cmd.fail('text=%s' % (qstr("Unrecognized command: %s" % (cmdStr)))) return self.cmdLog.info('< %s:%d %s' % (cmd.cmdr, cmd.mid, validatedCmd)) if len(cmdFuncs) > 1: cmd.warn('text=%s' % (qstr("command has more than one callback (%s): %s" % (cmdFuncs, validatedCmd)))) try: cmd.cmd = validatedCmd for func in cmdFuncs: func(cmd) except Exception as e: oneLiner = self.cmdTraceback(e) cmd.fail('text=%s' % (qstr("command failed: %s" % (oneLiner)))) # tback('newCmd', e) return except Exception as e: cmd.fail('text=%s' % (qstr('completely unexpected exception when processing a new command: %s' % (e)))) try: tback('newCmdFail', e) except: pass
def getStatus(self, cmd): error = self.getError(cmd=cmd) shutter = self.getShutter(cmd=cmd) grating = self.getGrating(cmd=cmd) outport = int(self.getOutport(cmd=cmd)) wavelength = float(self.getWave(cmd=cmd)) gen = cmd.inform if error == 'OK' else cmd.warn gen('monoerror=%s' % qstr(error)) gen('monograting=%s' % grating) gen('monochromator=%s,%d,%.3f' % (shutter, outport, wavelength))
def _status(self, cmd, keys): """ Actually generate the keywords for the passed in keys. """ for k, v in list(keys.items()): k = k.replace('.', '_') try: float(v) except: v = qstr(v) cmd.inform('%s=%s' % (k,v))
def setRaw(self, cmd): """ Send a direct control command to the PCM's gauge controller. """ ctrlr = cmd.cmd.name parts = cmd.cmd.keywords['setRaw'].values[0] cmdCode, cmdValue = parts cmd.diag('text="code=%r, value=%r"' % (cmdCode, cmdValue)) ret = self.actor.controllers[ctrlr].gaugeRawSet(cmdCode, cmdValue, cmd=cmd) cmd.finish('text=%s' % (qstr("returned %r" % ret)))
def passAlong(self, cmd): """ Pass a command along to another actor. """ actor = cmd.cmd.keywords["actor"].values[0] cmdString = cmd.cmd.keywords["cmd"].values[0] cmdVar = self.actor.cmdr.call(actor=actor, cmdStr=cmdString, forUserCmd=cmd, timeLim=30.0) if cmdVar.didFail: cmd.fail('text=%s' % (qstr('Failed to pass %s along to %s' % (cmdStr, actor)))) else: cmd.finish()
def handle_bad_exception(actor, e, threadName, msg): """ For each thread's "global" unexpected exception handler. Send error, dump stacktrace, try to reply with a failure. """ errMsg = qstr("Unexpected exception %s: %s, in sop %s thread" % (type(e).__name__, e, threadName)) actor.bcast.error('text=%s' % errMsg) tback(errMsg, e) try: msg.replyQueue.put(Msg.REPLY, cmd=msg.cmd, success=False) except Exception, e: pass
def passAlong(self, cmd): """ Pass a command along to another actor. """ actor = cmd.cmd.keywords["actor"].values[0] cmdString = cmd.cmd.keywords["cmd"].values[0] cmdVar = self.actor.cmdr.call(actor=actor, cmdStr=cmdString, timeLim=30.0) if cmdVar.didFail: cmd.fail('text=%s' % (qstr('Failed to pass %s along to %s' % (cmdStr, actor)))) else: cmd.finish()
def do_expose(cmd, actorState, expTime, dither, expType, comment, nreads=None): """Take an exposure, moving the dither position if requested (not None).""" # may not specify nreads and expTime, fail if this is the case if expTime is not None and nreads is not None: cmd.error("text=%s" % qstr("May not specify expTime AND nreads!")) return False if dither != None: cmdVar = do_dither(cmd, actorState, dither) if cmdVar.didFail: cmd.error('text=%s' % qstr("Failed to move APOGEE dither to %s position." % (dither))) return False if nreads is not None: expFlavor = "nreads=%i" % nreads # read is 10.8 seconds, round up and add overhead timeLim = 11 * nreads + 15.0 else: expFlavor = "time=%0.1f" % expTime timeLim = expTime + 15.0 # seconds comment = "comment=%s" % qstr(comment) if comment else "" exposeCmdStr = "expose %s object=%s %s" % (expFlavor, expType, comment) cmdVar = actorState.actor.cmdr.call(actor="apogee", forUserCmd=cmd, cmdStr=exposeCmdStr, keyVars=[], timeLim=timeLim) success = not cmdVar.didFail if not success: cmd.error('text="failed to start %s exposure"' % (expType)) else: cmd.inform('text="done with %s exposure"' % (expType)) return success
def do_lamp(self, cmd, action, replyQueue, noWait=False, delay=None): """ Perform action on this lamp (on or off). * noWait: don't wait for a response from the mcp and just assume it succeeded. Useful if you want to quickly change the lamp state, without worrying about if something timed-out inbetween. * delay: wait that long before claiming success. Use this if the lamp takes a while to be fully "on". """ if self.lampName in ignore_lamps: cmd.diag('text="ignoring %s.%s"' % (action, self.lampName)) replyQueue.put(Msg.REPLY, cmd=cmd, success=True) return # seconds timeLim = 0.1 if noWait else 30.0 cmdVar = self.actorState.actor.cmdr.call(actor="mcp", forUserCmd=cmd, cmdStr=("%s.%s" % (self.name, action)), timeLim=timeLim) if noWait: cmd.warn('text="Not waiting for response from: %s %s"' % (self.lampName, action)) replyQueue.put(Msg.LAMP_COMPLETE, cmd=cmd, success=True) elif cmdVar.didFail: bypassName = "lamp_%s" % (self.name) bypassed = myGlobals.bypass.get(name=bypassName) cmd.error('text=%s' % qstr("Failed to turn %s lamps %s (bypass(%s) = %s)" % (self.lampName, action, bypassName, bypassed))) if bypassed: cmd.warn('text="Ignoring failure on %s lamps"' % (self.lampName)) replyQueue.put(Msg.LAMP_COMPLETE, cmd=cmd, success=True) else: replyQueue.put(Msg.LAMP_COMPLETE, cmd=cmd, success=False) elif delay is not None: if delay > 0: cmd.inform('text="Waiting %gs for %s lamps to warm up"' % (delay, self.lampName)) endTime = time.time() + delay self.queue.put(Msg.WAIT_UNTIL, cmd=cmd, replyQueue=replyQueue, endTime=endTime) else: replyQueue.put(Msg.LAMP_COMPLETE, cmd=cmd, success=True)
def parseReply(self, cmdStr, reply, cmd=None): cmdType = cmdStr[:1] if cmdType == '?': replyFlag = '=' elif cmdType == '!': replyFlag = '*' replyStart = reply[:5] replyCheck = replyFlag + cmdStr[1:5] if not reply.startswith(replyCheck): cmd.warn('text=%s' % qstr('reply to command %r is the unexpected %r (vs %r)' % (cmdStr, replyStart, replyCheck))) return reply[5:].strip().split(';')
def checkFailure(cmd, replyQueue, cmdVar, failmsg, finish=True): """ Test whether cmdVar has failed, and if so issue failmsg as a 'warn' level text. Returns True if cmdVar failed, False if not. Send a success=True REPLY, if finish is True and we cmdVar didn't fail. Always send success=False if it did fail. """ if cmdVar.didFail: cmd.error('text=%s' % qstr(failmsg)) replyQueue.put(Msg.REPLY, cmd=cmd, success=False) return True else: if finish: replyQueue.put(Msg.REPLY, cmd=cmd, success=True) return False
def doApogeeSkyFlats(self, cmd): """Take a set of APOGEE sky flats, offsetting by 0.01 degree in RA.""" sopState = myGlobals.actorState cmdState = sopState.doApogeeSkyFlats keywords = cmd.cmd.keywords name = 'doApogeeSkyFlats' blocked = self.isSlewingDisabled(cmd) if blocked: cmd.fail('text=%s' % (qstr('will not take APOGEE sky flats: %s' % (blocked)))) return if "stop" in cmd.cmd.keywords or 'abort' in cmd.cmd.keywords: self.stop_cmd(cmd, cmdState, sopState, name) return if self.modifiable(cmd, cmdState): if "ditherPairs" in keywords: cmdState.set('ditherPairs', int(keywords["ditherPairs"].values[0])) if "expTime" in keywords: cmdState.set('expTime', int(keywords["expTime"].values[0])) self.status(cmd, threads=False, finish=True, oneCommand=name) return cmdState.reinitialize(cmd) expTime = float( keywords["expTime"].values[0]) if "expTime" in keywords else None cmdState.set('expTime', expTime) ditherPairs = int(keywords["ditherPairs"].values[0] ) if "ditherPairs" in keywords else None cmdState.set('ditherPairs', ditherPairs) if cmdState.ditherPairs == 0: cmd.fail('text="You must take at least one exposure"') return cmdState.setCommandState('running') sopState.queues[sopActor.MASTER].put(Msg.DO_APOGEE_SKY_FLATS, cmd, replyQueue=self.replyQueue, actorState=sopState, cmdState=cmdState)
def parseReply(self, cmdStr, reply, cmd=None): cmdType = cmdStr[:1] if cmdType == b'?': replyFlag = b'=' elif cmdType == b'!': replyFlag = b'*' replyStart = reply[:5] replyCheck = replyFlag + cmdStr[1:5] if not reply.startswith(replyCheck): cmd.warn('text=%s' % qstr('reply to command %r is the unexpected %r (vs %r)' % (cmdStr, replyStart, replyCheck))) return reply[5:].strip().split(b';')
def _reloadConfiguration(self, cmd=None): logging.info("reading config file %s", self.configFile) try: newConfig = configparser.ConfigParser() newConfig.read(self.configFile) except Exception as e: if cmd: cmd.fail('text=%s' % (qstr("failed to read the configuration file, old config untouched: %s" % (e)))) raise self.config = newConfig self.configureLogs() try: # Call optional user hook. self.reloadConfiguration(cmd) except: pass
def parseReply(self, cmdStr, reply, cmd=None): if not isinstance(cmdStr, str): cmdStr = cmdStr.decode('latin-1') cmdType = cmdStr[0] if cmdType == '?': replyFlag = '=' elif cmdType == '!': replyFlag = '*' replyStart = reply[:5] replyCheck = replyFlag + cmdStr[1:5] if not reply.startswith(replyCheck): cmd.warn('text=%s' % qstr('reply to command %r is the unexpected %r (vs %r)' % (cmdStr, replyStart, replyCheck))) replyStr = reply[5:].strip().split(';') return replyStr
def _getInstHeader(self, cmd): """ Gather FITS cards from all actors we are interested in. """ cmd.debug('text="fetching MHS cards..."') cards = fitsUtils.gatherHeaderCards(cmd, self.actor, shortNames=True) cmd.debug('text="fetched %d MHS cards..."' % (len(cards))) # Until we convert to fitsio, convert cards to pyfits pycards = [] for c in cards: if isinstance(c, str): pcard = 'COMMENT', c else: value = c['value'] if value is None: value = pyfits.Undefined() pcard = c['name'], value, c.get('comment', '') pycards.append(pcard) cmd.debug('text=%s' % (qstr("fetched card: %s" % (str(pcard))))) return pycards
def getOneResponse(self, sock=None, timeout=None, cmd=None): """ Return the next available complete line. Fetch new input if necessary. Args ---- sock : socket Uses self.sock if not set. timeout : float Uses self.timeout if not set. Returns ------- str or None : a single line of response text, with EOL character(s) stripped. """ while self.buffer.find(self.EOL) == -1: try: more = self.getOutput(sock=sock, timeout=timeout, cmd=cmd) except IOError: return '' msg = '%s added: %r' % (self.name, more) self.logger.debug(msg) if cmd: cmd.diag('text=%s' % (qstr(msg))) self.buffer += more if more == b'': return '' eolAt = self.buffer.find(self.EOL) ret = self.buffer[:eolAt] self.buffer = self.buffer[eolAt+len(self.EOL):] return ret.decode('latin-1')
def rawCommand(self, cmd): """send a raw command to the biasha board""" cmdKeys = cmd.cmd.keywords cmdStr = cmdKeys['raw'].values[0] ret = self.controller.sendOneCommand(cmdStr, cmd=cmd) cmd.finish('text=%s' % (qstr('returned: %s' % (ret))))
def strTraceback(self, e): oneLiner = self.cmdTraceback(e) return qstr("command failed: %s" % oneLiner)
def sendVersionKey(self, cmd): """ Generate the version keyword in response to cmd. """ version = self.versionString(cmd) cmd.inform('version=%s' % (qstr(version)))
def shutdown(self, why="cuz"): """ Called from above when we want to drop the connection. """ self.brains.bcast.respond('text=%s' % (qstr("shutting connection %d down" % (self.connID)))) actorLogger.info("CommandLink.shutdown because %s", why) self.transport.loseConnection()
def readout(self, imtype=None, expTime=None, darkTime=None, visit=None, comment='', doFeeCards=True, doModes=True, nrows=None, ncols=None, cmd=None, doRun=True): if imtype is not None: self.imtype = imtype else: imtype = self.imtype if expTime is not None: self.expTime = expTime else: expTime = self.expTime if comment is not None: self.comment = comment else: comment = self.comment # In operations, we are always told what our visit is. If we # are not told, use an internally tracked file counter. Since we # also need to run the ccd readout code outside of the actor, # that is maintained by the ccd object. if visit is None: visit = self.ccd.fileMgr.consumeNextSeqno() # If we are not told what our dark time is, guess that the exposure was not # paused. if darkTime is None: if self.expTime == 0: darkTime = 0.0 else: darkTime = self.expTime + 2*0.38 self.darkTime = darkTime if cmd is None: cmd = self.cmd def rowCB(line, image, errorMsg="OK", cmd=cmd, **kwargs): imageHeight = image.shape[0] everyNRows = 250 if (line % everyNRows != 0) and (line < imageHeight-1): return cmd.inform('readRows=%d,%d' % (line, imageHeight)) if self.exposureState != 'integrating': cmd.warn('text="reading out detector in odd state: %s"' % (str(self))) if not hasattr(self, 'headerCards'): self.grabHeaderKeys(cmd) self._setExposureState('reading', cmd=cmd) if doRun: im, _ = ccdFuncs.readout(self.imtype, expTime=self.expTime, darkTime=self.darkTime, ccd=self.ccd, feeControl=self.fee, nrows=nrows, ncols=ncols, doFeeCards=False, doModes=doModes, comment=self.comment, doSave=False, rowStatsFunc=rowCB) filepath = self.makeFilePath(visit, cmd) daqCards = ccdFuncs.fetchCards(self.imtype, self.fee, expTime=self.expTime, darkTime=self.darkTime) self.writeImageFile(im, filepath, visit, addCards=daqCards, cmd=cmd) else: im = None filepath = "/no/such/dir/nosuchfile.fits" for c in self.headerCards: cmd.inform('text="header card: %s"' % (str(c))) dirname, filename = os.path.split(filepath) rootDir, dateDir = os.path.split(dirname) self._setExposureState('idle', cmd=cmd) cmd.inform('filepath=%s,%s,%s' % (qstr(rootDir), qstr(dateDir), qstr(filename))) return im, filepath
def inform(self, cmd, didFail, returnStr): cmd.inform('subCommand=%i,%i,%s' % (self.id, didFail, qstr(returnStr))) self.finished = True
def getRaw(self, cmd): """ Send a direct query command to the PCM's gauge controller. """ cmdCode = cmd.cmd.keywords['getRaw'].values[0] ret = self.actor.controllers['PCM'].gaugeRawQuery(cmdCode, cmd=cmd) cmd.finish('text=%s' % (qstr("returned %r" % ret)))
def testloop(self, cmd): """ Run the expose-move loop a few times. For development. """ cnt = cmd.cmd.keywords["cnt"].values[0] expTime = cmd.cmd.keywords["expTime"].values[0] \ if "expTime" in cmd.cmd.keywords \ else 0.0 self.goHomeAll(cmd) self.queryMPAtable(cmd) self._convertF3CtoMCS(cmd) cmd.inform('text="loop = "%i'%(cnt)) for i in range(cnt): # cmd.inform('text="loop = "%i'%(i)) cmdString = "centroidOnDummy expTime=%0.1f" % (expTime) cmdVar = self.actor.cmdr.call(actor='mcs', cmdStr=cmdString, forUserCmd=cmd, timeLim=expTime) if cmdVar.didFail: cmd.fail('text=%s' % (qstr('Failed to expose with %s' % (cmdString)))) rawCentroids = self.actor.models['mcs'].keyVarDict['centroidsChunk'][0] # expTime = cmd.cmd.keywords["expTime"].values[0] \ # if "expTime" in cmd.cmd.keywords \ # else 0.0 # # # times = numpy.zeros((cnt, 4), dtype='f8') # # targetPos = self.targetPositions("some field ID") # for i in range(cnt): # times[i,0] = time.time() # # # Fetch measured centroid from the camera actor # cmdString = "centroid expTime=%0.1f" % (expTime) # cmdVar = self.actor.cmdr.call(actor='mcs', cmdStr=cmdString, # forUserCmd=cmd, timeLim=expTime+5.0) # if cmdVar.didFail: # cmd.fail('text=%s' % (qstr('Failed to expose with %s' % (cmdString)))) # # return # # Encoding will be encapsulated. # rawCentroids = self.actor.models['mcs'].keyVarDict['centroidsChunk'][0] # centroids = numpy.fromstring(base64.b64decode(rawCentroids), dtype='f4').reshape(2400,2) # times[i,1] = time.time() # # # Command the actuators to move. # cmdString = 'moveTo chunk=%s' % (base64.b64encode(targetPos.tostring())) # cmdVar = self.actor.cmdr.call(actor='mps', cmdStr=cmdString, # forUserCmd=cmd, timeLim=5.0) # if cmdVar.didFail: # cmd.fail('text=%s' % (qstr('Failed to move with %s' % (cmdString)))) # return # times[i,2] = time.time() # # cmdVar = self.actor.cmdr.call(actor='mps', cmdStr="ping", # forUserCmd=cmd, timeLim=5.0) # if cmdVar.didFail: # cmd.fail('text=%s' % (qstr('Failed to ping'))) # return # times[i,3] = time.time() # # for i, itimes in enumerate(times): # cmd.inform('text="dt[%d]=%0.4f, %0.4f, %0.4f"' % (i+1, # itimes[1]-itimes[0], # itimes[2]-itimes[1], # itimes[3]-itimes[2], # )) # cmd.inform('text="dt[mean]=%0.4f, %0.4f, %0.4f"' % ((times[:,1]-times[:,0]).sum()/cnt, # (times[:,2]-times[:,1]).sum()/cnt, # (times[:,3]-times[:,2]).sum()/cnt)) # cmd.inform('text="dt[max]=%0.4f, %0.4f, %0.4f"' % ((times[:,1]-times[:,0]).max(), # (times[:,2]-times[:,1]).max(), # (times[:,3]-times[:,2]).max())) cmd.finish("text='Testing loop finished.'")
def standbyOff(self, cmd): """ Put the pump back into normal (full-speed) mode. """ ret = self.actor.controllers['turbo'].stopStandby(cmd=cmd) cmd.finish('text=%r' % (qstr(ret)))