Advertisement
Guest User

Untitled

a guest
Sep 9th, 2017
239
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 15.66 KB | None | 0 0
  1. # PiStation 2 Monitoring Tools
  2. # InunoTaishou
  3.  
  4. #!/usr/bin/python
  5. # -*- coding: utf-8 -*-
  6.  
  7. import RPi.GPIO as GPIO
  8. from time import sleep, time
  9. from datetime import datetime
  10. import threading
  11. import subprocess
  12. import os
  13. import psutil
  14. import sys
  15.  
  16. if sys.version_info[0] < 3:
  17.     from Queue import Queue, Empty
  18. else:
  19.     from queue import Queue, Empty
  20.  
  21. # Using Pin # 5 (GPIO03 (SCLI, I^2C)) to restart, shutdown, and turn on the pi
  22. restart_shutdown_pin = 5
  23. # Using pin # 40 to turn the fan on and off
  24. fan_pin = 40
  25. # Using pin 8 for the front LEDs
  26. front_led_pin = 8
  27.  
  28. GPIO.setmode(GPIO.BOARD)
  29. GPIO.setwarnings(False)
  30.  
  31.  
  32. class PrintQueue:
  33.     def __init__(self):
  34.         self.queue = Queue()
  35.         self.__printing = True
  36.         threading.Thread(name='PrintQueue', target=self.__printer).start()
  37.  
  38.     def queue_add(self, text):
  39.         self.queue.put_nowait(text)
  40.  
  41.     def close(self):
  42.         self.queue.join()
  43.         self.__printing = False
  44.  
  45.     def __printer(self):
  46.         while self.__printing:
  47.             try:
  48.                 text = self.queue.get()
  49.                 print(text)
  50.                 self.queue.task_done()
  51.             except Empty:
  52.                 pass
  53.  
  54.             sleep(0.1)
  55.         print('close printer')
  56.         return
  57.  
  58.  
  59. class LogQueue:
  60.     def __init__(self):
  61.         self.queue = Queue()
  62.         self.__logging = True
  63.         self.__log_file = open(os.path.dirname(os.path.realpath(__file__)) + '/PiStation 2.log', 'a')
  64.         threading.Thread(name='LogQueue', target=self.__logger).start()
  65.  
  66.     def queue_add(self, text):
  67.         self.queue.put_nowait(text)
  68.  
  69.     def close(self):
  70.         self.queue.join()
  71.         self.__logging = False
  72.         self.__log_file.close()
  73.  
  74.     def __logger(self):
  75.         while self.__logging:
  76.             try:
  77.                 text = self.queue.get()
  78.                 self.__log_file.write(text)
  79.                 self.queue.task_done()
  80.             except Empty:
  81.                 pass
  82.  
  83.             sleep(0.1)
  84.         print('close logger')
  85.         return
  86.  
  87.  
  88. # LED Controller class
  89. # Wrapper for toggling the LED(s)
  90. class LedController:
  91.     def __init__(self, led_pin, led_on=True):
  92.         self.led_pin = led_pin
  93.         self.led_on = led_on
  94.  
  95.         GPIO.setup(self.led_pin, GPIO.OUT)
  96.         self.set_state(led_on)
  97.  
  98.     def toggle_led(self):
  99.         self.led_on = not self.led_on
  100.         GPIO.output(self.led_pin, self.led_on)
  101.  
  102.     def set_state(self, state):
  103.         self.led_on = state
  104.         GPIO.output(self.led_pin, self.led_on)
  105.  
  106.  
  107. # rsync Monitor class
  108. # Checks the process list to see if rsync is running
  109. class RsyncMonitor:
  110.     # LedController, delay between flashes
  111.     def __init__(self, led_controller, delay=0.2, logger=None, printer=None):
  112.         # User can create the class using a pin number or supply
  113.         # a LedController
  114.         self.printer = printer
  115.         self.logger = logger
  116.  
  117.         if isinstance(led_controller, int):
  118.             if 40 >= led_controller >= 3:
  119.                 led_controller = LedController(led_controller)
  120.  
  121.         if not isinstance(led_controller, LedController):
  122.             log_text = datetime.strftime(datetime.now(), '[%Y-%m-%d] [%H:%M:%S]') + ' Invalid LedController'
  123.             if self.printer or self.logger:
  124.                 if self.printer:
  125.                     self.printer.queue_add(log_text)
  126.                 if self.logger:
  127.                     self.logger.queue_add(log_text + '\n')
  128.             else:
  129.                 print(log_text)
  130.             raise ValueError
  131.  
  132.         if not (isinstance(delay, int) or isinstance(delay, float)):
  133.             log_text = datetime.strftime(datetime.now(), '[%Y-%m-%d] [%H:%M:%S]') + ' Delay must be int or float'
  134.             if self.printer or self.logger:
  135.                 if self.printer:
  136.                     self.printer.queue_add(log_text)
  137.                 if self.logger:
  138.                     self.logger.queue_add(log_text + '\n')
  139.             else:
  140.                 print(log_text)
  141.             raise ValueError
  142.  
  143.         self.led_controller = led_controller
  144.         self.delay = delay
  145.         self.__flashing = None
  146.         self.__rsync_pid = 0
  147.  
  148.     # Returns true if rsync is running (copying)
  149.     def is_copying(self):
  150.         if self.__rsync_pid:
  151.             # Saved the last rsync PID, check to see if
  152.             # it still exists
  153.             try:
  154.                 os.kill(self.__rsync_pid, 0)
  155.             except OSError:
  156.                 # Last PID does not exist, continue
  157.                 self.__rsync_pid = 0
  158.                 pass
  159.             else:
  160.                 # Last PID still exists, still running
  161.                 return True
  162.         try:
  163.             # Check to see if rsync exists
  164.             subprocess.check_output(['pidof', 'rsync'])
  165.             # rsync exists, return true
  166.             return True
  167.         # When the process does not exist, subprocess raises a CalledProcessError
  168.         # rsync is not running, return False
  169.         except subprocess.CalledProcessError:
  170.             return False
  171.  
  172.     # Monitor function, main loop for this class
  173.     def monitor(self, stop_event):
  174.         copy_timer = time()
  175.         try:
  176.             while not stop_event.is_set():
  177.                 # If rsync is running (copying)
  178.                 if self.is_copying():
  179.                     # Not currently flashing the LED(s)
  180.                     if not self.__flashing:
  181.                         # Timer used to show how long the copying process lasted
  182.                         copy_timer = time()
  183.  
  184.                         # Create the stop event, thread, and start it (start flashing)
  185.                         self.__flash_stop = threading.Event()
  186.                         self.__flashing = threading.Thread(target=self.__flash,
  187.                                                            args=(self.__flash_stop, self.led_controller, self.delay))
  188.                         self.__flashing.daemon = True
  189.                         self.__flashing.start()
  190.  
  191.                         if self.printer or self.logger:
  192.                             log_string = datetime.strftime(datetime.now(), '[%Y-%m-%d] [%H:%M:%S]') + ' Copying started'
  193.  
  194.                             if self.logger:
  195.                                 self.logger.queue_add(log_string + '\n')
  196.  
  197.                             if self.printer:
  198.                                 self.printer.queue_add(log_string)
  199.                 # Currently flashing but rsync is no longer running
  200.                 elif self.__flashing:
  201.                     # Stop the thread
  202.                     self.__flash_stop.set()
  203.                     # Clear it
  204.                     self.__flashing = self.__flash_stop = None
  205.  
  206.                     if self.printer or self.logger:
  207.  
  208.                         log_string = datetime.strftime(datetime.now(), '[%Y-%m-%d] [%H:%M:%S]') + \
  209.                                      ' Copying ended: ' + self.__timer_to_time(copy_timer)
  210.  
  211.                         if self.logger:
  212.                             self.logger.queue_add(log_string + '\n')
  213.  
  214.                         if self.printer:
  215.                             self.printer.queue_add(log_string)
  216.  
  217.                             copy_timer = 0
  218.  
  219.                     # Slight delay while we wait for the thread to close
  220.                     stop_event.wait(0.1)
  221.  
  222.                 stop_event.wait(0.25)
  223.         # Some error happened, close out of this gracefully
  224.         except Exception as e:
  225.             if hasattr(e, 'message'):
  226.                 print(e.message)
  227.             else:
  228.                 print(e)
  229.             pass
  230.  
  231.         if self.__flashing:
  232.             self.__flash_stop.set()
  233.         return
  234.  
  235.     @staticmethod
  236.     def __timer_to_time(timer):
  237.         seconds = time() - timer
  238.         days = int(seconds / 86400)
  239.         seconds %= 86400
  240.         hours = int(seconds / 3600)
  241.         seconds %= 3600
  242.         return '{0:02d}d {1:02d}h {2:02d}m {3:02d}s'.format(days, hours, int(seconds / 60), int(seconds % 60))
  243.  
  244.     # Function used for flashing the LED(s)
  245.     # Not inside the main while loop, this is threaded so the flashing
  246.     # is never interrupted from the sleeps
  247.     def __flash(self, stop_event, led, delay):
  248.         led_default_state = led.led_on
  249.  
  250.         # While the stop event has not been set, keep flashing
  251.         while not stop_event.is_set():
  252.             led.toggle_led()
  253.             stop_event.wait(delay)
  254.  
  255.         # Put the LED back to the default state we found it in
  256.         led.set_state(led_default_state)
  257.  
  258.  
  259. class FanMonitor:
  260.     def __init__(self, gpio_pin, max_temp=55, min_temp=50, min_seconds_on=30, logger=False, printer=False):
  261.         self.fan_on = False
  262.         self.fan_pin = gpio_pin
  263.         self.last_temperature = 0
  264.         self.max_temp = max_temp
  265.         self.min_temp = min_temp
  266.         self.__fan_timer = 0
  267.         self.min_seconds_on = min_seconds_on
  268.         self.logger = logger
  269.         self.printer = printer
  270.  
  271.         GPIO.setup(self.fan_pin, GPIO.OUT)
  272.  
  273.     @staticmethod
  274.     def cpu_temp():
  275.         return float(os.popen('vcgencmd measure_temp').readline().replace('temp=', '').replace('\'C\n', ''))
  276.  
  277.     @staticmethod
  278.     def cpu_percent():
  279.         return float(psutil.cpu_percent(interval=1))
  280.  
  281.     @staticmethod
  282.     def cpu_speed():
  283.         current_freq = subprocess.check_output('cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq', shell=True)\
  284.             .replace('0000\n', '')
  285.  
  286.         return \
  287.             float('0.' + current_freq) \
  288.             if int(current_freq) < 100 else \
  289.             float(current_freq[:1] + '.' + current_freq[1:])
  290.  
  291.     def check_temp(self):
  292.         current_temp = self.cpu_temp()
  293.  
  294.         if current_temp >= self.max_temp:
  295.             if self.__fan_timer:
  296.                 self.__fan_timer = 0
  297.  
  298.                 if self.logger or self.printer:
  299.                     log_write = datetime.strftime(datetime.now(), '[%Y-%m-%d] [%H:%M:%S]') + \
  300.                                 ' Resetting timer: CPU Temperature = {}°C | CPU Usage = {}% | CPU Speed = {}GHz' \
  301.                             .format(current_temp, self.cpu_percent(), self.cpu_speed())
  302.  
  303.                     if self.logger:
  304.                         self.logger.queue_add(log_write + '\n')
  305.  
  306.                     if self.printer:
  307.                         self.printer.queue_add(log_write)
  308.  
  309.             elif not self.fan_on:
  310.                 self.set_state(True)
  311.  
  312.                 if self.logger or self.printer:
  313.                     log_write = datetime.strftime(datetime.now(), '[%Y-%m-%d] [%H:%M:%S]') + \
  314.                                 ' Max temp reached ({}°C), turning fan on: CPU Temperature = {}°C | CPU Usage = {}% | CPU Speed = {}GHz' \
  315.                                 .format(self.max_temp, current_temp, self.cpu_percent(), self.cpu_speed())
  316.  
  317.                     if self.logger:
  318.                         self.logger.queue_add(log_write + '\n')
  319.  
  320.                     if self.printer:
  321.                         self.printer.queue_add(log_write)
  322.  
  323.         elif current_temp <= self.min_temp and self.fan_on:
  324.             if not self.__fan_timer:
  325.                 self.__fan_timer = time()
  326.  
  327.                 if self.logger or self.printer:
  328.                     log_write = datetime.strftime(datetime.now(), '[%Y-%m-%d] [%H:%M:%S]') + \
  329.                                 ' Min temp reached ({}°C), Initializing countdown ({}s): ' \
  330.                                 'CPU Temperature = {}°C | CPU Usage = {}% | CPU Speed = {}GHz' \
  331.                                 .format(self.min_temp, self.min_seconds_on, current_temp, self.cpu_percent(), self.cpu_speed())
  332.  
  333.                     if self.logger:
  334.                         self.logger.queue_add(log_write + '\n')
  335.  
  336.                     if self.printer:
  337.                         self.printer.queue_add(log_write)
  338.                 return
  339.  
  340.             current_time = time()
  341.  
  342.             if self.logger or self.printer:
  343.                 log_write = datetime.strftime(datetime.now(), '[%Y-%m-%d] [%H:%M:%S]') + \
  344.                             ' {}s left: CPU Temperature = {}°C | CPU Usage = {}% | CPU Speed = {}GHz' \
  345.                             .format(int(self.min_seconds_on - (current_time - self.__fan_timer)), current_temp, self.cpu_percent(), self.cpu_speed())
  346.                 if self.logger:
  347.                     self.logger.queue_add(log_write + '\n')
  348.  
  349.                 if self.printer:
  350.                     self.printer.queue_add(log_write)
  351.  
  352.             if current_time - self.__fan_timer > self.min_seconds_on:
  353.                 self.set_state(False)
  354.                 self.__fan_timer = 0
  355.                 if self.logger or self.printer:
  356.                     log_write = datetime.strftime(datetime.now(), '[%Y-%m-%d] [%H:%M:%S]') + \
  357.                                 ' Min temp and time reached, turning fan off'
  358.                     if self.logger:
  359.                         self.logger.queue_add(log_write + '\n')
  360.  
  361.                     if self.printer:
  362.                         self.printer.queue_add(log_write)
  363.  
  364.     def toggle_fan(self):
  365.         self.fan_on = not self.fan_on
  366.         GPIO.output(self.fan_pin, self.fan_on)
  367.         return self.fan_on
  368.  
  369.     def set_state(self, state):
  370.         self.fan_on = state
  371.         GPIO.output(self.fan_pin, self.fan_on)
  372.         return self.fan_on
  373.  
  374.  
  375. def button_pressed(pin):
  376.     if not GPIO.input(pin):
  377.         if logger or printer:
  378.             log_string = datetime.strftime(datetime.now(), '[%Y-%m-%d] [%H:%M:%S]') + ' Reset button pressed ....'
  379.             if logger:
  380.                 logger.queue_add(log_string + '\n')
  381.  
  382.             if printer:
  383.                 printer.queue_add(log_string)
  384.         timer = time()
  385.  
  386.         rsync_stop.set()
  387.  
  388.         while not GPIO.input(pin):
  389.             if time() - timer >= 2:
  390.                 if logger or printer:
  391.                     log_string = datetime.strftime(datetime.now(),
  392.                                                    '[%Y-%m-%d] [%H:%M:%S]') + 'Shutting down PiStation 2'
  393.                     if logger:
  394.                         logger.queue_add(log_string + '\n')
  395.  
  396.                     if printer:
  397.                         printer.queue_add(log_string)
  398.                 close()
  399.                 subprocess.call('shutdown -h now', shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  400.                 exit()
  401.  
  402.             led_controller.toggle_led()
  403.             sleep(0.15)
  404.  
  405.         if logger or printer:
  406.             log_string = datetime.strftime(datetime.now(), '[%Y-%m-%d] [%H:%M:%S]') + ' Restarting PiStation 2'
  407.             if logger:
  408.                 logger.queue_add(log_string + '\n')
  409.  
  410.             if printer:
  411.                 printer.queue_add(log_string)
  412.  
  413.         close()
  414.         subprocess.call('reboot now', shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  415.  
  416.  
  417. def close():
  418.     rsync_stop.set()
  419.  
  420.     if logger:
  421.         logger.close()
  422.  
  423.     if printer:
  424.         printer.close()
  425.  
  426.     GPIO.cleanup()
  427.  
  428.  
  429. # Set the restart and shutdown pin
  430. # Enable the Pull Up resistor, allows us to turn the pi back on pressing
  431. # the reset/power button
  432. GPIO.setup(restart_shutdown_pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)
  433.  
  434. printer = PrintQueue()
  435. logger = LogQueue()
  436.  
  437. GPIO.add_event_detect(restart_shutdown_pin, GPIO.BOTH, callback=button_pressed)
  438.  
  439. rsync_stop = threading.Event()
  440. led_controller = LedController(front_led_pin)
  441. fan_monitor = FanMonitor(fan_pin, logger=logger, printer=printer)
  442. rsync_monitor = RsyncMonitor(led_controller, printer=printer, logger=logger)
  443. rsync_thread = threading.Thread(target=rsync_monitor.monitor, args=(rsync_stop,))
  444. rsync_thread.start()
  445.  
  446. try:
  447.     while True:
  448.         sleep(5)
  449.         fan_monitor.check_temp()
  450.  
  451. except KeyboardInterrupt:
  452.     print(datetime.strftime(datetime.now(), '[%Y-%m-%d] [%H:%M:%S]') + ' Keyboard interrupt')
  453.     close()
  454.    
  455. sys.exit()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement