import pycurl import os from io import BytesIO import subprocess from ftplib import FTP import signal class Cam(object): """ Cam object Attributes ---------- log : logging_object name : str name of the cam, set by the section in the config file ip : str IP of the cam in string format location : str Location of the picutres on the SD-Card ftp_config : config_object FTP configuration; such as URI, username and password Methods ------- getFileList() Get the file list of the cam via curl organiseMissing(files) Get a list of files and decide which to download getImage(filename) Get Images from cam via curl uploadImage(filename) Upload the image to remote FTP server isOnline() Check if the cam is online via ping """ def __init__(self, log, name, ip, location, ftp_config): self.log = log self.name = name self.ip = ip self.getFileListPath = 'http://' + self.ip + '/command.cgi?op=100&DIR=' + location self.getFilePath = 'http://' + self.ip + '/' + location self.ftp_config = ftp_config #TODO: Test what happens if file is not present fname = './data/' + self.name + '/last.txt' self.last = str(0) try: with open(fname) as last_file: for line in last_file: self.last = line.strip().split()[0] except FileNotFoundError: self.log.debug(self.name + " last.txt was not found, default value 0 is set") self.log.debug(self.name + " last index is " + self.last) def getFileList(self): """Get the file list of the cam via curl Return: list of filenames """ # Assert that the cam is online, otherwise the whole function doesn't make sense assert self.isOnline() self.log.info(self.name + " get list of files") # Open, run and close the curl session crl = pycurl.Curl() b_obj = BytesIO() crl.setopt(pycurl.CONNECTTIMEOUT, 5) crl.setopt(pycurl.TIMEOUT, 5) crl.setopt(crl.URL, self.getFileListPath) crl.setopt(crl.WRITEDATA, b_obj) crl.perform() crl.close() # Get the file list get_list = b_obj.getvalue().decode('utf8').splitlines() del get_list[0] # Create empty list for processing later files = [] # Get the filenames only for line in get_list: files.append(line.split(',')[1]) self.log.debug(self.name + " " + " ".join(files)) # Return the file list return files def organiseMissing(self, files): """Get a list of files and decide which to download Parameters ---------- files: list List of files """ # Go trough the filenames, assuming they are sorted #TODO: If possible try to make it work without the assumption # Do this with another index (current highest) and update this index only with a bigger number # In the last step, update self.last with current highest index (and obviously update the value in the file for filename in files: file_index = int(''.join(list(filter(str.isdigit, filename)))) # Check if the file needs to be downloaded if file_index > int(self.last): self.log.info("Found a file: "+ filename) # Download the file self.getImage(filename) self.log.info("Image " + filename + " downloaded") try: # Upload images to corresponding FTP server self.uploadImage(filename) self.log.info("File " + filename + " uploaded to remote server") # Remove the local file os.remove('./data/' + self.name + '/pictures/' + filename) self.log.info("File " + filename + " removed from local storage") # Update the last index self.last = file_index except ConnectionRefusedError: self.log.error("Could not reach FTP server, exit now") return 1 # Update the last file for each cam only once last_file = open('./data/' + self.name + '/last.txt', "w+") last_file.write(str(self.last)) last_file.close() return 0 def getImage(self, filename): """Get Images from cam via curl Parameters ---------- filename : str Filnema of the image to download """ assert self.isOnline() # Initialize the curl object, set timeout to 5 seconds, otherwise curl might hang if # the cam is suddenly not reachable anymore crl = pycurl.Curl() crl.setopt(pycurl.CONNECTTIMEOUT, 5) crl.setopt(pycurl.TIMEOUT, 5) self.log.info("Download Picture from " + self.getFilePath + '/' + filename) #TODO: Set data path in default config section with open('./data/'+ self.name +'/pictures/' + filename, 'wb') as f: signal.signal(signal.SIGALRM, self.handler) signal.alarm(5) crl.setopt(crl.URL, self.getFilePath + '/' + filename) crl.setopt(crl.WRITEDATA, f) crl.perform() crl.close() signal.alarm(0) def uploadImage(self, filename): """Upload the image to remote FTP server Parameters ---------- filename : str Filename for the file which should be uploaded """ # Only if the config is set to new use the additional prefix, otherwise none if self.ftp_config['db'] == "new": prefix = "_neue_db" else: prefix = "" # Initialize FTP settings ftp_path = self.ftp_config['ftp_path'] + '/' + self.name + prefix #TODO: Move to config file ftp = FTP("xray876.server4you.net") ftp.login(self.ftp_config['ftp_user'], self.ftp_config['ftp_pwd']) ftp.cwd(ftp_path) with open('data/' + self.name + '/pictures/' + filename, 'rb') as f: ftp.storbinary('STOR %s' % os.path.basename(filename), f) ftp.quit() def isOnline(self): """Check if the Cam is online via ping Returns ------- 0 cam is online 1 cam is offline """ command = ['ping', '-c', '1', '-w', '1', '-q', self.ip] return subprocess.run(command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode == 0 def handler(self, signum, frame): self.log.debug("Signal handler called with signal" + signum) raise OSError("Could not reach camera")