217 lines
6.8 KiB
Python
217 lines
6.8 KiB
Python
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")
|
|
|
|
|