#!/usr/bin/python
# -*- encoding: utf-8 -*-

import time
import RPi.GPIO as GPIO
import datetime           # jen kvuli zaznamu trvani tisku

GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)

bod={}              # pamet pro obrazek ulozeny v souboru
font={}             # pamet, ve ktere je ulozeny font, nacetny z externiho souboru


# Samotna tiskarna ma vstupy aktivni v "0"
# Motory a jehla se ale aktivuji odeslanim logicke "1" na GPIO port.
# Tato "1" je pak tranzistorovym spinacem v interfejsu invertovana na "0".
# Logika cidel je nasledujici:
# - kdyz je vozik na zacatku drahy, je na vystupu tiskarny "1"; uprostred drahy je na vystupu "0" 
# - zarezy na kolech zpusobuji preklopeni vystupu tiskarny do "0"; pokud pred cidlem zarez neni, je na vystupu "1"


# prirazeni GPIO portu k cidlum a motorum
cidlo_vozik = 7     # cidlo jemneho posunu voziku  
cidlo_doraz = 8     # cidlo zacatku drahy voziku
cidlo_papir = 25    # cidlo posunu papiru

motor_zacatek = 17  # posun voziku k zacatku drahy (k cidlu) 
motor_konec   = 18  # posun voziku ke konci drahy (strana bez cidla) 
motor_papir   = 23  # spusteni motoru pro posun papiru
civka_jehla   = 24  # ovladani jehly

# nastaveni smeru GPIO
GPIO.setup(cidlo_vozik, GPIO.IN, pull_up_down=GPIO.PUD_UP)    
GPIO.setup(cidlo_doraz, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(cidlo_papir, GPIO.IN, pull_up_down=GPIO.PUD_UP)   

GPIO.setup(motor_zacatek, GPIO.OUT)                            
GPIO.setup(motor_konec  , GPIO.OUT)                            
GPIO.setup(motor_papir  , GPIO.OUT)                             
GPIO.setup(civka_jehla  , GPIO.OUT)                             

# uvodni nastaveni vsech signalu do "0" (zastaveni vsech motoru)
GPIO.output(motor_papir, False)
GPIO.output(motor_konec, False)
GPIO.output(motor_zacatek, False)
GPIO.output(civka_jehla, False)
time.sleep(1)     


#========================================================================
# najeti voziku na zacatek drahy (na stranu s cidlem)
def nazacatek():
  time.sleep (0.01)  # pauza, aby se stacily srovnat setrvacne sily v posunu hlavicky, nez se motor roztoci na druhou stranu
  GPIO.output(motor_zacatek, True)
  print "Rozjezd voziku smerem k zacatku drahy"
  while (GPIO.input(cidlo_doraz) == False):
    pass

  # vozik zajede jeste kousek za opticky doraz
  #  to je kvuli nasledne synchronizaci zacatku tisku, ktery zacina okamzikem odjeti hlavicky od dorazu
  # sice by se o to zajeti za doraz mela postarat setrvacnost, ale pro jistotu jsem tam chvilkovou pauzu zaradil
  time.sleep(0.05)     

  GPIO.output(motor_zacatek, False)  # vypnuti motoru posunu voziku
  print "Vozik je na zacatku drahy"

  time.sleep (0.01)  # na chvilku pauza, aby se stacily srovnat setrvacne sily v posunu hlavicky



#========================================================================
# posun papiru o 1 mikroradek
def papir():
  GPIO.output(motor_papir, True)        # spusteni motoru pro posun papiru
  print "START motoru pro posun papiru"
  while (GPIO.input(cidlo_papir) == True):
    pass
  while (GPIO.input(cidlo_papir) == False):
    pass
  GPIO.output(motor_papir, False)        # vypnuti motoru pro posun papiru
  print "Motor posunu papiru byl zastaven"





#========================================================================
# tisk jedne mikroradky a najeti hlavicky zpatky na zacatek
# maxsirka je pro obrazky vzdy 480 bodu. Pro text je to 8 bodu x pocet znaku
def mikrotisk(mikroradka, maxsirka = 480):

  print "tiskne se mikroradka c. " , mikroradka
  if (maxsirka > 480): maxsirka = 480      # pokud je sirka tisku textu moc velka, zbytek se orizne  

  GPIO.output(motor_konec, True)   # roztoceni motoru s posunem hlavicky smerem od cidla
  print "motor hlavicky je roztocen ve smeru tisku - zacina tisk" 


  while (GPIO.input(cidlo_doraz) == True):  # synchronizace zacatku radky v okamziku, kdy vozik opousti doraz
   pass  
  
  
  mikrosloupec=0
  while (mikrosloupec < maxsirka):


    # test posunu voziku o 1 mikrosloupec  
    # tohle je asi nejproblemovejsi test - stridani impulzu je tak rychle, ze to program obcas nestiha vyhodnotit
    # GPIO Polling (cekani na sestupnou nebo nabeznou hranu) to nestiha uz vubec

    while (GPIO.input(cidlo_vozik) == True):  
      pass
    while (GPIO.input(cidlo_vozik) == False):
      pass


    # test obrazovych dat jednoho mikradku
    if (bod[mikrosloupec,mikroradka] == 1):
      GPIO.output(civka_jehla, True)        # kdyz bod na prislusnem mikrosloupci existuje, klepne se jehlou
      GPIO.output(civka_jehla, False)
    else:
      GPIO.output(civka_jehla, False)       # tohle s jehlou nic nedela, je to jen kvuli 
      GPIO.output(civka_jehla, False)       #   casove symetrii obou vetvi podminky


    mikrosloupec = mikrosloupec + 1


  GPIO.output(motor_konec, False)   # zastaveni motoru s posunem hlavicky smerem od cidla
  print "motor a tisk se zastavil na konci radky"
  nazacatek()              # najeti hlavicky na zacatek drahy



#========================================================================
# nacteni obrazkoveho souboru do dvourozmerne promenne "bod[]"
# tuhle cast jsem si vypujcil z clanku o ovladani grafickych displeju
def nactidata (jmeno):
  print "nacitam data ze souboru: " , jmeno

  soubor=open(jmeno, "rb")          # otevreni souboru s obrazkem a nacteni obsahu do pameti
  data = soubor.read()  
  soubor.close()                    # uzavreni souboru

  # zjisteni zacatku obrazovych dat v souboru (viz specifikace formatu souboru BMP)
  zacatekdat = ord(data[10]) + (ord(data[11]) * 256) + (ord(data[12]) * 65536) + (ord(data[13]) * 16777216)

  # pocet mikroradek v obrazku (viz specifikace formatu souboru BMP)
  vyska_obrazku = ord(data[22]) + (ord(data[23]) * 256) + (ord(data[24]) * 65536) + (ord(data[25]) * 16777216)

  bajt=zacatekdat
  for r in range (vyska_obrazku-1 , -1 , -1):  # cteni promenne data[] bajt po bajtu a prevod na souradnice superx,supery
    for s in range (60):       # sirka obrazku je pevne nastavena na 480 bodu (to je 8 x 60 bajtu v BMP obrazku)
      for b in range(8):
        superx = (s*8) + b     # s = bajt v osmicich bitu (0 az 59) , b = bit v kazdem bajtu 0 az 7
        supery = r             # r = aktualni mikroradka
        maska = (1 << (7-b))   
        if (ord(data[bajt]) & maska != 0):   # dvoubarevna windowsovska bitmapa ma opacne nastavene bity nez tiskarna:
          bod[superx,supery] = 0          # jednickove bity jsou bile body
        else:
          bod[superx,supery] = 1          # nulove bity jsou cerne body

      bajt = bajt +1          # prejde na dalsi bajt z grafickych dat

  return vyska_obrazku

#========================================================================
# nacteni fontu ze souboru do seznamu "font[]"
# i tohle jsem si vypujcil z clanku o grafickych displejich
def nacist_font(jmenosouboru):
  fontfile=file(jmenosouboru,"r")
  adresafontu=0
  for radka in fontfile:
    rozlozeno = radka.split(",")                   # vysosani jednotlivych bajtu z jedne radky ...
    for bajt in range(8):   # 8 bajtu na radce
      font[adresafontu] = int(rozlozeno[bajt][-4:],0) # ... a ulozeni kazdeho toho bajtu do seznamu
      adresafontu = adresafontu + 1
  fontfile.close()

# zobrazeni jednoho znaku z vetsiho fontu
def bajtznaku(kod,mikroradka):  
  kod  = kod-32
  adrf = kod*8 + mikroradka
  return font[adrf]




#========================================================================
# podprogram pro tisk textu
def tisktextu (napis):

  maxsirka = 8 * len(napis)

  # rozklad textu na jednotlive mikroradky
  # z fontu kazdeho pismena se vybira prislusna mikroradka a tyto mikroradky se slozi za sebe do promenne bod[]
  # vysledna mikroradka se pak vytiskne
  
  for mikroradka in range (8):
    for znak in range (len(napis)):
      kod=ord(napis[znak:znak+1])
      bajt=bajtznaku(kod,mikroradka)
      for bit in range (8):
        mikrosloupec = 8*znak + bit

        if (bajt & pow(2,(7-bit)) != 0):
          bod[mikrosloupec,mikroradka] = 1   # bod je
        else:
          bod[mikrosloupec,mikroradka] = 0   # bod neni

    papir()                         # posun papiru
    mikrotisk(mikroradka,maxsirka)  # vlastni tisk jedne mikroradky



#========================================================================
# zacatek hlavniho programu

nacist_font("/home/pi/font2.txt")

nazacatek()   # najeti voziku na uvodni pozici - k dorazu

datcas = datetime.datetime.now()          # zjisteni casu startu tisku
starttisku = str(datcas.hour) + ":" + str(datcas.minute).rjust(2,"0")
tisktextu("start tisku: " + starttisku)   # tisk textu v textovem rezimu

for r in range(10):  # odradkovani o 10 prazdnych mikroradek
  papir()  


# tisk obrazku
vyska_obrazku = nactidata("/home/pi/obr480x400.bmp")  # nacteni obrazovych dat z bitmapoveho souboru se sirkou 480 bodu
for r in range (vyska_obrazku):  # pocet mikroradek, ktere se maji tisknout (vyska obrazku)
  papir()        # natazeni papiru o jednu mikroradku
  mikrotisk(r)   # vytisteni jedne mikroradky

for r in range(10):  # odradkovani o 10 prazdnych mikroradek
  papir()  


datcas = datetime.datetime.now()           # zjisteni casu konce tisku
stoptisku = str(datcas.hour) + ":" + str(datcas.minute).rjust(2,"0")
tisktextu("tisk ukoncen: " + stoptisku)    # tisk textu v textovem rezimu







  
  
  