flashair-uploader/flashairup/cam.py

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")