def wait_for_move(self): """ Wait for telescope to stop moving. """ if not self.enabled: azcam.AzcamWarning("telescope not enabled") return # loop for up to ~20 seconds for i in range(200): reply = self.get_keyword("MOTION") try: motion = int(reply[0]) except: raise azcam.AzcamError("bad MOTION status keyword: %s" % reply[1]) if not motion: return time.sleep(0.1) # stop the telescope command = "CANCEL" reply = self.Tserver.command(command) raise azcam.AzcamError("stopped motion flag not detected")
def set_image_roi(roi: list = []) -> None: """ Set the global image region of interest "db.imageroi". If roi is not specified, use display ROI. Args: roi: ROI list or [] """ # set directly with given value if roi != []: azcam.db.imageroi = roi return # use display ROIs roi = [] try: reply = azcam.db.tools["display"].get_rois(-1, "image") except AttributeError: raise azcam.AzcamError("cannot set ROI - no display found") if not reply: raise azcam.AzcamError("could not get display ROI") azcam.db.imageroi = reply return
def command(self, Command): """ Command interface for Mont4k instrument. Use instead of instrument server Command(). """ with self.lock: self.iserver.open() if self.iserver.opened: self.iserver.recv1() # read string and ignore for now self.iserver.send(Command) reply = self.iserver.recv1() self.iserver.send("CLIENTDONE") self.iserver.recv1() # read string and ignore for now time.sleep(0.10) # ! self.iserver.close() else: raise azcam.AzcamError("could not open Mont4k instrument") # check for error, valid replies starts with 'OK: ' and errors with '?: ' if reply.startswith("OK: "): reply = reply[4:] reply = reply.rstrip() return reply else: raise azcam.AzcamError(reply)
def command(self, command: str, terminator: str = "\n") -> list: """ Sends a command to the server and receives a reply back. Args: command: command string to send terminator: termination string to append the command Returns: tokenized list of the server's reply. """ with self.lock: if not self.open(): raise azcam.AzcamError( "could not open connection to tempcon server", error_code=2) self.send(command, terminator) reply = self.recv(-1, "\n") self.last_response = reply if command not in ["exposure.get_status"]: reply = shlex.split(reply) else: reply = [reply.strip()] return reply
def send_image(self, localfile=None, remotefile=None): """ Send image to remote image server. """ if localfile is None: localfile = f"{azcam.db.tools['exposure'].temp_image_file}.{azcam.db.tools['exposure'].get_extname(self.filetype)}" if remotefile is None: remotefile = f"{self.remote_imageserver_filename}.{azcam.db.tools['exposure'].get_extname(self.filetype)}" self.overwrite = azcam.db.tools["exposure"].overwrite self.test_image = azcam.db.tools["exposure"].test_image self.display_image = azcam.db.tools["exposure"].display_image self.filetype = azcam.db.tools["exposure"].filetype self.size_x = azcam.db.tools["exposure"].size_x self.size_y = azcam.db.tools["exposure"].size_y if self.remote_imageserver_type == "azcam": self.azcam_imageserver(localfile, remotefile) elif self.remote_imageserver_type == "lbtguider": self.lbtguider_imageserver(localfile, remotefile) elif self.remote_imageserver_type == "dataserver": self.dataserver(localfile, remotefile) elif self.remote_imageserver_type == "ccdacq": self.ccdacq_imageserver(localfile, remotefile) else: raise azcam.AzcamError("Unknown remote image server type")
def get_image_roi() -> list: """ Get the data and noise regions of interest in image image coordinates. Check for ROI's in the following order: - azcam.db.imageroi if defined - display.roi if defined Returns: list of ROIs """ # database roi if azcam.db.get("imageroi"): if azcam.db.imageroi != []: return azcam.db.imageroi # display.roi roi = [] try: reply = azcam.db.tools["display"].get_rois(0, "image") except AttributeError: raise azcam.AzcamError("cannot get ROI - display not found") roi.append(reply) reply = azcam.db.tools["display"].get_rois(1, "image") if reply: roi.append(reply) else: roi.append(roi[0]) return roi
def get_data(self, roi_number=0): """ Returns [Status,[pixel1,pixel2,...]] in display ROI. :param int roi_number: Number of ROI to get data :return list: [status,[pixel1,pixel2,...]] for ROI """ self.read_rois() numrois = len(self.detector_roi) if numrois == 0: raise azcam.AzcamError("No ROI defined") elif roi_number > numrois: raise azcam.AzcamError("Invalid ROI number") roi = self.detector_roi[roi_number] try: firstcol = roi[0] lastcol = roi[1] firstrow = roi[2] lastrow = roi[3] width = lastcol - firstcol + 1 length = lastrow - firstrow + 1 xcenter = firstcol + width / 2.0 ycenter = firstrow + length / 2.0 cmd = "data detector %d %d %d %d yes" % ( xcenter, ycenter, width, length, ) # may not be right datads9 = self.xpaget(cmd) data = [] datads9 = datads9.split("\n") for d in datads9: if len(d) == 0: continue data.append(float(d)) return data except Exception as e: azcam.log(f"ds9 error: {e}") return []
def ccdacq_imageserver(self, localfile, remotefile=None): """ Send raw image data to cccdacq (ICE) application. """ # open socket to remote image server ccdacqsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) ccdacqsocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 0) try: ccdacqsocket.connect( (self.remote_imageserver_host, self.remote_imageserver_port)) except Exception as message: ccdacqsocket.close() raise azcam.AzcamError( f"ccdacq image server not opened: {message}") # send header s1 = "%d %d\r\n" % (self.size_x, self.size_y) if ccdacqsocket.send(str.encode(s1)) != len(s1): raise azcam.AzcamError(f"socket send error header1") s1 = "NoFilename NoImageType\r\n" if ccdacqsocket.send(str.encode(s1)) != len(s1): raise azcam.AzcamError(f"socket send error header2") # send file data buff = azcam.db.tools["exposure"].image.data[0] if ccdacqsocket.send(str.encode(buff)) != len(buff): raise azcam.AzcamError( f"Could not send all image data to ccdacq server") # wait before closing try: reply = ccdacqsocket.recv(1) except Exception: pass # close socket ccdacqsocket.close() return
def recv(self, length: int = -1, terminator: str = "\n") -> str: """ Receives a reply from a server. Terminates the socket read when length bytes are received or when the terminator is received. Args: length: number of bytes to receive. -1 means receive through terminator. terminator: terminator string. """ # receive Length bytes if length != -1: reply = self.socket.recv(length).decode() return reply # read socket until Terminator found msg = "" loop = 0 while True: try: msg += self.socket.recv(1024).decode() except ConnectionAbortedError: raise azcam.AzcamError("Connection aborted") if len(msg) == 0: pass elif msg[-1] == terminator: # found terminator at end break # check for infinite loop loop += 1 if loop > 1024: raise azcam.AzcamError("Socket recv loop max retry exceeded") time.sleep(0.005) if len(msg) < 2: raise azcam.AzcamError("Invalid command response received") if msg[-2] == "r": reply = msg[:-2] # remove CR/LF else: reply = msg[:-1] # remove LF return reply
def make_file_folder(subfolder: str, increment: bool = True, use_number: bool = False) -> tuple: """ Creates a new subfolder in the current FileFolder. Args: subfolder: subfolder name to create increment: - if True, subfolder name may be incremented to create a unique name (e.g. ptc1, ptc2, ptc3...) use_number: - if True, starts with '1' after Subfolder name (e.g. report1 not report) Returns: tuple (currentfolder,newfolder) """ currentfolder = azcam.utils.curdir() sf = subfolder + "1" if use_number else subfolder try: newfolder = os.path.join(currentfolder, sf) # new subfolder os.mkdir(newfolder) newfolder = azcam.utils.fix_path(newfolder) except Exception: if not increment: raise azcam.AzcamError("could not make new subfolder") else: for i in range(1, 1000): newfolder = os.path.join( currentfolder, subfolder + str(i) ) # try a new subfolder name try: os.mkdir(newfolder) newfolder = azcam.utils.fix_path(newfolder) break except Exception: # error OK continue if i == 999: raise azcam.AzcamError("could not make subfolder") newfolder = azcam.utils.fix_path(newfolder) return (currentfolder, newfolder)
def lbtguider_imageserver(self, localfile, remotefile=None): """ Send image to an LBT guider image server. """ # open image file on disk with open(localfile, "rb") as gfile: if not gfile: raise azcam.AzcamError(f"Could not open local image file") lSize = os.path.getsize(localfile) buff = gfile.read() # open socket to LBT image server guidesocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) guidesocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 0) try: guidesocket.connect( (self.remote_imageserver_host, self.remote_imageserver_port)) except Exception as message: guidesocket.close() raise azcam.AzcamError( f"LBT guider ImageServer not opened: {message}") # send filesize in bytes, \r\n terminated sockBuf = "%d\r\n" % lSize if guidesocket.send(str.encode(sockBuf)) != len(sockBuf): raise azcam.AzcamError(f"GuideSocket send error") # send file data if guidesocket.send(str.encode(buff)) != len(buff): raise azcam.AzcamError( f"Could not send all image file data to LBT ImageServer") # close socket guidesocket.close() return
def find_file_in_sequence(file_root: str, file_number: int = 1) -> tuple: """ Returns the Nth file in an image sequence where N is file_number (-1 for first file). Args: file_root: image file root name. file_number: image file number in sequence. Returns: tuple (filename,sequencenumber). """ currentfolder = azcam.utils.curdir() for _, _, files in os.walk(currentfolder): break for f in files: if f.startswith(file_root): break try: if not f.startswith(file_root): raise azcam.AzcamError("image sequence not found") except Exception: raise azcam.AzcamError("image sequence not found") firstfile = azcam.utils.fix_path(os.path.join(currentfolder, f)) firstsequencenumber = firstfile[-9:-5] firstnum = firstsequencenumber firstsequencenumber = int(firstsequencenumber) sequencenumber = firstsequencenumber + file_number - 1 newnum = "%04d" % sequencenumber filename = firstfile.replace(firstnum, newnum) return (filename, sequencenumber)
def command(self, command): """ Send a command to a server process using the 'server' object in the database. This command traps all errors and returns exceptions and as error string. Returns None or a string. """ # get tokenized reply - check for comm error try: reply = self.remserver.command(command) except azcam.AzcamError as e: if e.error_code == 2: raise # raise azcam.AzcamError("could not connect to server") else: raise if command in ["exposure.get_status"]: return reply[0][3:] # status for socket communications is OK or ERROR if reply[0] == "ERROR": raise azcam.AzcamError(f"command error: {' '.join(reply[1:])}") elif reply[0] == "OK": if len(reply) == 1: return None elif len(reply) == 2: return reply[1] else: return reply[1:] else: raise azcam.AzcamError( f"invalid server response: { ' '.join(reply)}") return # can't get here
def set_cursor_mode(self, mode="point", clear_rois=0) -> None: """ Set the display cursor mode. :param str mode: Cursor mode ("point", "crosshair") :param bool clear_rois: True to clear all ROIs """ if mode == "point": self._set_pointer(clear_rois) elif mode == "crosshair": self._set_crosshair(clear_rois) else: raise azcam.AzcamError("Invalid cursor mode") return
def _filter_busy(self): """ Return True if filter wheel is moving or False if not. """ for i in range(10): reply = self.command("SHOW FWBUSY") if reply.lstrip().startswith("1"): return True elif reply.lstrip().startswith("0"): return False else: continue time.sleep(0.1) raise azcam.AzcamError("filter wheel BUSY timeout")
def get_keyword(self, keyword): """ Read an instrument keyword value. This command will read hardware to obtain the keyword value. """ if keyword == "FILTER": reply = self.get_filter(0) else: raise azcam.AzcamError("keyword not defined") # store value in Header self.set_keyword(keyword, reply) reply, t = self.header.convert_type(reply, self.header.typestrings[keyword]) return [reply, self.header.comments[keyword], t]
def wait_filter_busy(self): """ Waits for filter wheel to stop moving. """ for i in range(50): reply = self.command("SHOW FWBUSY") if reply.lstrip().startswith("1"): # busy time.sleep(0.1) continue elif reply.lstrip().startswith("0"): # not busy return else: time.sleep(0.1) # other continue raise azcam.AzcamError("filter wheel BUSY timeout")
def plot_image( azimage: object, scale_type: str = "sdev", scale_factor: float = 20.0, cmap: str = "gray", ) -> None: """ Plot an Azcam image buffer nicely. Args: scale_type: one of (sdev, minmax, scaled, absolute). scale_factor: scaling factor for 8-bit conversion. cmap: color map name. """ if not azimage.assembled: azimage.assemble(1) if scale_type == "sdev": s = azimage.buffer.std() m = azimage.buffer.mean() z1 = m - scale_factor * s z2 = m + scale_factor * s elif scale_type == "minmax": z1 = azimage.buffer.min() z2 = azimage.buffer.max() elif scale_type == "scaled": m = azimage.buffer.mean() z1 = m / scale_factor z2 = m * scale_factor elif scale_type == "absolute": m = azimage.buffer.mean() z1 = m - scale_factor z2 = m + scale_factor else: raise azcam.AzcamError("unrecognized scale_type") plt.imshow(azimage.buffer, cmap=cmap, vmin=z1, vmax=z2, origin="lower") return
def get_keyword(self, keyword: str) -> List: """ Read a temperature keyword value and returns it as [value, comment, type string] Args: keyword: name of keyword Returns: list of [keyword, comment, type] """ reply = self.get_temperatures() if keyword == "CAMTEMP": temp = reply[0] elif keyword == "DEWTEMP": temp = reply[1] elif keyword in self.get_keywords(): value = self.header.values[keyword] temp = value else: raise azcam.AzcamError(f"invalid keyword: {keyword}") # store temperature values in header if keyword == "CAMTEMP": temp = float(f"{temp:.03f}") self.header.set_keyword("CAMTEMP", temp, "Camera temperature in C", "float") self.header.set_keyword("CCDTEMP1", temp, "Camera temperature in C", "float") elif keyword == "DEWTEMP": temp = float(f"{temp:.03f}") self.header.set_keyword("DEWTEMP", temp, "Dewar temperature in C", "float") self.header.set_keyword("CCDTEMP2", temp, "Dewar temperature in C", "float") t = self.header.typestrings[keyword] return [temp, self.header.comments[keyword], t]
def check_keyboard(wait: bool = False) -> str: """ Checks keyboard for a key press. For Windows OS only. Args: wait: True to wait until a key is pressed Returns: key which was pressed or empty string. """ # TODO: map sequences like 'F1' if os.name != "nt": raise azcam.AzcamError("check_keyboard not supported on this OS") loop = 1 key = "" while loop: if msvcrt.kbhit(): key = msvcrt.getch() try: key = key.decode() # since the key is byte type, maybe escape sequence so check for more # if msvcrt.kbhit(): # key1 = msvcrt.getch() # # key = key + key1.decode() except UnicodeDecodeError: pass break if not wait: loop = 0 return key
def combine( file_list: list = [], out_filename: str = "combined.fits", combination_type: str = "median", overscan_correct: int = 1, fit_order=3, datatype="float32", ) -> None: """ Make a combination iamge from a list of FITS filenames. Args: file_list: list of filenames to combine. out_filename: output filename. combination_type: combination type, "median", "sum", or "mean". overscan_correct: line fit order if >0 for overscan correction before combination. """ numfiles = len(file_list) if numfiles < 2: raise azcam.AzcamError("two or more images are required") header = [] # header for output file dataset = [] # combined data for fnum, f in enumerate(file_list): filename = azcam.utils.make_image_filename(f) # overscan correct if overscan_correct > 0: colbias(filename, fit_order) dataarray = [] # each dataset by channel for each file # open each image and get data with pyfits.open(filename) as im: numexts, firstext, lastext = get_extensions(filename) # setup based on first image if fnum == 0: if numexts > 0: MEF = 1 header.append(im[0].header) for i in range(firstext, lastext): header.append(im[i].header) dataarray.append(im[i].data) else: MEF = 0 header = im[0].header # save for output file dataarray = im[0].data else: if numexts > 0: for i in range(numexts): dataarray.append(im[i + 1].data) else: # dataarray=dataarray+im.data dataarray = im[0].data dataset.append(dataarray) if MEF: data3d = [] for chan in range(numexts): data3d.append(numpy.array([x[chan] for x in dataset])) data_combined = [] for chan in range(numexts): if combination_type == "median": data_combined.append(numpy.median(data3d[chan], axis=0)) elif combination_type == "sum": data_combined.append(numpy.sum(data3d[chan], axis=0).astype(datatype)) elif combination_type == "mean": data_combined.append(numpy.mean(data3d[chan], axis=0).astype(datatype)) else: data3d = numpy.array([x for x in dataset]) if combination_type == "median": data_combined = numpy.median(data3d, axis=0).astype(datatype) elif combination_type == "sum": data_combined = numpy.sum(data3d, axis=0).astype(datatype) elif combination_type == "mean": data_combined = numpy.mean(data3d, axis=0).astype(datatype) if MEF: newdata = [] for i in range(numexts): newdata.append(data_combined[i]) else: newdata = data_combined # set output datatype if datatype != "float64": newdata = [x.astype(datatype) for x in newdata] # write result if MEF: phdu = pyfits.PrimaryHDU(None, header[0]) hdulist = pyfits.HDUList() hdulist.append(phdu) for i in range(firstext, lastext): hdu = pyfits.PrimaryHDU(newdata[i - 1], header[i]) hdulist.append(hdu) with warnings.catch_warnings(): # surpress warning warnings.simplefilter("ignore") hdulist.writeto(out_filename, overwrite=1) hdulist.close() else: im1 = pyfits.PrimaryHDU(newdata, header) im1.writeto(out_filename, overwrite=1) # update header add_history( out_filename, "COMBINED Data was %s combined from %d images" % (combination_type, numfiles), 0, ) return
def arith( filename1: str, operator: str, filename2: str, filename3: str = "", datatype: str = "uint16", ) -> None: """ Simple image arithmetic of FITS files. Args: filename1: image filename. operator: '+','-','/', or '*'. filename2: may be an image filename or a constant. filename3: optional, must be an image filename. If not specified, result goes into filename1. datatype: valid datatype string for resultant data type. """ MakeU16 = 1 if datatype == "uint16" else 0 header = [] # header for output file # open Image1 filename1 = azcam.utils.make_image_filename(filename1) numext1, fext, lext = get_extensions(filename1) im1 = pyfits.open(filename1, lazy_load_hdus=False) # this is an hdulist if numext1 > 0: MEF = 1 header.append(im1[0].header) # save for output file data1 = [] # first data index is 0 for i in range(1, lext): header.append(im1[i].header) # save for output if _is_image_extension(im1, i): # only use image data data1.append(im1[i].data) else: MEF = 0 header = im1[0].header # save for output file data1 = im1[0].data # make float - new if MEF: for i in range(len(data1)): if _is_image_extension(im1, i): data1[i] = data1[i].astype("float32") else: data1 = data1.astype("float32") # check if filename2 is actually a number and not a filename if isinstance(filename2, (int, float)): data2 = filename2 SCALAR = 1 # open Image2 and get data else: SCALAR = 0 filename2 = azcam.utils.make_image_filename(filename2) numext2, fext, lext = get_extensions(filename2) with pyfits.open(filename2, lazy_load_hdus=False) as im2: if numext1 != numext2: im2.close() raise azcam.AzcamError("unequal FITS image extensions") if MEF: data2 = [] for i in range(1, lext): if _is_image_extension(im2, i): data2.append(im2[i].data.astype("float32")) else: data2 = im2[0].data.astype("float32") # operate NewFile = 1 if MEF: data3 = [] if operator == "*": if MEF: for i in range(len(data1)): if SCALAR: data3.append(data1[i] * data2) else: data3.append(data1[i] * data2[i]) else: data3 = data1 * data2 elif operator == "+": if MEF: for i in range(len(data1)): if SCALAR: data3.append(data1[i] + data2) else: data3.append(data1[i] + data2[i]) else: data3 = data1 + data2 elif operator == "-": if MEF: for i in range(len(data1)): if SCALAR: data3.append(data1[i] - data2) else: data3.append(data1[i] - data2[i]) else: data3 = data1 - data2 elif operator == "/": if MEF: for i in range(len(data1)): if SCALAR: data3.append(data1[i] / data2) else: data3.append(data1[i] / data2[i]) else: data3 = data1 / data2 # write result (all data is now float32) if filename3 == "": filename3 = filename1 os.remove(filename3) filename3 = azcam.utils.make_image_filename(filename3) if NewFile: if MEF: phdu = pyfits.PrimaryHDU(None, header[0]) hdulist = pyfits.HDUList() hdulist.append(phdu) for i in range(numext1): if MakeU16: numpy.clip(data3[i], 0, 2 ** 16, data3[i]) # clip values below zero hdu = pyfits.ImageHDU(data3[i].astype("uint16"), header[i + 1]) else: hdu = pyfits.ImageHDU(data3[i], header[i + 1]) hdulist.append(hdu) if len(header) > len(data3): for i in range(len(data3) + 1, len(im1)): hdulist.append(im1[i]) hdulist.writeto(filename3, overwrite=1) im1.close() else: im1.close() im3 = pyfits.PrimaryHDU(data3, header) im3.writeto(filename3, overwrite=1) return
"FitsTemplate_mont4k_rts2.txt") parfile = os.path.join(azcam.db.datafolder, "parameters_mont4k_rts2.ini") RTS2 = 1 cmdport = 2412 azcam.db.servermode = "RTS2" default_tool = "rts2" elif "CSS" in option: template = os.path.join(azcam.db.datafolder, "templates", "FitsTemplate_mont4k_css.txt") parfile = os.path.join(azcam.db.datafolder, "parameters_mont4k_css.ini") CSS = 1 cmdport = 2422 azcam.db.servermode = "CSS" default_tool = "css" else: azcam.AzcamError("invalid menu item") parfile = parfile # **************************************************************** # controller # **************************************************************** controller = ControllerArc() controller.timing_board = "arc22" controller.clock_boards = ["gen3"] controller.video_boards = ["gen2"] controller.utility_board = "gen3" controller.set_boards() controller.utility_file = os.path.join(azcam.db.systemfolder, "dspcode", "dsputility/util3.lod") controller.pci_file = os.path.join(azcam.db.systemfolder, "dspcode", "dsppci", "pci3.lod")
def azcam_imageserver(self, localfile, remotefile=None): """ Send image to azcam image server. """ if remotefile is None: remotefile = self.remote_imageserver_filename # open image file on disk with open(localfile, "rb") as dfile: lSize = os.path.getsize(localfile) buff = dfile.read() # open socket to DataServer dataserver_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) dataserver_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 0) dataserver_socket.settimeout(self.timeout) dataserver_socket.connect( (self.remote_imageserver_host, int(self.remote_imageserver_port))) if self.overwrite or self.test_image: remotefile = "!" + remotefile print(remotefile) # send header # file types: 0 FITS, 1 MEF, 2 binary s1 = "%16d %s %d %d %d %d" % ( lSize, remotefile, self.filetype, self.size_x, self.size_y, self.display_image, ) s1 = "%-256s" % s1 status = dataserver_socket.send(str.encode(s1)) if status != 256: raise azcam.AzcamError( "Could not send image header to remote image server") # get 16 char ASCII header return status from image server # reply = dataserver_socket.recv(16) # if len(reply) != 16: # raise azcam.AzcamError("Did not receive header return status from remote image server") # retstat=int(reply[:1]) # retstat=int(reply) retstat = 0 # check header return status codes if retstat != 0: if retstat == -1: raise azcam.AzcamError("Bad reply from remote image server") elif retstat == -2: raise azcam.AzcamError( "Remote ImageServer not create image filename") elif retstat == -3: # raise azcam.AzcamError( "Folder does not exist on remote machine") else: raise azcam.AzcamError( "Unknown error from remote image server") # send file data reply = dataserver_socket.send(buff) if reply != len(buff): raise azcam.AzcamError( "Did not send entire image file data to remote image server") # get 16 char ASCII final return status from image server reply = dataserver_socket.recv(16).decode() # if len(reply) != 2: # raise AzcamError("did not receive entire return status from remote image server") retstat = int(reply[:1]) # check final return status error codes if retstat != 0: raise azcam.AzcamError( "Bad final return status from remote image server") # close socket # time.sleep() dataserver_socket.close() return
def start_logging(self, logtype="13", host="localhost", port=2404, logfile=None, use_timestamp=1): """ Start the azcam logger. :param logtype: code for loggers to start (1 console, 2 socket, 3 file, codes may be combined as '23') :param host: hostname for logging over socket :param port: socket port number :param logfile: base filename of log file. If not absolute path, will use db.systemfolder. :param use_timestamp: append timestamp to logfile name. """ # remove default logger for customization try: self.logger.remove(0) except Exception: pass # console handler if "1" in logtype: self.logger.add( sys.stdout, colorize=True, filter=self._logfilter, format="{message}", enqueue=True, # backtrace=True, # diagnose=True, ) azcam.log("Logging to console") # socket handler if "2" in logtype: socket_handler = logging.handlers.SocketHandler( "localhost", port, ) self.logger.add(socket_handler) azcam.log(f"Logging to logging server on port {port}") # rotating file handler if "3" in logtype: if logfile is None: if self.logfile is None: raise azcam.AzcamError("no logfile specified") else: self.logfile = logfile if use_timestamp: tt = datetime.datetime.strftime(datetime.datetime.now(), "%d%b%y_%H%M%S") s1, s2 = os.path.splitext(self.logfile) self.logfile = f"{s1}_{tt}{s2}" self.logger.add( self.logfile, format="{time:DD-MMM-YY HH:mm:ss.SSS} | {level} | {message}", rotation="10 MB", retention="1 week", ) azcam.log(f"Logging to file {self.logfile}") return
def dataserver(self, localfile, remotefile): """ Send image to dataserver. """ ImageSendBufferSize = 1024 * 32 # open image file on disk with open(localfile, "rb") as dfile: lSize = os.path.getsize(localfile) buff = dfile.read() # open socket to DataServer dataserver_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) dataserver_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 0) dataserver_socket.settimeout(self.timeout) dataserver_socket.connect( (self.remote_imageserver_host, int(self.remote_imageserver_port))) if self.overwrite or self.test_image: remotefile = "!" + remotefile azcam.log("Sending image to %s as %s" % (self.remote_imageserver_host, remotefile)) # send header # file types: 0 FITS, 1 MEF, 2 binary s1 = "%16d %s %d %d %d %d" % ( lSize, remotefile, self.filetype, self.size_x, self.size_y, self.display_image, ) s1 = "%-256s" % s1 status = dataserver_socket.send(str.encode(s1)) if status != 256: raise azcam.AzcamError( "Could not send image header to remote DataServer") # get 16 char ASCII header return status from image server # reply = dataserver_socket.recv(16) # if len(reply) != 16: # raise azcam.AzcamError("Did not receive header return status from remote image server") # retstat=int(reply[:1]) # retstat=int(reply) retstat = 0 # check header return status codes (updated 14jul11) if retstat != 0: if retstat == 1: # overwrite existing name wihtout flag raise azcam.AzcamError( "Remote image server could not create image filename") elif retstat == 2: # not enough space raise azcam.AzcamError( "Remote image server does not have enough disk space") elif retstat == 3: # raise azcam.AzcamError( "Remote image server reports folder does not exist") else: raise azcam.AzcamError( "Unknown error from remote image server") # send file data, new 10Sep13 numchunks = int(lSize / ImageSendBufferSize) if lSize - (numchunks * ImageSendBufferSize) != 0: remainder = lSize - (numchunks * ImageSendBufferSize) else: remainder = 0 if numchunks == 0: size = remainder else: size = ImageSendBufferSize end = 0 for _ in range(numchunks): start = end end = start + size try: # dataserver_socket.send(str.encode(buff[start:end])) dataserver_socket.send(buff[start:end]) except Exception as message: raise azcam.AzcamError( f"Did not send image file data to remote image server: {message}" ) if remainder > 0: # dataserver_socket.send(str.encode(buff[end:])) dataserver_socket.send(buff[end:]) """ # send file data try: reply=dataserver_socket.send(str.encode(buff)) except Exception as message: raise azcam.AzcamError("Did not send image file data to remote image server: %s" % message) if reply!=len(buff): raise azcam.AzcamError("Did not send entire image file data to remote image server") """ # get 16 char ASCII final return status from image server """ try: reply = dataserver_socket.recv(16) except: raise azcam.AzcamError("Did not receive return status from remote image server") if len(reply) != 2: raise azcam.AzcamError("Did not receive entire return status from remote image server") retstat=int(reply[:1]) # check final return status error codes if retstat != 0: raise azcam.AzcamError("Bad final return status from remote image server") """ # close socket time.sleep(1) # 3 dataserver_socket.close() return
def parse_command_string(command: str): """ Parse a command string into tool and arguments. If command does not start with a dotted object.method token, then assume it is the method of the default_tool. Returns (objid, args, kwargs) objid is a bound method of a class args is a list of strings kwargs is a dict of strings """ # parse command string tokens = azcam.utils.parse(command, 0) cmd = tokens[0] arglist = tokens[1:] args = [] kwargs = {} if len(arglist) == 0: pass else: for token in arglist: if "=" in token: keyname, value = token.split("=") kwargs[keyname] = value else: args.append(token) if "." not in cmd: # get method from db.default_tool if azcam.db.default_tool is None: s = f"command not recognized: {cmd} " raise azcam.AzcamError(s) else: objid = getattr(azcam.db.tools[azcam.db.default_tool], cmd) else: # get method from tool in db.tools objects = cmd.split(".") if objects[0] not in azcam.db.tools: raise azcam.AzcamError(f"remote call not allowed: {objects[0]}", 4) if len(objects) == 1: objid = azcam.db.tools[objects[0]] elif len(objects) == 2: objid = getattr(azcam.db.tools[objects[0]], objects[1]) elif len(objects) == 3: objid = getattr(getattr(azcam.db.tools[objects[0]], objects[1]), objects[2]) elif len(objects) == 4: objid = getattr( getattr(getattr(azcam.db.tools[objects[0]], objects[1]), objects[2]), objects[3], ) else: objid = None # too complicated for now # kwargs = {} # l1 = len(tokens) # if l1 > 1: # args = tokens[1:] # if "=" in args[0]: # # assume all keywords for now # kwargs = {} # for argtoken in args: # keyword, value = argtoken.split("=") # kwargs[keyword] = value # args = [] # else: # args = [] return objid, args, kwargs