def update_headers(self): """ Update all headers, reading current data. """ # set flag that update is in progress self.updating_header = 1 # all headers to be updated must be in azcam.db['headers'] for objectname in azcam.db.headers: if (objectname == "controller" or objectname == "system" or objectname == "exposure" or objectname == "focalplane"): continue try: azcam.db.tools[objectname].update_header( ) # dont crash so all headers get updated except Exception as e: azcam.log(f"could not get {objectname} header: {e}") # update focalplane header which is not in db self.image.focalplane.update_header() # try to update system header last if "system" in azcam.db.headers: try: azcam.db.headers["system"].update_header() except Exception: pass # set flag that update is finished self.updating_header = 0 return
def begin(self, port=-1): """ Start command server. """ if port == -1: port = self.port else: self.port = port server_address = ("", port) # '' better than localhost when no network try: self.server = ThreadedTCPServer(server_address, MyBaseRequestHandler) self.server.RequestHandlerClass.cmdserver = self self.is_running = 1 self.server.serve_forever() # waits here forever except Exception as message: self.is_running = 0 azcam.log( f"ERROR in cmdserver:{repr(message)} Is it already running? Exiting..." ) time.sleep(2) os._exit(1) # Exits here when server is aborted return
def open(self) -> bool: """ Open a socket connection to the server. Creates the socket and makes a connection. Returns: True if socket is already open or if it is opened here. """ # check if socket is already open if self.socket is not None: return True self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 0) try: self.socket.settimeout(0.1) self.socket.connect((self.host, self.port)) self.connected = True except Exception: self.close() self.connected = False return False # check if there is a welcome message self.set_timeout(0.5) try: welcome = self.recv() except socket.timeout: welcome = "" self.set_timeout(0) # initally no timeout if len(welcome) > 0: azcam.log(welcome) return True
def read_header(self): """ Returns telescope header info. returns [Header[]]: Each element Header[i] contains the sublist (keyword, value, comment, and type). Example: Header[2][1] is the value of keyword 2 and Header[2][3] is its type. Type is one of str, int, or float. """ if not self.enabled: azcam.AzcamWarning("telescope not enabled") return header = [] data = self.Tserver.azcam_all() keywords = list(data.keys()) keywords.sort() list1 = [] for key in list(self.Tserver.keywords): try: t = self.header.typestrings[key] v = data[self.Tserver.keywords[key]] c = self.header.comments[key] list1 = [key, v, c, t] header.append(list1) except Exception as message: azcam.log("ERROR", key, message) continue # store value in Header self.header.set_keyword(list1[0], list1[1], list1[2], list1[3]) return header
def flush(self, Cycles=1): """ Flush/clear detector. Cycles is the number of times to flush the detector. """ azcam.log("Flushing") return azcam.db.tools["controller"].flush(int(Cycles))
def finish(self): """ Called when existing connection is closed. """ if azcam.db.tools["cmdserver"].log_connections and azcam.db.tools[ "cmdserver"].verbose: azcam.log(f"Connection closed to {str(self.client_address)}") return socketserver.BaseRequestHandler.finish(self)
def mouse_motion(event): """ Get the x and y pixel coords for plots. :param event: """ if event.inaxes: azcam.log("data coords", event.xdata, event.ydata) return
def setup(self): """ Called when new connection made. """ if azcam.db.tools["cmdserver"].log_connections and azcam.db.tools[ "cmdserver"].verbose: azcam.log( f"Client connection made from {str(self.client_address)}", prefix="cmd> ", ) return socketserver.BaseRequestHandler.setup(self)
def abort(self) -> None: """ Sets the global exposure abort flag and tries to abort a remote server exposure. """ azcam.db.abortflag = 1 # send abort to server, error OK try: azcam.db.tools["server"].command(f"{self.objname}.abort") except Exception as e: azcam.log(f"abort error: {e}") return
def get_regions(self, coordinate_type="image"): """ Returns a list of regions, each a list of [shape,coords...]. :param str coordinate_type: Type of coords as defined by the display server ['image','detector', 'amplifier'] :param list: List of ROI info in ds9 format """ data = [] # test, could be slow but seems to work nicely self.set_display() try: if coordinate_type == "amplifier": cmd = "regions -system amplifier -strip yes" elif coordinate_type == "detector": cmd = "regions -system detector -strip yes" elif coordinate_type == "image": cmd = "regions -system image -strip yes" else: return [] datads9 = self.xpaget(cmd) datads9 = datads9.lstrip("detector;") datads9 = datads9.lstrip("image;") datads9 = datads9.lstrip("amplifier;") datads9 = datads9.rstrip(";") datads9 = datads9.split(";") # a list of regions if len(datads9) > 0: shape = "box" for roinum, d in enumerate(datads9): # each region d = d.lstrip("box(") d = d.rstrip(")") coords = d.split(",") a = [float(x) for x in coords] a.insert(0, shape) data.append(a) else: return [] except Exception as e: azcam.log(e) return [] return data
def expose(self, exposure_time=-1, imagetype="", title=""): """ Make a complete exposure. exposure_time is the exposure time in seconds imagetype is the type of exposure ('zero', 'object', 'flat', ...) title is the image title. """ # allow custom operations self.start() azcam.log("Exposure started") # if last exposure was aborted, warn before clearing flag if self.exposure_flag == self.exposureflags["ABORT"]: azcam.AzcamWarning("Previous exposure was aborted") # begin if self.exposure_flag != self.exposureflags["ABORT"]: self.begin(exposure_time, imagetype, title) # integrate if self.exposure_flag != self.exposureflags["ABORT"]: self.integrate() # readout if (self.exposure_flag != self.exposureflags["ABORT"] and self.exposure_flag == self.exposureflags["READ"]): try: self.readout() except azcam.AzcamError: pass # end if self.exposure_flag != self.exposureflags["ABORT"]: self.end() self.exposure_flag = self.exposureflags["NONE"] self.completed = 1 azcam.log("Exposure finished") # allow custom operations self.finish() return
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 receive_command(self, currentclient): """ Receive a string from socket until terminator is found. Returns a string. Returns empty string on error. :param currentclient: client ID for socket ID """ terminator = "\n" # likely ends with \r\n # read socket until terminator found msg = "" msg1 = "" while True: try: msg1 = self.request.recv(1024).decode() if msg1 == "": return "" if msg1[-1] == terminator: # found terminator msg += msg1 break msg += msg1 except socket.error as e: if e.errno == 10054: # connection closed pass else: azcam.log(f"receive_command: {e}", prefix="Err-> ") break reply = msg[:-1] # \n if len(reply) == 0: return "" if reply[-1] == "\r": reply = msg[:-1] # \r if reply is None: reply = "" return reply
def get_filters(self, filter_id=0): """ Return a list of all available/loaded filters. """ try: reply = self.command("SHOWFILTERS") except azcam.AzcamError: time.sleep(3) try: reply = self.command("SHOWFILTERS") except azcam.AzcamError: azcam.log("ERROR reading loaded filters in get_filters") raise # no error, so parse return string into list reply1 = reply.lstrip(" ") # remove leading space filterlist = reply1.split(" ") # make a list self.InstalledFilters = filterlist # save list return filterlist
azcam.utils.add_searchfolder(azcam.db.systemfolder, 0) # top level only azcam.utils.add_searchfolder(os.path.join(azcam.db.systemfolder, "common"), 1) azcam.db.datafolder = os.path.join("/data", azcam.db.systemname) parfile = f"{azcam.db.datafolder}/parameters_console_{azcam.db.systemname}.ini" # **************************************************************** # add folders to search path # **************************************************************** azcam.utils.add_searchfolder(azcam.db.systemfolder, 0) # **************************************************************** # start logging # **************************************************************** logfile = os.path.join(azcam.db.datafolder, "logs", "console.log") azcam.db.logger.start_logging(logfile=logfile) azcam.log(f"Configuring console for {azcam.db.systemname}") # **************************************************************** # display # **************************************************************** display = Ds9Display() dthread = threading.Thread(target=display.initialize, args=[]) dthread.start() # thread just for speed # **************************************************************** # console tools # **************************************************************** from azcam.tools import create_console_tools create_console_tools()
# define folders for system # **************************************************************** azcam.db.systemname = "mont4k" azcam.db.systemfolder = os.path.dirname(__file__) azcam.db.systemfolder = azcam.utils.fix_path(azcam.db.systemfolder) azcam.db.datafolder = os.path.join("/data", azcam.db.systemname) azcam.db.datafolder = azcam.utils.fix_path(azcam.db.datafolder) parfile = os.path.join(azcam.db.datafolder, f"parameters_{azcam.db.systemname}.ini") # **************************************************************** # enable logging # **************************************************************** logfile = os.path.join(azcam.db.datafolder, "logs", "server.log") azcam.db.logger.start_logging(logfile=logfile) azcam.log(f"Configuring for {option}") # **************************************************************** # configure system options # **************************************************************** CSS = 0 RTS2 = 0 NORMAL = 0 if "mont4k" in option: template = os.path.join(azcam.db.datafolder, "templates", "FitsTemplate_mont4k_master.txt") parfile = os.path.join(azcam.db.datafolder, "parameters_mont4k.ini") NORMAL = 1 cmdport = 2402 azcam.db.servermode = "mont4k" default_tool = None
""" restart_cameraserver This is just a fix for PC hang issue. """ import os import azcam filepath = "C:\\azcam\\camera_servers\\installer64_19.3\\RestartServiceAdmin.bat.lnk" if os.path.exists(filepath): os.system(filepath) else: azcam.log("Command file for restart_cameraserver not found")
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
azcam.db.datafolder = os.path.join("/data", azcam.db.systemname) azcam.db.datafolder = azcam.utils.fix_path(azcam.db.datafolder) azcam.db.verbosity = 2 # useful for controller status parfile = os.path.join(azcam.db.datafolder, f"parameters_{azcam.db.systemname}.ini") # **************************************************************** # add folders to search path # **************************************************************** azcam.utils.add_searchfolder(azcam.db.systemfolder, 0) # **************************************************************** # enable logging # **************************************************************** logfile = os.path.join(azcam.db.datafolder, "logs", "server.log") azcam.db.logger.start_logging(logfile=logfile) azcam.log(f"Configuring {azcam.db.systemname}") # **************************************************************** # display # **************************************************************** display = Ds9Display() # **************************************************************** # controller # **************************************************************** controller = ControllerArchon() azcam.db.controller = controller controller.camserver.port = 4242 if sysname == "pepsired": controller.camserver.host = "10.0.1.1"
azcam.db.systemfolder = os.path.dirname(__file__) azcam.db.datafolder = azcam.db.systemfolder azcam.db.systemfolder = azcam.utils.fix_path(azcam.db.systemfolder) azcam.db.datafolder = azcam.utils.fix_path(azcam.db.datafolder) # **************************************************************** # add folders to search path # **************************************************************** azcam.utils.add_searchfolder(azcam.db.systemfolder, 0) # **************************************************************** # enable logging # **************************************************************** logfile = os.path.join(azcam.db.datafolder, "logs", "server.log") azcam.db.logger.start_logging(logfile=logfile) azcam.log(f"Configuring {azcam.db.systemname}") # **************************************************************** # broadcast: # **************************************************************** guider_address = "guider2" guider_port = 2405 if BROADCAST: udpobj = UDPinterface() reply = udpobj.get_ids() if reply == []: azcam.log("No systems responded to broadcast") guider_address = "guider2" guider_port = 2405 for system in reply: tokens = system[0].split(" ")
def set_par(self, parameter: str, value: typing.Any = None) -> None: """ Set the value of a parameter in the parameters dictionary. Args: parameter (str): name of the parameter value (Any): value of the parameter. Defaults to None. Returns: None """ parameter = parameter.lower() if azcam.db.mode == "console": try: azcam.db.tools["server"].command(f"parameters.set_par {parameter} {value}") except azcam.AzcamError: return return None # special cases if parameter == "imagefilename": azcam.db.tools["exposure"].image.filename = value return None elif parameter == "imagetitle": if value is None or value == "" or value == "None": azcam.db.tools["exposure"].set_image_title("") else: azcam.db.tools["exposure"].set_image_title(f"{value}") return None elif parameter == "exposuretime": azcam.db.tools["exposure"].set_exposuretime(value) return None elif parameter == "logcommands": azcam.db.tools["cmdserver"].logcommands = int(value) return None # parameter must be in parameters try: attribute = azcam.db.pardict[parameter] except KeyError: azcam.AzcamWarning(f"Parameter {parameter} not available for set_par") return None # object must be a tool tokens = attribute.split(".") numtokens = len(tokens) if numtokens < 2: azcam.log("%s not valid for parameter %s" % (attribute, parameter)) return None # first try to set value type _, value = azcam.utils.get_datatype(value) object1 = tokens[0] # run through tools try: obj = azcam.db.tools[object1] for i in range(1, numtokens - 1): obj = getattr(obj, tokens[i]) # last time is actual object try: setattr(obj, tokens[-1], value) except AttributeError: pass # azcam.AzcamWarning(f"Could not set parameter: {parameter}") except KeyError: pass return None
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 get_status(self): """ Return a variety of system status data in one dictionary. """ progress = 0 filename = self.get_filename() # filename = os.path.basename(filename) et = self.get_exposuretime() if self.is_exposure_sequence: self.exposure_sequence_number seqcount = self.exposure_sequence_number seqtotal = self.exposure_sequence_total else: seqcount = 0 seqtotal = 0 ef = self.exposure_flag expstate = self.exposureflags_rev.get(ef, "") if azcam.db.tools["tempcon"].enabled: try: camtemp, dewtemp = azcam.db.tools["tempcon"].get_temperatures( )[0:2] camtemp = f"{camtemp:.1f}" dewtemp = f"{dewtemp:.1f}" except Exception as e: azcam.log(e) camtemp = -999.9 # error reading temperature dewtemp = -666.6 else: camtemp = -999.9 dewtemp = -666.6 # camtemp = f"{camtemp:8.3f}" # dewtemp = f"{dewtemp:8.3f}" if ef == 1: expcolor = "green" et = self.get_exposuretime() if et == 0: progress = 0.0 explabel = "" else: etr = self.get_exposuretime_remaining() explabel = f"{etr:.1f} sec remaining" progress = float(100.0 * (etr / et)) elif ef == 7: expcolor = "red" progress = int(100.0 * (self.get_pixels_remaining() / self.image.focalplane.numpix_image)) explabel = f"{progress}% readout" elif ef == 8: expcolor = "cyan" explabel = "exposure setup" else: expcolor = "transparent" progress = 0.0 explabel = "" expstate = "" if self.message == "" and expstate != "": message = expstate if self.is_exposure_sequence: message = ( f"{message} - {self.exposure_sequence_number} of {self.exposure_sequence_total}" ) else: message = self.message # debug if 0: self.pgress += 5.0 if self.pgress > 100: self.pgress = 0 progress = self.pgress response = { "message": message, "exposurelabel": explabel, "exposurecolor": expcolor, "exposurestate": expstate, "progressbar": progress, "camtemp": camtemp, "dewtemp": dewtemp, "filename": filename, "seqcount": seqcount, "seqtotal": seqtotal, "timestamp": self._timestamp(0), "imagetitle": self.get_image_title(), "imagetype": self.get_image_type(), "imagetest": self.test_image, "exposuretime": self.get_exposuretime(), "colbin": self.image.focalplane.col_bin, "rowbin": self.image.focalplane.row_bin, "systemname": azcam.db.systemname, "mode": azcam.db.servermode, } return response
def guide(self, number_exposures=1): """ Make a complete guider exposure sequence. NumberExposures is the number of exposures to make, -1 loop forever """ AbortFlag = 0 number_exposures = int(number_exposures) # system must be reset once before an exposure can be made if not azcam.db.tools["controller"].is_reset: azcam.db.tools["controller"].reset() # parameters for faster operation flusharray = self.flush_array azcam.log("Guide started") # this loop continues even for errors since data is sent to a seperate client receiving images LoopCount = 0 while True: if 0: self.begin(exposure_time=-1, imagetype="object", title="guide image") # integrate self.integrate() # readout if self.exposure_flag == self.exposureflags["READ"]: try: self.readout() self.guide_status = 1 # image read OK self.guide_image_copy = self.image except Exception: self.guide_status = 2 # image not read OK, but don't stop guide loop self.image = self.guide_image_copy # image writing self.end() self.exposure_flag = self.exposureflags["NONE"] else: self.expose(-1, "object", "guide image") AbortFlag = azcam.db.abortflag if AbortFlag: break if number_exposures == -1: continue else: LoopCount += 1 if LoopCount >= number_exposures: break # finish self.guide_status = 0 self.flush_array = flusharray if AbortFlag: azcam.AzcamWarning("Guide aborted") else: azcam.log("Guide finished") return
def sequence(self, number_exposures=1, flush_array_flag=-1, delay=-1): """ Take an exposure sequence. Uses pre-set exposure time, image type and image title. NumberExposures is the number of exposures to make. FlushArrayFlag defines detector flushing: -1 => current value defined by exposure.exposure_sequence_flush [default] 0 => flush for each exposure 1 => flush after first exposure only 2 => no flush Delay => delay between exposures in seconds -1 => no change """ AbortFlag = 0 self.is_exposure_sequence = 1 self.exposure_sequence_number = 1 self.exposure_sequence_total = number_exposures number_exposures = int(number_exposures) flush_array_flag = int(flush_array_flag) if delay != -1 and delay != "-1": self.exposure_sequence_delay = float(delay) # set flushing currentflush = self.flush_array if flush_array_flag == -1: flush_array_flag = self.exposure_sequence_flush if flush_array_flag == 0 or flush_array_flag == 1: FlushArray = True else: FlushArray = False self.flush_array = FlushArray self.comp_sequence = (self.check_comparison_imagetype() and azcam.db.tools["instrument"].enabled) if self.comp_sequence: azcam.log("Starting comparison sequence") azcam.db.tools["instrument"].set_comps(self.image_type) if azcam.db.tools["instrument"].shutter_strobe: pass # these instruments use shutter to turn on comps else: azcam.db.tools["instrument"].comps_on() azcam.db.tools["instrument"].comps_delay( ) # delay for lamp warmup if needed for i in range(number_exposures): if i > 0: time.sleep(self.exposure_sequence_delay) if i > 0 and flush_array_flag == 1: self.flush_array = False self.expose(self.exposure_time, self.image_type, self.title) # check and clear user abort AbortFlag = azcam.db.abortflag if AbortFlag: break # sequence may have been stopped if not self.is_exposure_sequence: break self.exposure_sequence_number += 1 # turn off comps if self.comp_sequence: azcam.db.tools["instrument"].comps_off() self.comp_sequence = 0 self.flush_array = currentflush self.is_exposure_sequence = 0 self.exposure_sequence_number = 1 if AbortFlag: self.aborted = 1 return
def handle(self): """ Called when a connection is made from a client. Starts an infinite loop waiting for new commands. Commands are executed sequentially. """ if azcam.db.tools["cmdserver"].welcome_message is not None: self.request.send( str.encode(azcam.db.tools["cmdserver"].welcome_message + "\r\n")) while True: try: prefix_in = f"Rcv{self.currentclient:01}> " prefix_out = f"Out{self.currentclient:01}> " # extra space for indent # ************************************************************************ # receive command from the network socket # ************************************************************************ try: command_string = self.receive_command( self.currentclient).strip() except ConnectionResetError: azcam.log( f"Client {azcam.db.tools['cmdserver'].socketnames[self.currentclient]} disconnected", prefix=prefix_in, ) break except Exception as e: azcam.log(f"ERROR in handle: {e}", prefix="Err-> ") break # ************************************************************************ # disconnect on empty string - important # ************************************************************************ if command_string.strip() == "": try: self.request.send(str.encode("OK\r\n")) except OSError: pass except Exception as e: azcam.log( f"Null command send error for client {self.currentclient}: {e}" ) # azcam.log(f"closing connection to client {self.currentclient}") break # ************************************************************************ # log received command # ************************************************************************ try: self.cmdserver.socketnames[self.currentclient] except Exception: azcam.db.tools["cmdserver"].socketnames[ self.currentclient] = f"unknown_{self.currentclient}" if azcam.db.tools["cmdserver"].logcommands: azcam.log(command_string.strip(), prefix=prefix_in) # ************************************************************************ # check special cases which do not leave cmdserver # ************************************************************************ # close socket connection to client if command_string.lower().startswith("closeconnection"): azcam.log( f"closing connection to {azcam.db.tools['cmdserver'].socketnames[self.currentclient]}", prefix=prefix_in, ) self.request.send(str.encode("OK\r\n")) self.request.close() break # register - register a client name, example: register console elif command_string.lower().startswith("register"): x = command_string.split(" ") azcam.db.tools["cmdserver"].socketnames[ self. currentclient] = f"{x[1]}_{int(self.currentclient)}" self.request.send(str.encode("OK\r\n")) azcam.log(f"OK client {self.currentclient}", prefix=prefix_out) # log reply command_string = "" # echo - for polling as "echo hello" or just "echo" elif command_string.lower().startswith("echo"): s = command_string.split(" ") if len(s) == 1: reply = "OK" elif len(s) == 2: reply = "OK %s" % s[1] else: reply = "OK %s" % " ".join(s[1:]) self.request.send(str.encode(reply + "\r\n")) if azcam.db.tools["cmdserver"].logcommands: azcam.log("%s" % reply, prefix=prefix_out) command_string = "" # update - azcammonitor elif command_string.lower().startswith("update"): if azcam.db.tools["cmdserver"].monitorinterface == 0: azcam.log("ERROR could not update azcammonitor", prefix=prefix_out) reply = "ERROR Could not update azcammonitor" else: azcam.db.tools["cmdserver"].monitorinterface.Register() azcam.log("%s" % "OK", prefix=prefix_out) reply = "OK" self.request.send(str.encode(reply + "\r\n")) command_string = "" # exit - send reply for handshake before closing socket and shutting down elif command_string.lower().startswith("exit"): self.request.send(str.encode("OK\r\n")) azcam.log("%s" % "OK", prefix=prefix_out) # log reply self.request.close() os._exit(0) # kill python # ************************************************************************ # process all other command_strings # ************************************************************************ if command_string != "": # execute command try: reply = azcam.db.tools["cmdserver"].command( command_string) except Exception as e: reply = f"ERROR {repr(e)}" # log reply if azcam.db.tools["cmdserver"].logcommands: azcam.log(reply, prefix=prefix_out) # send reply to socket self.request.send(str.encode(reply + "\r\n")) else: time.sleep(0.10) # for telnet except Exception as message: # catch everything so cmdserver never crashes azcam.log(f"ERROR in cmdserver: {command_string}: {message}") # try to reply but this may not work try: self.request.send(str.encode(f"ERROR {repr(message)}\r\n")) except Exception as e: print(e) pass # OK to do nothing return