output.py 10.5 KB
#!/usr/bin/env python

import time
import settings
from matrix import Matrix
from data import data

class Output():
	
	def __init__(self, mode = "terminal"):
		self.mode = mode
		self.errors = False
		self.disk_show = True
		if mode == "lcd":
			self.matrix = Matrix(device="/dev/ttyACM0")
			self.matrix.matrixOn()
			self.matrix.matrixSize(16, 2)
			self.matrix.matrixClear()
			self.matrix.matrixAutoscrollOff()
			self.matrix.matrixBrightness(250)
			self.matrix.matrixRGB(0,255,0)
		else:
			self.matrix = False
	
	def sizeof_fmt(self, num, suffix='B'):
		""" Handy convertor for unit suffixes """
		for unit in ['','K','M','G','T','P','E','Z']:
			if abs(num) < 1024.0:
				return "%3.0f%s%s" % (num, unit, suffix)
			num /= 1024.0
		return "%.1f%s%s" % (num, 'Yi', suffix)
		
	def lcd_overview(self, data, interval):
		""" Show the information on the lcd serial screen """
		self.matrix.matrixClear()
		# Display error conditions first and foremost
		error = False			
		if len(data.errors) > 0:
			# On detection of errors, screen goes red
			self.matrix.matrixRGB(settings.CPU_PEAK['r'],settings.CPU_PEAK['g'],settings.CPU_PEAK['b'])
			# Loop over and scroll each error - ignore the normal cycle process in the outer call
			e_num = 1
			for e in data.errors:
				
				display_text = "%s: %s" % (e_num, e)
				
				if len(display_text) <= settings.DISPLAY_WIDTH:
					# Display normal errors that fit in the width
					self.matrix.matrixClear()
					self.matrix.matrixWrite("%s Errors!" % len(data.errors))
					self.matrix.matrixNewline()
					self.matrix.matrixWrite(display_text)
					
				else:
					# Display longer errors that need to be scrolled
					display_text = ""
					e = "%s: %s" % (e_num, e)
					for i in range (0, len(e)):  
						self.matrix.matrixClear()
						self.matrix.matrixWrite("%s Errors!" % len(data.errors))
						self.matrix.matrixNewline()
						display_text = e[i:(i+settings.DISPLAY_WIDTH)]  
						self.matrix.matrixWrite(display_text)
						if i == 0:
							# Pause the first time we show the beginning of the error.
							time.sleep(1)
						else:
							# Slight gap between each character scrolling by.
							time.sleep(settings.SCROLL_SPEED)
					
				# Sleep for next error
				time.sleep(1)
				e_num += 1
			error = True
		
		# Don't bother printing metrics if we have errors to show!
		if error:
			self.errors = True
			return
		else:
			self.errors = False
			
		# If CPU load is above a trigger level, 
		# If no errors, screen defaults back to green
		if data.cpu_percent_avg >= settings.CPU_PEAK['level']:
			self.matrix.matrixRGB(settings.CPU_PEAK['r'],settings.CPU_PEAK['g'],settings.CPU_PEAK['b'])
		elif data.cpu_percent_avg >= settings.CPU_HIGH['level']:
			self.matrix.matrixRGB(settings.CPU_HIGH['r'],settings.CPU_HIGH['g'],settings.CPU_HIGH['b'])
		elif data.cpu_percent_avg >= settings.CPU_MED['level']:
			self.matrix.matrixRGB(settings.CPU_MED['r'],settings.CPU_MED['g'],settings.CPU_MED['b'])
		elif (data.cpu_percent_avg >= settings.CPU_LOW['level']) or (data.cpu_percent_avg > settings.CPU_IDLE['level']):
			self.matrix.matrixRGB(settings.CPU_LOW['r'],settings.CPU_LOW['g'],settings.CPU_LOW['b'])
		else:
			self.matrix.matrixRGB(settings.CPU_IDLE['r'],settings.CPU_IDLE['g'],settings.CPU_IDLE['b'])
		
		# Are we showing alternate screen 1?
		# Show CPU
		# Show IP address
		if (interval >= settings.DETAIL_1_INTERVAL) and (interval <= (settings.DETAIL_1_INTERVAL + settings.DETAIL_DURATION)):
			self.matrix.matrixWrite("CPU %2d%%  %sMHz" % (data.cpu_percent_avg, data.cpu_mhz))
			self.matrix.matrixWrite("IP %s" % (data.network_address))
			return
				
		# Are we showing alternate screen 2?
		# Show CPU
		# Show CPU load
		if (interval >= settings.DETAIL_2_INTERVAL) and (interval <= (settings.DETAIL_2_INTERVAL + settings.DETAIL_DURATION)):
			self.matrix.matrixWrite("CPU %2d%%  %sMHz" % (data.cpu_percent_avg, data.cpu_mhz))
			self.matrix.matrixWrite("%s %s %s" % (data.load_average[0], data.load_average[1], data.load_average[2]))
			return
			
		# Are we showing alternate screen 3?
		# Show CPU
		# Show Disk space
		if (interval >= settings.DETAIL_3_INTERVAL) and (interval <= (settings.DETAIL_3_INTERVAL + settings.DETAIL_DURATION)):
			self.matrix.matrixWrite("CPU %2d%%  %sMHz" % (data.cpu_percent_avg, data.cpu_mhz))
			if self.disk_show:
				self.matrix.matrixWrite("DSK %s/%s" % (self.sizeof_fmt(num = data.disk_space), self.sizeof_fmt(num = data.disk_size)))
				self.disk_show = False
			else: 
				self.matrix.matrixWrite("NFS %s/%s" % (self.sizeof_fmt(num = data.nfs_space), self.sizeof_fmt(num = data.nfs_size)))
				self.disk_show = True
			return
		
		# Are we showing alternate screen 4?
		# Show CPU
		# Show RAM use
		if (interval >= settings.DETAIL_4_INTERVAL) and (interval <= (settings.DETAIL_4_INTERVAL + settings.DETAIL_DURATION)):
			self.matrix.matrixWrite("CPU %2d%%  %sMHz" % (data.cpu_percent_avg, data.cpu_mhz))
			self.matrix.matrixWrite("RAM %s/%s" % (self.sizeof_fmt(num = data.ram_avail), self.sizeof_fmt(num = data.ram_total)))
			return
		
		# Show the normal screen
		# Show CPU
		# Show total network and disk IO
		self.matrix.matrixWrite("CPU %2d%%  %sMHz" % (data.cpu_percent_avg, data.cpu_mhz))
		self.matrix.matrixWrite("IO %ss %ss" % (self.sizeof_fmt(num = data.stats_bandwidth["net_bytes_rw_bw"]), self.sizeof_fmt(num = data.stats_bandwidth["disk_bytes_rw_bw"])))
		return
		
	def terminal_overview(self, data, interval):
		""" SHow the main overview screen on a unix console """
					
		# Display error conditions first and foremost
		error = False			
		if len(data.errors) > 0:
			# Loop over and scroll each error - ignore the normal cycle process in the outer call
			e_num = 1
			for e in data.errors:
				
				display_text = "%s: %s" % (e_num, e)
				
				if len(display_text) <= settings.DISPLAY_WIDTH:
					# Display normal errors that fit in the width
					print "%s Errors detected!" % len(data.errors)
					print display_text
					
				else:
					# Display longer errors that need to be scrolled
					display_text = ""
					e = "%s: %s" % (e_num, e)
					for i in range (0, len(e)):  
						print "%s Errors detected!" % len(data.errors)
						display_text = e[i:(i+settings.DISPLAY_WIDTH)]  
						print display_text
						if i == 0:
							# Pause the first time we show the beginning of the error.
							time.sleep(1)
						else:
							# Slight gap between each character scrolling by.
							time.sleep(settings.SCROLL_SPEED)
					
				# Sleep for next error
				time.sleep(1)
				e_num += 1
			error = True
			
		# Don't bother printing metrics if we have errors to show!
		if error:
			self.errors = True
			return
		else:
			self.errors = False
			
		# Are we showing alternate screen 1?
		# Show CPU
		# Show IP address
		if (interval >= settings.DETAIL_1_INTERVAL) and (interval <= (settings.DETAIL_1_INTERVAL + settings.DETAIL_DURATION)):
			#print "CPU %2d%% %2d%% %2d%% %2d%%" % (data.cpu_percent[0], data.cpu_percent[1], data.cpu_percent[2], data.cpu_percent[3])
			print "CPU %2d%%  %sMHz" % (data.cpu_percent_avg, data.cpu_mhz)
			print "IP %s" % (data.network_address)
			return
				
		# Are we showing alternate screen 2?
		# Show CPU
		# Show CPU load
		if (interval >= settings.DETAIL_2_INTERVAL) and (interval <= (settings.DETAIL_2_INTERVAL + settings.DETAIL_DURATION)):
			#print "CPU %2d%% %2d%% %2d%% %2d%%" % (data.cpu_percent[0], data.cpu_percent[1], data.cpu_percent[2], data.cpu_percent[3])
			print "CPU %2d%%  %sMHz" % (data.cpu_percent_avg, data.cpu_mhz)
			print "LOAD %s %s %s" % (data.load_average[0], data.load_average[1], data.load_average[2])
			return
			
		# Are we showing alternate screen 3?
		# Show CPU
		# Show Disk space
		if (interval >= settings.DETAIL_3_INTERVAL) and (interval <= (settings.DETAIL_3_INTERVAL + settings.DETAIL_DURATION)):
			#print "CPU %2d%% %2d%% %2d%% %2d%%" % (data.cpu_percent[0], data.cpu_percent[1], data.cpu_percent[2], data.cpu_percent[3])
			print "CPU %2d%%  %sMHz" % (data.cpu_percent_avg, data.cpu_mhz)
			print "DSK %s / %s" % (self.sizeof_fmt(num = data.disk_space), self.sizeof_fmt(num = data.disk_size))
			return
		
		# Are we showing alternate screen 4?
		# Show CPU
		# Show RAM use
		if (interval == settings.DETAIL_4_INTERVAL):
			#print "CPU %2d%% %2d%% %2d%% %2d%%" % (data.cpu_percent[0], data.cpu_percent[1], data.cpu_percent[2], data.cpu_percent[3])
			print "CPU %2d%%  %sMHz" % (data.cpu_percent_avg, data.cpu_mhz)
			display_text = ""
			t = "RAM %s of %s used, %s buffers." % (self.sizeof_fmt(num = data.ram_avail), self.sizeof_fmt(num = data.ram_total), (self.sizeof_fmt(num = data.ram_buffer)))
			for i in range (0, len(t)):  
				print "CPU %2d%%  %sMHz" % (data.cpu_percent_avg, data.cpu_mhz)
				display_text = t[i:(i+settings.DISPLAY_WIDTH)]  
				print display_text
				if i == 0:
					# Pause the first time we show the beginning of the error.
					time.sleep(1)
				else:
					# Slight gap between each character scrolling by.
					time.sleep(settings.SCROLL_SPEED)
			return
		
		# Show the normal screen
		# Show CPU
		# Show total network and disk IO
		#print "CPU %2d%% %2d%% %2d%% %2d%%" % (data.cpu_percent[0], data.cpu_percent[1], data.cpu_percent[2], data.cpu_percent[3])
		print "CPU %2d%%  %sMHz" % (data.cpu_percent_avg, data.cpu_mhz)
		print "IO %s/s %s/s" % (self.sizeof_fmt(num = data.stats_bandwidth["net_bytes_rw_bw"]), self.sizeof_fmt(num = data.stats_bandwidth["disk_bytes_rw_bw"]))
		return		
		
	def run(self):
		""" Main loop that calls the appropriate output module. """
		
		# Instantiate a new copy of the data acquisition class
		d = data()
		# Do the first collection of system metrics
		d.update(services = True)
		c = 0 
		interval = 0
		while True:
			c += 1
			interval += 1
			
			# Collect system metrics
			if interval == settings.SERVICE_INTERVAL:
				# Service metrics take longer, so we only
				# collected them at certain intervals
				d.update(services = True)
			else:
				# Grab all the normal system metrics
				# unless we had errors last time, so
				# check for errors again.
				if self.errors:
					d.zap()
					d.update(services = True)
				else:
					d.update()
			
			# Call the correct output module
			if self.mode == "terminal":
				self.terminal_overview(d, interval)
			elif self.mode == "lcd":
				self.lcd_overview(d, interval)
			else:
				self.terminal_overview(d, interval)
			
			# Reset the display loop after showing the last alternate screen
			if interval >= (settings.DETAIL_4_INTERVAL):
				interval = 0
				
			# Wait until we're supposed to wake up next
			time.sleep(settings.INTERVAL)
						
			# Zap counters to prevent overflows or
			# memory growth after every N loops
			if c == settings.ZAP_INTERVAL:
				d.zap()
				c = 0	
				d.update(services = True)