#!/usr/bin/env python
# -*- coding: utf-8 -*-

#
import sys, traceback
import datetime
import numpy as np
import matplotlib
# Force matplotlib to not use any Xwindows backend.
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import numpy as np

import time
import RPi.GPIO as GPIO
import RPi.GPIO as GPIO2
import RPi.GPIO as GPIO3
import RPi.GPIO as GPIO4


from subprocess import call

# define GPIO pins for ultraschall
GPIOTrigger = 17 # I
GPIOEcho    = 18 # I
GPIO2Trigger = 22 # II
GPIO2Echo    = 23 # II
GPIO3Trigger = 24 # III
GPIO3Echo    = 25 # III



max_oel = 620
minutes = mdates.MinuteLocator()   # every hour
hours = mdates.HourLocator()   # every hour
years = mdates.YearLocator()   # every year
months = mdates.MonthLocator()  # every month
#yearsFmt = mdates.DateFormatter('%Y')
yearsFmt = mdates.DateFormatter('%Y-%m-%d_%H:%M')
##################################################################################
# The wiring for the LCD is as follows:
# 1 : GND
# 2 : 5V
# 3 : Contrast (0-5V)*
# 4 : RS (Register Select)
# 5 : R/W (Read Write)       - GROUND THIS PIN
# 6 : Enable or Strobe
# 7 : Data Bit 0             - NOT USED
# 8 : Data Bit 1             - NOT USED
# 9 : Data Bit 2             - NOT USED
# 10: Data Bit 3             - NOT USED
# 11: Data Bit 4
# 12: Data Bit 5
# 13: Data Bit 6
# 14: Data Bit 7
# 15: LCD Backlight +5V**
# 16: LCD Backlight GND

# Define GPIO to LCD mapping
LCD_RS = 5
LCD_E  = 12
LCD_D4 = 6
LCD_D5 = 13
LCD_D6 = 19
LCD_D7 = 26


# Define some device constants
LCD_WIDTH = 20    # Maximum characters per line
LCD_CHR = True
LCD_CMD = False

LCD_LINE_1 = 0x80 # LCD RAM address for the 1st line
LCD_LINE_2 = 0xC0 # LCD RAM address for the 2nd line
LCD_LINE_3 = 0x94 # LCD RAM address for the 3rd line
LCD_LINE_4 = 0xD4 # LCD RAM address for the 4th line

# Timing constants
E_PULSE = 0.0005
E_DELAY = 0.0005

#####################################################################
def lcd_init():
  # Initialise display
  lcd_byte(0x33,LCD_CMD) # 110011 Initialise
  lcd_byte(0x32,LCD_CMD) # 110010 Initialise
  lcd_byte(0x06,LCD_CMD) # 000110 Cursor move direction
  lcd_byte(0x0C,LCD_CMD) # 001100 Display On,Cursor Off, Blink Off
  lcd_byte(0x28,LCD_CMD) # 101000 Data length, number of lines, font size
  lcd_byte(0x01,LCD_CMD) # 000001 Clear display
  time.sleep(E_DELAY)

def lcd_byte(bits, mode):
  # Send byte to data pins
  # bits = data
  # mode = True  for character
  #        False for command

  GPIO3.output(LCD_RS, mode) # RS

  # High bits
  GPIO3.output(LCD_D4, False)
  GPIO3.output(LCD_D5, False)
  GPIO3.output(LCD_D6, False)
  GPIO3.output(LCD_D7, False)
  if bits&0x10==0x10:
    GPIO3.output(LCD_D4, True)
  if bits&0x20==0x20:
    GPIO3.output(LCD_D5, True)
  if bits&0x40==0x40:
    GPIO3.output(LCD_D6, True)
  if bits&0x80==0x80:
    GPIO3.output(LCD_D7, True)

  # Toggle 'Enable' pin
  lcd_toggle_enable()

  # Low bits
  GPIO3.output(LCD_D4, False)
  GPIO3.output(LCD_D5, False)
  GPIO3.output(LCD_D6, False)
  GPIO3.output(LCD_D7, False)
  if bits&0x01==0x01:
    GPIO3.output(LCD_D4, True)
  if bits&0x02==0x02:
    GPIO3.output(LCD_D5, True)
  if bits&0x04==0x04:
    GPIO3.output(LCD_D6, True)
  if bits&0x08==0x08:
    GPIO3.output(LCD_D7, True)

  # Toggle 'Enable' pin
  lcd_toggle_enable()

def lcd_toggle_enable():
  # Toggle enable
  time.sleep(E_DELAY)
  GPIO3.output(LCD_E, True)
  time.sleep(E_PULSE)
  GPIO3.output(LCD_E, False)
  time.sleep(E_DELAY)

def lcd_string(message,line,style):
  # Send string to display
  # style=1 Left justified
  # style=2 Centred
  # style=3 Right justified

  if style==1:
    message = message.ljust(LCD_WIDTH," ")
  elif style==2:
    message = message.center(LCD_WIDTH," ")
  elif style==3:
    message = message.rjust(LCD_WIDTH," ")

  lcd_byte(line, LCD_CMD)

  for i in range(LCD_WIDTH):
    lcd_byte(ord(message[i]),LCD_CHR)

#############################################
def tail_last_lines():
  cmd = ["tail","-n300","/home/pi/datafile.csv"]
  fh = open("/home/pi/datafile_last_week.csv","wb")
  call(cmd,stdout=fh)
  fh.close()


def render_data(datasource_file, picture_dest_file_and_path, titel):
  print("##### Render started:", datasource_file)
  days, oel_t1 = np.loadtxt(datasource_file, delimiter = ';', usecols=(0,3),unpack=True, converters={ 0: mdates.strpdate2num('%Y-%m-%d_%H:%M')})
  days, oel_t2 = np.loadtxt(datasource_file, delimiter = ';', usecols=(0,6),unpack=True, converters={ 0: mdates.strpdate2num('%Y-%m-%d_%H:%M')})
  days, oel_t3 = np.loadtxt(datasource_file, delimiter = ';', usecols=(0,9),unpack=True, converters={ 0: mdates.strpdate2num('%Y-%m-%d_%H:%M')})
  days, temp_s1= np.loadtxt(datasource_file, delimiter = ';', usecols=(0,10),unpack=True, converters={ 0: mdates.strpdate2num('%Y-%m-%d_%H:%M')})

  ####### GRAPH 1 ############# 
  plt.clf()
  plt.subplot(2, 1, 1)
  plt.subplots_adjust(hspace = .001)
  plt.tick_params(labelright=True, labelbottom=False)
  plt.plot_date(x=days, y=oel_t1, fmt="r-")
  plt.plot_date(x=days, y=oel_t2, fmt="g-")
  plt.plot_date(x=days, y=oel_t3, fmt="b-")

  axes = plt.gca()
  axes.set_ylim([0,800])
  plt.yticks([0, 50, 100, 150, 200, 250, 300, 350, 400, 450, 500, 550, 600, 650, 700, 750, 800])
  plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d_%H:%M'))
  plt.gca().xaxis.set_major_locator(mdates.DayLocator())
  #plt.gca().xaxis.set_major_locator(mdates.MonthLocator())

  plt.xticks(rotation=90)
  plt.tick_params(axis='both', which='major', labelsize=5)
  plt.title(titel)
  plt.xlabel('Zeit')
  plt.ylabel('Liter')
  plt.grid(b=True, linestyle='-', which='both', linewidth=1, alpha=0.3)
  
  ####### GRAPH 2 ############# 
  plt.subplot(2, 1, 2)
  plt.subplots_adjust(hspace = .001)
  plt.tick_params(labelright=True)
  plt.xticks(rotation=90)
  plt.tick_params(axis='both', which='major', labelsize=5)
  plt.plot_date(x=days, y=temp_s1, fmt="r-")
  plt.yticks([-10, -5, 0, 5, 10, 15, 20, 25, 30, 35, 40, 45])
  plt.grid(b=True, linestyle='-', which='both', linewidth=1, alpha=0.3)
  plt.xlabel('Zeit')
  plt.ylabel('Temp.')
  plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d_%H:%M'))
  plt.gca().xaxis.set_major_locator(mdates.DayLocator())
 

#  plt.savefig(picture_dest_file_and_path, dpi=150)   # save the figure to file
 # plt.close(plt)    # close the figure
  print("##### Render completed")
############################################
def update_display(t1_cm, t2_cm, t3_cm, t1_liter, t2_liter, t3_liter):
  lcd_init()
  zeit = ""
  zeit = time.strftime("%Y-%m-%d_%H:%M")
  lcd_string(str(zeit),LCD_LINE_1,1)
#  lcd_string("TANK 1 (cm):"+str(t1_cm),LCD_LINE_2,1)
  lcd_string("TANK 1 (L ):"+str(t1_liter),LCD_LINE_2,1)
#  lcd_string("TANK 2 (cm):"+str(t2_cm),LCD_LINE_3,1)
  lcd_string("TANK 2 (L ):"+str(t2_liter),LCD_LINE_3,1)
#  lcd_string("TANK 3 (cm):"+str(t3_cm),LCD_LINE_4,1)
  lcd_string("TANK 3 (L ):"+str(t3_liter),LCD_LINE_4,1)

#  lcd_string("   TANK 1 |  TANK 2",LCD_LINE_2,1)
#  lcd_string("CM: "+'{:<5}'.format(str(t1_cm))   +" |  "+ str(t2_cm) ,LCD_LINE_3,1)
#  lcd_string("L : "+'{:<5}'.format(str(t1_liter))+" |  "+ str(t2_liter) ,LCD_LINE_4,1)



############################################
def update_webserver(t1_cm, t2_cm, t3_cm, t1_liter, t2_liter, t3_liter):
  zeit = time.strftime("%Y-%m-%d_%H:%M")
  line0 = str(zeit) + "\n"
  line1 = "   TANK 1 |  TANK 2\n"
  line2 = "CM: "+'{:<5}'.format(str(t1_cm))   +" |  "+ str(t2_cm) + "\n"
  line3 = "L : "+'{:<5}'.format(str(t1_liter))+" |  "+ str(t2_liter) + "\n"
  line1 = "Tank 1: L : "+'{:<5}'.format(str(t1_liter))+"\n"
  line2 = "Tank 1:CM : "+'{:<5}'.format(str(t1_cm))+"\n"
  line3 = "Tank 2: L : "+'{:<5}'.format(str(t2_liter))+"\n"
  line4 = "Tank 2:CM : "+'{:<5}'.format(str(t2_cm))+"\n"
  line5 = "Tank 3: L : "+'{:<5}'.format(str(t3_liter))+"\n"
  line6 = "Tank 3:CM : "+'{:<5}'.format(str(t3_cm))+"\n"
  text = line0 + line1 +line2 + line3 + line4 + line5 +line6
  return text

############################################
def get_temperatur_s1():
        tempfile = open("/sys/bus/w1/devices/28-000005da3366/w1_slave")
        thetext =  tempfile.read()
        tempfile.close()
        #tempdata = thetext.split("\n")[1].split(" ")[9] 
        tempdata = thetext.split("\n")[1].split("=")[1]
	#print "tempdate:" + tempdata
        #temperatur = float(tempdata[2:])
        temperatur = float(tempdata)
        temperatur = temperatur / 1000
        print "temperatur:" + str(temperatur)
        return temperatur

# function to measure the distance
def MeasureDistance():
  # set trigger to high
  GPIO.output(GPIOTrigger, True)
  
  # set trigger after 10µs to low
  time.sleep(0.00001)
  GPIO.output(GPIOTrigger, False)

  # store initial start time
  StartTime = time.time()
  StopTime = time.time()

  # store start time
  while GPIO.input(GPIOEcho) == 0:
    StartTime = time.time()

  # store stop time
  while GPIO.input(GPIOEcho) == 1:
    StopTime = time.time()

  # calculate distance
  TimeElapsed = StopTime - StartTime
  Distance = (TimeElapsed * 34300) / 2

  print("%.2f" % (Distance))
  return Distance
##################################
def MeasureDistance2():
  # set trigger to high
  GPIO2.output(GPIO2Trigger, True)
  
  # set trigger after 10µs to low
  time.sleep(0.00001)
  GPIO2.output(GPIO2Trigger, False)

  # store initial start time
  StartTime = time.time()
  StopTime = time.time()

  # store start time
  while GPIO2.input(GPIO2Echo) == 0:
    StartTime = time.time()

  # store stop time
  while GPIO2.input(GPIO2Echo) == 1:
    StopTime = time.time()

  # calculate distance
  TimeElapsed = StopTime - StartTime
  Distance = (TimeElapsed * 34300) / 2

  print("%.2f" % (Distance))
  return Distance

########################################
def MeasureDistance3():
  # set trigger to high
  GPIO4.output(GPIO3Trigger, True)
  
  # set trigger after 10µs to low
  time.sleep(0.00001)
  GPIO4.output(GPIO3Trigger, False)

  # store initial start time
  StartTime = time.time()
  StopTime = time.time()

  # store start time
  while GPIO4.input(GPIO3Echo) == 0:
    StartTime = time.time()

  # store stop time
  while GPIO4.input(GPIO3Echo) == 1:
    StopTime = time.time()

  # calculate distance
  TimeElapsed = StopTime - StartTime
  Distance = (TimeElapsed * 34300) / 2

  print("%.2f" % (Distance))
  return Distance

########################################
# main function
def main():
  print("Start.")
  zeit = time.strftime("%Y-%m-%d_%H:%M")
  try:
    print("Messe Sensor 1")
    Distance = 0
    Distance = MeasureDistance()
    print("1 Fertig")
    time.sleep(1)
    print("Messe Sensor 2")
    Distance2 = 0
    Distance2 = MeasureDistance2()
    print("2 Fertig")
    print("Messe Sensor 3")
    Distance3 = 0
    Distance3 = MeasureDistance3()
    print("3 Fertig")
    print("Messe Temp. Sensor S1")
    temperatur = 0
    temperatur = get_temperatur_s1()
    print("--- Messungen Fertig ---")
    # Ziehe ca. 4 cm ab, da der Tank nicht ganz voll befuellt werden kann
    ##Distance = Distance -4
    # 6 dezimeter * 8 dezimeter * hoehe/10
    # da 10*10*10 dezimeter = 1 Liter
    # Max 620 Liter 
    # Volumen = 
    LuftVolumen = 7 * 9 * round(Distance,0) * 0.1
    LuftVolumen2 = 7 * 9 * round(Distance2,0) * 0.1
    LuftVolumen3 = 7.5 * 7.5 * round(Distance3,0) * 0.1

    OelVolumen = 662 - LuftVolumen
    OelVolumen2 = 662 - LuftVolumen2
    OelVolumen3 = 760 - LuftVolumen3
    liter = LuftVolumen - 620
    zeit = ""
    zeit = time.strftime("%Y-%m-%d_%H:%M")
    
    print("messe:Zeit,\t\t        Distanz,     Oelvolumen in Liter, Temp.")
    print("t1    %s\t\t%3.2f         %3.0f\t\t   %3.0f" % (zeit, Distance, OelVolumen, temperatur))
    print("t2    %s\t\t%3.2f         %3.0f\t\t   %3.0f" % (zeit, Distance2, OelVolumen2, temperatur))
    print("t3    %s\t\t%3.2f         %3.0f\t\t   %3.0f" % (zeit, Distance3, OelVolumen3, temperatur))
    datastring = "%s;%.2f;%.0f;%.0f;%.2f;%.0f;%.0f;%.2f;%.0f;%.0f;%.1f\n" % (zeit, Distance, LuftVolumen, OelVolumen,Distance2, LuftVolumen2, OelVolumen2, Distance3, LuftVolumen3, OelVolumen3, temperatur)
    update_display(round(Distance,1), round(Distance2,1), round(Distance3,1), round(OelVolumen,1), round(OelVolumen2,1), round (OelVolumen3,1))
    print("Display Updated")
    text = update_webserver(round(Distance,1), round(Distance2,1), round(Distance3,1), round(OelVolumen,1), round(OelVolumen2,1), round(OelVolumen3,1))
    print("Webserver Updated")
    fh = open('/home/pi/datafile.csv', 'a')
    fh.write(datastring)
    fh.close
    print("Saved to file.")
    #fh2 = open('/var/www/html/stand.txt', 'w')
    #fh2.write(text)
    #fh2.close
    tail_last_lines()
    render_data("/home/pi/datafile_last_week.csv","/var/www/html/stand7.png","Oelstand letzten 7 Tage")
    render_data("/home/pi/datafile.csv","/var/www/html/stand.png", "Oelstand gesamt")
    print("Render complete.")

    #time.sleep(600)

  except Exception as e:
    errorlog = '/home/pi/error.log'
    error_string = "Exception on %s . Measurement stopped. Testprog. \n" % (zeit)
    fh3 = open(errorlog, 'a')
    fh3.write(error_string)
    fh3.write('-'*60 + '\n')
    traceback.print_exc(file=fh3)
    fh3.write('-'*60 + '\n')
    fh3.close

    GPIO.cleanup()
    #GPIO2.cleanup()

def sensortest():
#  lcd_string("Teste Sensor 1...",LCD_LINE_1,1)
  lcd_string("Sensor Test...",LCD_LINE_1,1)
  Distance = MeasureDistance()
  lcd_string("T1: "+str(round(Distance,1))+" CM = OK" ,LCD_LINE_2,1)
  time.sleep(1)
#  lcd_string("Teste Sensor 2...",LCD_LINE_3,1)
  Distance2 = MeasureDistance2()
  lcd_string("T2: "+str(round(Distance2,1))+" CM = OK" ,LCD_LINE_3,1)
  time.sleep(1)
  Distance3 = MeasureDistance3()
  lcd_string("T3: "+str(round(Distance3,1))+" CM = OK" ,LCD_LINE_4,1)
  time.sleep(5)
       

if __name__ == '__main__':
  while True:
    GPIO.cleanup()
    # use GPIO pin numbering convention
    GPIO.setmode(GPIO.BCM)
    GPIO2.setmode(GPIO2.BCM)
    GPIO3.setmode(GPIO3.BCM)
    GPIO4.setmode(GPIO3.BCM)
    # Main program block

    GPIO3.setmode(GPIO3.BCM)       # Use BCM GPIO numbers
    GPIO3.setup(LCD_E, GPIO3.OUT)  # E
    GPIO3.setup(LCD_RS, GPIO3.OUT) # RS
    GPIO3.setup(LCD_D4, GPIO3.OUT) # DB4
    GPIO3.setup(LCD_D5, GPIO3.OUT) # DB5
    GPIO3.setup(LCD_D6, GPIO3.OUT) # DB6
    GPIO3.setup(LCD_D7, GPIO3.OUT) # DB7

    # Initialise display
    lcd_init()


    # set up GPIO pins
  #  lcd_string("Teste Sensor 1...",LCD_LINE_1,1)
    GPIO.setup(GPIOTrigger, GPIO.OUT)
    GPIO.setup(GPIOEcho, GPIO.IN)
   # lcd_string("OK",LCD_LINE_2,1)
  
    #lcd_string("Teste Sensor 2...",LCD_LINE_3,1)
    GPIO2.setup(GPIO2Trigger, GPIO2.OUT)
    GPIO2.setup(GPIO2Echo, GPIO2.IN)
   # lcd_string("OK",LCD_LINE_4,1)
    GPIO4.setup(GPIO3Trigger, GPIO4.OUT)
    GPIO4.setup(GPIO3Echo, GPIO4.IN)
  
    # set trigger to false
    lcd_string("Starte Programm",LCD_LINE_1,1)
    lcd_string("",LCD_LINE_2,1)
    lcd_string("",LCD_LINE_3,1)
    lcd_string("",LCD_LINE_4,1)
    #GPIO2.output(GPIO2Trigger, False)
  
    # call main function
    #try:
    sensortest()
    main()
    print("Run complete.")
    time.sleep(1800)
   #except KeyboardInterrupt:
    #  pass
    #finally:
    #  lcd_byte(0x01, LCD_CMD)
    #  lcd_string("Program beendet!",LCD_LINE_1,2)
    #  lcd_string("Tschuldigung!",LCD_LINE_2,2)
