def jobRun(self): ### List all jpg files in the current local sub-folder self._locdir = os.path.join(self._config['image_dir'], self._imageFIFO.crtSubDir) self._image_names = os.path.join(self._locdir, self._imageFIFO.crtSubDir + '-*' + self._imageFIFO.camID + '.jpg') self.imagelist = sorted(glob.glob(self._image_names)) if len(self.imagelist) > 0: logging.debug("imagelist: %s .. %s" % (self.imagelist[0], self.imagelist[-1])) else: logging.debug("imagelist: empty. No %s found!" % self._image_names) ### Run directory/file management only if no errors were detected when ### updating to remote directory if not self._eventDbErr.is_set(): # Process the new list only if it is changed and has at least max length if ( not (self._imagelist_ref == self.imagelist) ) and \ len(self.imagelist) > self._config['list_size']: # Remove all the local images, which are # not in the camera buffer and are in the uploaded images buffer try: self._imageFIFO.acquireSemaphore() self._imageUpldFIFO.acquireSemaphore() for img in self.imagelist: if not img in self._imageFIFO and \ img in self._imageUpldFIFO: logging.info("Remove image: %s" % img) self._rmimg = subprocess.Popen("rm " + img, stderr=subprocess.PIPE, stdout=subprocess.PIPE, shell=True) self._diroutput, self._direrrors = self._rmimg.communicate() except OSError as e: raise rpiBaseClassError("%s::: jobRun(): File %s could not be deleted!\n%s" % (self.name, img, e), ERRLEV2) except: raise rpiBaseClassError("%s::: jobRun(): Unhandled Exception:\n%s" % (self.name, str(sys.exc_info())), ERRCRIT) finally: self._imageFIFO.releaseSemaphore() self._imageUpldFIFO.releaseSemaphore() #raise rpiBaseClassError("%s::: jobRun(): Test crash!" % self.name, 4) # Update status self.statusUpdate = (self.name, len(self.imagelist)) # Update image list in the current local sub-folder self._imagelist_ref = sorted(glob.glob(self._image_names)) if len(self._imagelist_ref) > 0: logging.debug("imagelist_ref: %s .. %s" % (self._imagelist_ref[0], self.imagelist[-1])) else: logging.debug("imagelist_ref: empty. No %s found!" % self._image_names) else: logging.info("eventDbErr is set!")
def _mkdirImage(self, path): """ Create a new remote directory. Examples: _mkdirImage('/dropbox_dir_test') """ try: self._dbx.files_create_folder('/' + os.path.normpath(path)) logging.debug("%s::: Remote output folder /%s created." % (self.name, path)) except ApiError as e: noerr = False # dropbox.files.CreateFolderError if e.error.is_path(): # dropbox.files.WriteError we = e.error.get_path() if we.is_conflict(): # dropbox.files.WriteConflictError wce = we.get_conflict() # union tag is 'folder' if wce.is_folder(): logging.info("%s::: Remote output folder /%s already exist!" % (self.name, path)) noerr = True if not noerr: raise rpiBaseClassError("_mkdirImage(): Remote output folder /%s was not created! %s" % (path, e.error), ERRCRIT) else: pass
def endDayOAM(self): """ End-of-Day Operation and Maintenance sequence. """ self._lsImage(self.upldir) logging.info("%s::: %d images in the remote folder %s" % (self.name, len(self.imageDbList), self.upldir)) # Lock the uplaod buffer self.imageUpldFIFO.acquireSemaphore() try: upldimg=[] for img in self.imageUpldFIFO: upldimg.append(img) with open(self.logfile,'w') as logf: json.dump(upldimg, logf) del upldimg logging.info("%s::: Local log file %s updated." % (self.name, self.logfile)) except IOError: raise rpiBaseClassError("endDayOAM(): Local log file %s was not found." % self.logfile, ERRCRIT) finally: # Release the upload buffer self.imageUpldFIFO.releaseSemaphore()
def _mvImage(self, from_path, to_path): """ Move/rename a remote file or directory. Examples: _mvImage('./path1/dropbox-move-test.jpg', '/path2/dropbox-move-test.jpg') """ try: self._dbx.files_move( '/' + os.path.normpath(from_path), '/' + os.path.normpath(to_path) ) logging.debug("%s::: _mvImage(): Moved file from %s to %s" % (self.name, from_path, to_path)) except ApiError as e: raise rpiBaseClassError("_mvImage(): Image %s could not be moved to %s! %s" % (from_path, to_path, e.error), ERRLEV2)
def _putImage(self, from_path, to_path, overwrite=False): """ Copy local file to remote file. Stores the uploaded files names in self.imageUpldFIFO. Examples: _putImage('./path/test.jpg', '/path/dropbox-upload-test.jpg') """ try: mode = (WriteMode.overwrite if overwrite else WriteMode.add) with open(from_path, "rb") as from_file: self._dbx.files_upload( from_file, '/' + os.path.normpath(to_path), mode) if not overwrite: self.imageUpldFIFO.append(from_path) logging.debug("%s::: _putImage(): Uploaded file from %s to remote %s" % (self.name, from_path, to_path)) except IOError: raise rpiBaseClassError("_putImage(): Local img file %s could not be opened." % from_path, ERRCRIT) except ApiError as e: raise rpiBaseClassError("_putImage(): %s" % e.error, ERRLEV2)
def _lsImage(self,from_path): """ List the image/video files in the remote directory. Stores the found file names in self.imageDbList. """ try: if self._imageDbCursor is None: self.ls_ref = self._dbx.files_list_folder('/' + os.path.normpath(from_path), recursive=False, include_media_info=True ) else: new_ls = self._dbx.files_list_folder_continue(self._imageDbCursor) if new_ls.entries == []: logging.debug("%s::: _lsImage():: No changes on the server." % self.name) else: self.ls_ref = new_ls # Select only images and only the ones for the current imgid (camid) foundImg = False for f in self.ls_ref.entries: if 'media_info' in f._all_field_names_ and \ f.media_info is not None: if self.imgid in f.path_lower: img = '.%s' % f.path_lower foundImg = True if not img in self.imageDbList: self.imageDbList.append(img) if not foundImg: self.imageDbList = [] ### Store the hash of the folder self._imageDbCursor = self.ls_ref.cursor if len(self.imageDbList) > 0: logging.debug("%s::: _lsImage():: imageDbList[0..%d]: %s .. %s" % (self.name, len(self.imageDbList)-1, self.imageDbList[0], self.imageDbList[-1]) ) else: logging.debug("%s::: _lsImage():: imageDbList[]: empty" % self.name) except ApiError as e: raise rpiBaseClassError("_lsImage(): %s" % e.error, ERRLEV2)
def initClass(self): """" (re)Initialize the class. """ ### Host/cam ID self._camera = None self.camid = 'CAM1' if subprocess.check_output(["hostname", ""], shell=True).strip().decode('utf-8').find('pi2') > 0: self.camid = 'CAM2' ### Init the FIFO buffer self.imageFIFO.camID = self.camid self.imageFIFO.clear() self.crtlenFIFO = 0 ### Init the "dark" time flag and reference image brightness # (used only when RPICAM or RASPISTILL= True) self.bDarkExp = False self.imgbr = 128 ### Init GPIO ports self.IRport = 19 # use GPIO19 if USEGPIO: GPIO.cleanup(self.IRport) GPIO.setmode(GPIO.BCM) GPIO.setup(self.IRport, GPIO.OUT, initial=0) ### Init the font self._TXTfont = ImageFont.truetype("/usr/share/fonts/truetype/freefont/FreeSans.ttf", 16) ### Create output folder try: os.mkdir(self._config['image_dir']) self.imgSubDir = time.strftime('%d%m%y', time.localtime()) logging.info("%s::: Local output folder %s created." % (self.name, self._config['image_dir'])) except OSError as e: if e.errno == EEXIST: logging.info("%s::: Local output folder %s already exist!" % (self.name, self._config['image_dir'])) pass else: raise rpiBaseClassError("%s::: initClass(): Local output folder %s could not be created" % (self.name, self._config['image_dir']) , ERRCRIT) ### Fill in the fifo buffer with images found in the output directory ### Only the image files with the current date are listed! #imagelist_ref = sorted(glob.glob(self._config['image_dir'] + '/' + time.strftime('%d%m%y', time.localtime()) + '-*.jpg')) #self.imageFIFO.acquireSemaphore() #for img in imagelist_ref: # if not img in self.imageFIFO: # self.imageFIFO.append(img) #self.imageFIFO.releaseSemaphore() # Ephem parameters # The ephem.localtime() function converts a PyEphem date into a Python datetime object # expressed in your local time zone. # A negative value of horizon can be used when an observer is high off of the ground. self._sun = ephem.Sun() self._loc = ephem.Observer() self._loc.lat = self._config['dark_loc'][0] self._loc.lon = self._config['dark_loc'][1] self._loc.pressure = 0 self._loc.horizon = '-2:30'
def jobRun(self): ### Create the daily output sub-folder ### Set the full image file path #self._config['image_subdir'] = time.strftime('%d%m%y', time.localtime()) self.imageFIFO.crtSubDir = time.strftime('%d%m%y', time.localtime()) self._locdir = os.path.join(self._config['image_dir'], self.imageFIFO.crtSubDir) try: os.mkdir(self._locdir) logging.info("%s::: Local daily output folder %s created." % (self.name, self._locdir)) except OSError as e: if e.errno == EEXIST: logging.debug("%s::: Local daily output folder %s already exist!" % (self.name, self._locdir)) pass else: raise rpiBaseClassError("%s::: jobRun(): Local daily output folder %s could not be created" % (self.name, self._locdir) , ERRCRIT) finally: self.image_name = self.imageFIFO.crtSubDir + '-' + time.strftime('%H%M%S', time.localtime()) + '-' + self.camid + '.jpg' self.image_path = os.path.join(self._locdir, self.image_name) ### Take a new snapshot and save the image locally try: # Lock the buffer self.imageFIFO.acquireSemaphore() if FAKESNAP: logging.debug('Faking snapshot: ' + self.image_name) self._grab_cam = subprocess.Popen("touch " + self.image_path, stderr=subprocess.PIPE, stdout=subprocess.PIPE, shell=True) # Check return/errors self._camoutput, self._camerrors = self._grab_cam.communicate() elif RASPISTILL: # Use raspistill -n -vf -hf -awb auto -q 95 self._grab_cam = subprocess.Popen("raspistill -n -vf -hf -q 95 -co 30 -w 640 -h 480 -o " + self.image_path, stderr=subprocess.PIPE, stdout=subprocess.PIPE, shell=True) # Check return/errors #self.grab_cam.wait() self._camoutput, self._camerrors = self._grab_cam.communicate() elif RPICAM: # Init the camera #with picamera.PiCamera() as self._camera: self._camera = picamera.PiCamera() self._camera.resolution = (1024, 768) self._camera.exif_tags['IFD0.Copyright'] = 'Copyright (c) 2016 Istvan Z. Kovacs' #self._camera.hflip = True #self._camera.vflip = True self._camera.rotation = 0 if self.camid == 'CAM1': self._camera.rotation = 90 elif self.camid == 'CAM2': self._camera.rotation = 180 # Set camera exposure according to the 'dark' time threshold self._setCamExp() # Create the in-memory stream stream = io.BytesIO() # Camera warm-up time and capture self._camera.capture(stream, format='jpeg') # Read stream to a PIL image stream.seek(0) image = Image.open(stream) # When in 'dark' time # Calculate brightness and adjust shutter speed sN = ': ' if self.bDarkExp: sN = 'n' + sN if self.camid == 'CAM1': # Calculate brightness #self._grayscaleAverage(image) self._averagePerceived(image) # Recapture image with new shutter speed if needed if self.imgbr < 118 or \ self.imgbr > 138: # Release the buffer (this capture could take a few seconds) self.imageFIFO.releaseSemaphore() # Shutter speed (micro seconds) ss = self._camera.shutter_speed logging.debug('Before: Br=%d, Ss=%dus' % (self.imgbr, ss)) # Re-capture the picture time.sleep(3) self._camera.shutter_speed = int(ss*(2 - float(self.imgbr)/128)) self._camera.capture(stream, format='jpeg') stream.seek(0) image = Image.open(stream) # Re-calculate brightness self._averagePerceived(image) logging.debug('After: Br=%d, Ss=%dus' % (self.imgbr, self._camera.shutter_speed)) # Lock the buffer self.imageFIFO.acquireSemaphore() #elif self.camid == 'CAM2': # Do nothing ? #else: # Do nothing ? # Add overlay text to the final image draw = ImageDraw.Draw(image,'RGBA') draw.rectangle([0,image.size[1]-20,image.size[0],image.size[1]], fill=(150,200,150,100)) draw.text((2,image.size[1]-18), self.camid + sN + time.strftime('%b %d %Y, %H:%M', time.localtime()), fill=(0,0,0,0), font=self._TXTfont) #n_width, n_height = TXTfont.getsize('#XX') #draw.text((image.size[0]-n_width-2,image.size[1]-18), '#XX', fill=(0,0,0,0), font=self._TXTfont) del draw # Save image and close the stream image.save( self.image_path, format='jpeg', quality=95 ) #image.close() # Close BytesIO stream stream.close() # Set output indicators self._camoutput = self.image_path self._camerrors = '' else: # Use fswebcam -d /dev/video0 -s brightness=50% -s gain=32 self._grab_cam = subprocess.Popen("fswebcam -d /dev/video0 -q -r 640x480 --jpeg=95 " + self.image_path, stderr=subprocess.PIPE, stdout=subprocess.PIPE, shell=True) ### Check return/errors self._camoutput, self._camerrors = self._grab_cam.communicate() logging.info('Snapshot: ' + self.image_name) ### Add image to deque (FIFO) self.imageFIFO.append(self.image_path) self.crtlenFIFO = len(self.imageFIFO) if self.crtlenFIFO > 0: logging.debug("imageFIFO[0..%d]: %s .. %s" % (self.crtlenFIFO-1, self.imageFIFO[0], self.imageFIFO[-1])) else: logging.debug("imageFIFO[]: empty") ### Update status self.statusUpdate = (self.name, self.crtlenFIFO) ### Close the picamera if RPICAM: self._camera.close() except OSError as e: raise rpiBaseClassError("%s::: jobRun(): Snapshot %s could not be created!\n%s" % (self.name, self.image_path, e), ERRLEV2) finally: # Release the buffer self.imageFIFO.releaseSemaphore()
def jobRun(self): try: # Lock the buffer self._imageFIFO.acquireSemaphore() # Get the current images in the FIFO # Refresh the last remote image when available if len(self._imageFIFO): # Update remote cam image with the current (last) image if not (self._imageFIFO[-1] == self.crt_image_snap): self._putImage(self._imageFIFO[-1], self._config['image_snap'], True) self.crt_image_snap = self._imageFIFO[-1] self.numImgUpdDb += 1 logging.info("Updated remote %s with %s" % (self._config['image_snap'], self._imageFIFO[-1]) ) # Lock the upload buffer self.imageUpldFIFO.acquireSemaphore() # Check if a new upload sub-folder has to be used if not (self.imageUpldFIFO.crtSubDir == self._imageFIFO.crtSubDir): self.imageUpldFIFO.crtSubDir = self._imageFIFO.crtSubDir self.upldir = os.path.normpath(os.path.join(self._config['image_dir'], self.imageUpldFIFO.crtSubDir)) self._mkdirImage(self.upldir) # Upload only images in the FIFO which have not been uploaded yet for img in self._imageFIFO: if not img in self.imageUpldFIFO: self._putImage(img, os.path.join(self.upldir, os.path.basename(img))) logging.info("Uploaded %s" % img ) # Release the upload buffer self.imageUpldFIFO.releaseSemaphore() # Update status self.statusUpdate = (self.name, self.numImgUpdDb) else: # Update status self.statusUpdate = (self.name, ERRNONE) logging.info('Nothing to upload') # Handle exceptions, mostly HTTP/SSL related! except exceptions.Timeout as e: # Catching this error will catch both ReadTimeout and ConnectTimeout. raise rpiBaseClassError("%s::: jobRun(): Connect/ReadTimeoutError:\n%s" % (self.name, str(e)), ERRLEV2) except exceptions.ConnectionError as e: # A Connection error occurred. raise rpiBaseClassError("%s::: jobRun(): ConnectionError:\n%s" % (self.name, str(e)), ERRLEV2) except exceptions.HTTPError as e: # An HTTP error occurred. raise rpiBaseClassError("%s::: jobRun(): HTTPError:\n%s" % (self.name, str(e)), ERRLEV2) except exceptions.RequestException as e: # There was an ambiguous exception that occurred while handling your request. raise rpiBaseClassError("%s::: jobRun(): RequestException:\n%s" % (self.name, str(e)), ERRLEV2) # except BadStatusLine as e: # self.eventErr_set('run()') # logging.debug("BadStatusLine:\n%s" % str(e)) # pass except rpiBaseClassError as e: if e.errval == ERRCRIT: self.endDayOAM() raise rpiBaseClassError("%s::: jobRun(): %s" % (self.name, e.errmsg), e.errval) except RuntimeError as e: self.endDayOAM() raise rpiBaseClassError("%s::: jobRun(): RuntimeError:\n%s" % (self.name, str(e)), ERRCRIT) except: self.endDayOAM() raise rpiBaseClassError("%s::: jobRun(): Unhandled Exception:\n%s" % (self.name, str(sys.exc_info())), ERRCRIT) finally: # Release the buffer self._imageFIFO.releaseSemaphore()
def initClass(self): """" (re)Initialize the class. """ #self.imageDbHash = None self._imageDbCursor = None self.imageDbList = [] self.numImgUpdDb = 0 self.crt_image_snap = None self.imgid = self._imageFIFO.camID + '.jpg' self.upldir = os.path.normpath(os.path.join(self._config['image_dir'], self.imageUpldFIFO.crtSubDir)) self.logfile = './upldlog.json' ### When there are already images listed in the upload log file, then # make sure we don't upload them to the remote folder again # Else, create the file with an empty list; to be updated in endDayOAM() try: self.imageUpldFIFO.acquireSemaphore() self.imageUpldFIFO.clear() if os.path.isfile(self.logfile): with open(self.logfile,'r') as logf: upldimg = json.load(logf) for img in upldimg: self.imageUpldFIFO.append(img) del upldimg logging.info("%s::: Local log file %s found and loaded." % (self.name, self.logfile)) else: with open(self.logfile,'w') as logf: json.dump([], logf) logging.info("%s::: Local log file %s initialized." % (self.name, self.logfile)) except IOError: raise rpiBaseClassError("%s::: initClass(): Local log file %s was not found or could not be created." % (self.name, self.logfile), ERRCRIT) finally: # Release the upload buffer self.imageUpldFIFO.releaseSemaphore() ### Init Dropbox API client self._token_file = self._config['token_file'] self._dbx = None self.dbinfo = None try: with open(self._token_file, 'r') as token: self._dbx = Dropbox(token.read()) info = self._dbx.users_get_current_account() # info._all_field_names_ = # {'account_id', 'is_paired', 'locale', 'email', 'name', 'team', 'country', 'account_type', 'referral_link'} self.dbinfo ={'email': info.email, 'referral_link': info.referral_link} logging.info("%s::: Loaded access token from ''%s''" % (self.name, self._token_file) ) ### Create remote root folder (relative to app root) if it does not exist yet self._mkdirImage(os.path.normpath(self._config['image_dir'])) except rpiBaseClassError as e: if e.errval == ERRCRIT: self.endDayOAM() raise rpiBaseClassError("initClass(): %s" % e.errmsg, e.errval) except IOError: self.endDayOAM() raise rpiBaseClassError("initClass(): Token file ''%s'' could not be read." % (self.name, self._token_file), ERRCRIT) except AuthError as e: self.endDayOAM() raise rpiBaseClassError("initClass(): AuthError:\n%s" % e.error, ERRCRIT) except DropboxException as e: self.endDayOAM() raise rpiBaseClassError("initClass(): DropboxException:\n%s" % str(e), ERRCRIT) except InternalServerError as e: self.endDayOAM() raise rpiBaseClassError("initClass(): InternalServerError:\n%s" % str(e.status_code), ERRCRIT)