#!/usr/bin/env python
# -*- coding: iso-8859-15 -*-

# Description: Example of use of libIris
# Copyright (C) 2007 by Juan Gonzalez and Rafael Treviño Menéndez
# Author: Juan Gonzalez <juan@iearobotics.com>
#         Rafael Treviño Menéndez <skasi.7@gmail.com>

# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Library General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

import sys
import os
import pygtk
import gtk
import gtk.glade
import gnomevfs
import gobject
import time

sys.path = ['..'] + sys.path

#---------------------------
#-- Modulos de LibIris
#---------------------------
import libStargate.Picp
import libIris.Pic16_Bootloader
import libIris.Pic16_Firmware
import libIris.IntelHex

#-- Bootloader. Obtenido con:
#-- hex2python PIC16_bootloader_1.2.hex

bootloader=[[0x0000, 0x0000, 0x158A, 0x160A, 0x2E88],[0x1E80, 0x158A, 0x160A, 0x2E80, 0x018A, 0x158A, 0x160A, 0x2E83, 0x0000, 0x1E03, 0x2E83, 0x1683, 0x3087, 0x0086, 0x1283, 0x0186, 0x3090, 0x0098, 0x1683, 0x1518, 0x1698, 0x301F, 0x0099, 0x1283, 0x3003, 0x00FA, 0x3031, 0x0090, 0x140C, 0x26F1, 0x3AEA, 0x1D03, 0x2EB4, 0x01FA, 0x2EBB, 0x26F1, 0x00F9, 0x3AE3, 0x1903, 0x2EBD, 0x0879, 0x3AEA, 0x1903, 0x2EBB, 0x0879, 0x3AED, 0x1D03, 0x2EA2, 0x30E4, 0x26EC, 0x3003, 0x00FA, 0x26F1, 0x0190, 0x0198, 0x1683, 0x0198, 0x1283, 0x018C, 0x2E83, 0x30EB, 0x2EEA, 0x26F1, 0x00F6, 0x26F1, 0x00F5, 0x26F1, 0x00F1, 0x00FB, 0x26F1, 0x00F3, 0x01F2, 0x3021, 0x1283, 0x1303, 0x0276, 0x1903, 0x2ED6, 0x0875, 0x3903, 0x00FC, 0x00FD, 0x1003, 0x0DFC, 0x3020, 0x077C, 0x2ED7, 0x3020, 0x0084, 0x26F1, 0x0080, 0x07F2, 0x0A84, 0x0BFB, 0x2ED8, 0x0872, 0x0673, 0x30E8, 0x1D03, 0x2EEA, 0x30E7, 0x26EC, 0x2703, 0x3800, 0x30E4, 0x1903, 0x30E5, 0x26EC, 0x2EA2, 0x0064, 0x1E0C, 0x2EEC, 0x0099, 0x0008, 0x0064, 0x087A, 0x1903, 0x2EFF, 0x1C0C, 0x2EFF, 0x1010, 0x0BFA, 0x2EFB, 0x3400, 0x100C, 0x300B, 0x008F, 0x1410, 0x1E8C, 0x2EF1, 0x081A, 0x0008, 0x3021, 0x0276, 0x1903, 0x2F55, 0x1683, 0x1703, 0x178C, 0x1283, 0x1303, 0x0875, 0x39FC, 0x1703, 0x008D, 0x1303, 0x0876, 0x1703, 0x008F, 0x3020, 0x0084, 0x1303, 0x087D, 0x1903, 0x2F2D, 0x1683, 0x1703, 0x140C, 0x0000, 0x0000, 0x1283, 0x080E, 0x0080, 0x0A84, 0x080C, 0x0080, 0x0A84, 0x0A8D, 0x1303, 0x03FD, 0x03F5, 0x0AF1, 0x0AF1, 0x2F16, 0x0871, 0x00FC, 0x3E20, 0x0084, 0x1003, 0x0CFC, 0x0876, 0x1703, 0x008F, 0x1303, 0x0875, 0x1703, 0x008D, 0x1303, 0x087C, 0x1703, 0x078D, 0x1903, 0x0A8F, 0x1703, 0x080D, 0x3903, 0x1903, 0x2F55, 0x1683, 0x140C, 0x0000, 0x0000, 0x1283, 0x080E, 0x0080, 0x0A84, 0x080C, 0x0080, 0x0A84, 0x0A8D, 0x1303, 0x0AF1, 0x0AF1, 0x2F40, 0x0875, 0x00F7, 0x0876, 0x00F8, 0x01FB, 0x3002, 0x00F4, 0x0871, 0x027B, 0x1803, 0x3401, 0x087B, 0x3E20, 0x0084, 0x3021, 0x0278, 0x1703, 0x1683, 0x1903, 0x2F73, 0x178C, 0x0183, 0x301E, 0x0278, 0x3080, 0x1903, 0x0277, 0x1803, 0x2FBB, 0x2F75, 0x138C, 0x0183, 0x0877, 0x1703, 0x008D, 0x1303, 0x0878, 0x1D03, 0x2F8A, 0x3004, 0x0277, 0x1803, 0x2F8A, 0x1703, 0x1683, 0x1F8C, 0x2F87, 0x1283, 0x3084, 0x078D, 0x0183, 0x301E, 0x2F8B, 0x0878, 0x1703, 0x008F, 0x0800, 0x008E, 0x0A84, 0x0800, 0x008C, 0x1683, 0x150C, 0x3055, 0x008D, 0x30AA, 0x008D, 0x148C, 0x0000, 0x0000, 0x0183, 0x1683, 0x1703, 0x1B8C, 0x2FC3, 0x1283, 0x1303, 0x0064, 0x1E0D, 0x2FA2, 0x120D, 0x1683, 0x1703, 0x110C, 0x140C, 0x0000, 0x0000, 0x1283, 0x0384, 0x0800, 0x060E, 0x1D03, 0x2FB7, 0x0A84, 0x0800, 0x060C, 0x1903, 0x2FBB, 0x0183, 0x0BF4, 0x2F5C, 0x3400, 0x1283, 0x1303, 0x3002, 0x07FB, 0x0AF7, 0x1903, 0x0AF8, 0x2F5A, 0x1283, 0x1303, 0x120D, 0x1683, 0x1703, 0x110C, 0x1283, 0x1703, 0x080D, 0x3903, 0x3C03, 0x1D03, 0x2FBB, 0x3003, 0x028D, 0x1283, 0x1303, 0x3004, 0x00FC, 0x3007, 0x0284, 0x0000, 0x1683, 0x1703, 0x140C, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0800, 0x1283, 0x1703, 0x060E, 0x1D03, 0x2FED, 0x0A84, 0x0800, 0x060C, 0x1903, 0x2FF4, 0x1283, 0x1303, 0x30FC, 0x05F7, 0x3006, 0x02FB, 0x2FB7, 0x1283, 0x1703, 0x0A8D, 0x0A84, 0x1283, 0x1303, 0x0BFC, 0x2FD8, 0x2FBB],[0x2000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x3F32],]



#-- Nombre del fichero .glade con el interfaz de la aplicacion
GLADE_FILE = "pyburn-gtk.glade"

#-- Directorio donde  buscar el fichero .glade. Si no se encuentra
#-- se usa el directorio actual
GLADE_DIR  = os.path.join(sys.prefix,"share/pyburn-gtk/glade")

#-- Timeout para la realizacion del PING. En mili-segundos
PING_TIMEOUT = 500

#-- Estados para el interfaz
GUI_INICIAL    = 0
GUI_OK_SERIAL  = 1
GUI_OK_PICP    = 2
GUI_PROGESS    = 3

#-- Valor por defecto de la palabra de configuracion
DEFAULT_CONFIG_WORD = 0x3F3A

#-- Timeout para la deteccion del bootloader (en segundos)
#-- Usado para la descarga del Firmware PICP
TIMEOUT = 800

#--------------------------------------------------------------------
#-- Obtener la palara de configuracion de un fichero .hex parseado
#-- Esta funcion deberia estar en la libIris
#-- DEVUELVE:
#--  * El valor de palabra de configuracion (si existe)
#--  * -1 si no existe
#--------------------------------------------------------------------
def get_config(mem):
  for dir,(vh,vl) in mem:
    if dir==0x2007:
      return vh*256 + vl
      
  return -1


#-------------------------------------------------------------------
#--                  CLASE PRINCIPAL
#-------------------------------------------------------------------
class Pyburn:
  """Programa pyburn-gtk"""


  #-------------------------
  #- INICIALIZACION
  #-------------------------
  def __init__(self):
    
    #-- Localizar fichero .glade 
    #-- primero buscar en el directorio GLADE_DIR
    glade1 = os.path.join(GLADE_DIR,GLADE_FILE);
    
    if os.path.exists(glade1):
      #-- Fichero localizado en GLADE_DIR
      self.fichero_glade = glade1
    else:
      #-- Se probara en el directorio actual
      self.fichero_glade = GLADE_FILE    
    
    #-- Para depurar...
    print "Usando %s" % self.fichero_glade
    
    #-- Abrir fichero .glade 
    try:
      self.wTree = gtk.glade.XML(self.fichero_glade,"window1") 
    except:
      print "Error al abrir fichero .glade"
      sys.exit(-1)
      
    #------------------------------------------------------------
    #--   Obtener todos los widgets
    #------------------------------------------------------------
    
    #-- Ventana principal
    self.window = self.wTree.get_widget("window1")
    
    #------------- PUERTO SERIE -----------------------------
    self.entry_serial = self.wTree.get_widget("entry_serial")
    
    #------------- FIRMWARE ---------------------------------
    self.button_boot = self.wTree.get_widget("button_boot")
    self.button_test = self.wTree.get_widget("button_test")
    self.button_test2 = self.wTree.get_widget("button_test2")
    self.button_check = self.wTree.get_widget("button_check")
    self.button_picp = self.wTree.get_widget("button_picp")
    
    #------------- FICHERO .HEX ----------------------------
    self.entry_file = self.wTree.get_widget("entry_file")
    self.button_search = self.wTree.get_widget("button_search")
    self.button_grabar = self.wTree.get_widget("button_grabar")
    
    #------------- AVANZADO ----------------------------------
    self.entry_config = self.wTree.get_widget("entry_config")
    self.button_write_config = self.wTree.get_widget("button_write_config")
    self.button_default_config = self.wTree.get_widget("button_default_config")
    
    #------------- STARGATE ----------------------------------
    self.check_ping = self.wTree.get_widget("check_ping")
    self.pixmap = self.wTree.get_widget("image_conexion")
    self.label_stargate_id = self.wTree.get_widget('label_stargate_id')    
    
    #------------- OTROS -------------------------------------
    self.progressbar = self.wTree.get_widget('progressbar1')
    self.button_cancel = self.wTree.get_widget("button_cancel")
    self.statusbar = self.wTree.get_widget("statusbar1")
    self.context_id = self.statusbar.get_context_id("pyburn")    
    
   
    #---------------------------------------------------
    #-- Configurar las funciones de retro-llamada
    #---------------------------------------------------
   
    #-- Conectar la ventana principal al evento "destroy"
    self.window.connect("destroy", gtk.main_quit)
    
    #-- Conectar todas las senales de retrollamada definidas en el 
    #-- fichero .glade
    dic = { 
            "on_entry_serial_changed"   : self.port_changed,
            "on_button_grabar_clicked"  : self.grabar_clicked,
            "on_check_clicked"          : self.check_clicked,
            "on_test_clicked"           : self.test_clicked,
            "on_test2_clicked"          : self.test2_clicked,
            "on_boot_clicked"           : self.boot_clicked,
            "on_picp_clicked"           : self.picp_clicked,
            "on_config_default_clicked" : self.config_default_clicked,
            "on_config_update_clicked"  : self.config_update_clicked,
            "on_config_changed"         : self.config_changed,
            "on_entry_file_changed"     : self.file_changed,
            "on_button_cancel_clicked"  : self.button_cancel_clicked,
            "on_button_search_clicked"  : self.button_search_clicked,
          }
    self.wTree.signal_autoconnect(dic)

    #---------------------------------
    #-- VARIABLES DE ESTADO
    #---------------------------------
    #-- Stargate picp
    self.picp = None
    
    #-- Temporizador para el ping
    self.timer_id = None
      
    #-- Estado de la conexion en la ultima vez que se comprobo
    self.conexion = False  
 
    #-- Palabra de configuracion OK o no
    self.config_ok=True;
    
    #-- Valor de la palabra de configuracion
    self.config_word=DEFAULT_CONFIG_WORD
    
    #-- Fichero .hex ok o no
    self.file_ok=False;
    
    #-- Tamano total del programa a grabar
    self.tamano=1;
    
    #-- Palabras grabadas
    self.grabado=0;
    
    #-- No se ha apretado el boton de Cancelar la descarga
    self.cancelar = False
    
    #-- POR hacer: Interfaz Iris a utilizar
    self.iris = None
    
    #--------------------------------
    #-- Actulizacion del interfaz
    #--------------------------------
    #-- Leer los iconos del estado de la conexion
    #-- Por hacer: se tendra que leer del directorio donde se instalo
    self.pixbuf_off = gtk.gdk.pixbuf_new_from_file("./pixmaps/off.xpm")
    self.pixbuf_on = gtk.gdk.pixbuf_new_from_file("./pixmaps/on.xpm")
    
    #-- Establecer el icono inicial de la conexión (apagado)
    self.pixmap.set_from_pixbuf(self.pixbuf_off)
    
    #-- Establecer el valor de la palabra de configuracion por defecto
    self.entry_config.set_text("%04X" % DEFAULT_CONFIG_WORD);
    
    #-- Pasar al estado inicial del interfaz
    self.interfaz_estado(GUI_INICIAL);
    
    #-- Mensaje inicial de la barra de estado
    self.statusbar.push(self.context_id, "Seleccionar puerto serie") 
    
  
  #------------------------------------------------------------
  #-- Abrir el puerto serie leyendo el dispositivo del entry
  #-- DEVUELVE:
  #--   -True: Ok
  #--   -False: Error al abrir el puerto serie
  #------------------------------------------------------------
  def open_port(self):
    #-- Primero obtener el nombre del dispositivo serie
    entry = self.entry_serial.child
    serialName = entry.get_text()
    
    #-- Si habia un "Stargate" abierto, cerrarlo primero
    if self.picp:
      self.picp.close()
    
    try:
      self.picp = libStargate.Picp.Open(serialName, logCallback=None)
      return True
      
    except libStargate.Main.StargateError, msg:
      self.picp = None
      
      #-- Si hay error indicarlo en la barra de estado y abortar
      self.statusbar.push(self.context_id, "ERROR al abrir puerto serie")
      
      #-- Cambiar estado del interfaz
      self.interfaz_estado(GUI_INICIAL);
      return False
  
  #---------------------------------------------------------------------
  #-- CALLBACK: Puerto nuevo seleccionado
  #-- Se invoca cada vez que hay un cambio en el entry del puerto serie
  #---------------------------------------------------------------------
  def port_changed(self,widget):
       
    #-- Si habia un temporizador activado, eliminarlo
    if self.timer_id:
      gobject.source_remove(self.timer_id)  
      
    #-- De momento no hay conexion
    self.conexion = False  
    self.gui_update_conexion()
    
    #-- Abrir puerto serie
    if (not self.open_port()):
      return
      
    #-- Indicar que se ha abierto el puerto   
    self.statusbar.push(self.context_id, "Puerto serie ABIERTO")   
      
    #-- Activar el ping
    self.check_ping.set_active(True)

    #-- Actualizar el estado del interfaz
    self.interfaz_estado(GUI_OK_SERIAL)
    
    #-- Hacer ping a la grabadora
    self.Ping();

    #-- Lanzar temporizacion para ejecutar ping regularmente
    self.timer_id = gobject.timeout_add(PING_TIMEOUT, self.Ping)
     

  #------------------------------------------------------------------
  #-- Hacer un Ping a la grabadora
  #-- Se cambia el estado de la conexion segun lo que haya sucedido
  #------------------------------------------------------------------
  def Ping(self):
    self.update()
    
    #-- El ping solo se hace si esta activado el checkbutton del ping
    if self.check_ping.get_active():
      
      if self.picp:
      
        #-- Obtener estado de la conexion actual con el Ping
        okping = self.picp.ping()
        
        #-- La conexion se ha establecido o recuperado
        if self.conexion==False and okping:
          
          #-- Obtener cadena de identificacion
          cad = self.picp.id();
          

          #-- Comprobar si es servidor de tipo picp
          es_picp = self.picp.check_server_type()
          
          if es_picp:
            self.interfaz_estado(GUI_OK_PICP);
            self.conexion=True
            self.gui_update_conexion()
            #-- Escribir nombre en el entry_box
            self.label_stargate_id.set_label("<i>CONECTADA</i>")
          else:
            self.interfaz_estado(GUI_OK_SERIAL);
            
        #-- La conexion se ha perdido  
        elif self.conexion==True and (not okping):
          self.conexion=False
          self.gui_update_conexion()
          
          #-- Llevar la interfaz al estado OK_serial
          self.interfaz_estado(GUI_OK_SERIAL);
      else:
        #-- Si hay ping es poque el puerto serie esta abierto, asi
        #-- que aqui no deberia llegar
        print "WARNING: no deberia ocurrir esto"      
        self.interfaz_estado(GUI_INICIAL);
          
    return True
  

  #-----------------------------------------------------------
  #-- Actualizar el estado grafico de la conexion
  #-- Se dibuja un icono u otro segun que haya o no conexion
  #-----------------------------------------------------------
  def gui_update_conexion(self):
    if self.conexion:
      self.pixmap.set_from_pixbuf(self.pixbuf_on)
    else:
      self.pixmap.set_from_pixbuf(self.pixbuf_off)
      
      #-- Ya no se conoce el nombre del stargate
      self.label_stargate_id.set_label("<i>SIN CONEXION</i>")

  #---------------------------
  #-- Actualizar el interfaz
  #---------------------------  
  def update(self):
    while gtk.events_pending():
      gtk.main_iteration_do()

  #-----------------------------------------------------
  #-- Detectar el PIC destino
  #-- DEVUELVE:
  #--   True: Pic detectado
  #--   False: No detectado o error de comunicacion
  #-----------------------------------------------------
  def check_pic(self):
    try:
      id,config = self.picp.readConfig()
    except: 
      self.statusbar.push(self.context_id, "Error de comunicacion") 
      return False;
      
    if id!=0x3FFF:  
      self.statusbar.push(self.context_id,
                          "PIC DETECTADO. ID: %04X. Palabra config: %04X" 
                          % (id,config))   
      return True;
    else:
      self.statusbar.push(self.context_id, "PIC DESTINO NO DETECTADO")   
      return False;

  #---------------------------
  #-- Boton de check
  #---------------------------
  def check_clicked(self,widget):
    self.check_pic();


  #----------------------------
  #-- Realizar la grabacion
  #----------------------------
  def grabar_clicked(self,widget):
    
    file = self.entry_file.get_text()
    
    #-- Realizar el parseo del fichero 
    try:
      hr = libIris.IntelHex.HexReader (file)
    except libIris.IntelHex.ReaderError,msg:
      #-- Por hacer: imprimir mensaje de error
      self.statusbar.push(self.context_id, "%s" % msg)
      return      
     
    #-- Obtener la palabra de configuracion. Usar la establecida por defecto
    #-- si no existe    
    config = get_config(hr.memory())
    if config == -1:
      config = DEFAULT_CONFIG_WORD;
      
    #-- Poner la palabra de configuracion en el entry
    self.entry_config.set_text("%04X" % config);    
    
    #-- Realizar la grabacion
    self.burn_program(hr.dataBlocks())
    
  def config_default_clicked(self,widget):
    #-- Establecer el valor de la palabra de configuracion por defecto
    self.entry_config.set_text("%04X" % DEFAULT_CONFIG_WORD);
    
  
  #------------------------------------------
  #-- Actualizar palabra de configuracion 
  #------------------------------------------
  def config_update_clicked(self,widget):
    self.burn_config()
    
  #-------------------------------------------
  #-- Cambio en la palabra de configuracion   
  #-------------------------------------------
  def config_changed(self,widget):
  
    #-- Leer el valor del entry
    config_str = self.entry_config.get_text()
    
    #-- Comprobar si la cadena actual es valida o no
    try:
      #-- Convertir a un numero hexadecimal
      self.config_word = int (config_str, 16);
      
      #-- Indicar que palabra correcta
      self.config_ok=True;
      
    except ValueError:
    
      #-- Inicar que palabra incorrecta
      self.config_ok=False;
    
    #-- Actualizar interfaz
    self.interfaz_estado(GUI_OK_PICP);
    
    
  #------------------------------------
  #-- Cambio en el nombre del fichero  
  #------------------------------------
  def file_changed(self,widget):
  
    #-- Leer nombre del fichero 
    file_name = self.entry_file.get_text()
    
    #-- El estado del interfaz se cambia seguen que 
    #-- el fichero exista o no
    if not os.path.exists(file_name):
      self.file_ok=False
    else:
      self.file_ok=True
    
    #-- Actualizar el estado del interfaz
    self.interfaz_estado(GUI_OK_PICP);
    
    
  #----------------------------
  #-- Grabar el ledp1
  #----------------------------
  def test_clicked(self,widget):
    self.burn_program(libIris.Pic16_Firmware.ledp1)
    
  #----------------------------
  #-- Grabar el ledp2
  #----------------------------
  def test2_clicked(self,widget):
    self.burn_program(libIris.Pic16_Firmware.ledp2) 
       
    
  #-----------------------------
  #-- Grabar el bootloader
  #-----------------------------
  def boot_clicked(self,widget):
    self.burn_program(bootloader) 
    
  
  #------------------------------------
  #-- Grabar palabra de configuracion
  #------------------------------------
  def burn_config(self):
  
    #-- Detectar pic destino. Si no esta se aborta
    pic_detected=self.check_pic();
    if (not pic_detected):
      #-- Actualizar barra de status
      self.statusbar.push(self.context_id, 
                        "ERROR: PIC no detectado. Config word no Grabada")
      self.update() 
      return;
  
    #-- Grabar palabra de configuracion
    try:
      self.picp.writeConfig(self.config_word);
    except:
      #-- Actualizar barra de status
      self.statusbar.push(self.context_id, "ERROR de comunicacion")
      self.update()    
      
    #-- Actualizar barra de status
    self.statusbar.push(self.context_id, 
                        "GRABADA PALABRA DE CONFIGURACION: %04X" 
                        % self.config_word)    


  #-----------------------
  #-- Grabar firmware
  #-----------------------
  def burn_program(self,prog):  
    
    #-- Detectar pic destino. Si no esta se aborta
    pic_detected=self.check_pic();
    if (not pic_detected):
      return;
      
    #-- desactivar el ping
    self.check_ping.set_active(False)
    self.update();

    #-- Pasar al interfaz de "PROGRESO"
    self.interfaz_estado(GUI_PROGESS)
    
    #-- Inicialmente no hay cancelacion
    self.cancelar=False;
    
    #-- Grabar el programa
    try:
      ok=self.picp.writeProgram (prog,stateCallback=self.burn_state)
    except libStargate.Picp.Error,msg:
    
      #-- Actualizar barra de status
      self.statusbar.push(self.context_id, "%s" % msg)
      self.update()
      
      #-- Actualizar interfaz
      self.interfaz_estado(GUI_OK_PICP)
      
      #-- Activar ping
      self.check_ping.set_active(True)
      return;
      
    #-- Por hacer: El ok nos indica si se ha grabado bien o no
    if not ok:
    
      #-- Actualizar barra de status
      self.statusbar.push(self.context_id, "ABORTADO")
      self.update()
      
      #-- Activar ping
      self.check_ping.set_active(True)
      
      #-- Pasar al interfaz de "PICP"
      self.interfaz_estado(GUI_OK_PICP)
      
      return
      
    #-- Grabar palabra de configuracion
    try:
      self.picp.writeConfig(self.config_word);
    except:
      #-- Actualizar barra de status
      self.statusbar.push(self.context_id, "ERROR de comunicacion")
      self.update()    
      
    #-- Actualizar barra de status
    self.statusbar.push(self.context_id, 
                        "GRABADA PALABRA DE CONFIGURACION: %04X" 
                        % self.config_word)
    self.progressbar.set_fraction(1.0)
    self.update()
    
    #-- Activar ping
    self.check_ping.set_active(True)
    
    #-- Pasar al interfaz de "PICP"
    self.interfaz_estado(GUI_OK_PICP)
    
  #------------------------
  #-- Boton de cancelacion
  #------------------------
  def button_cancel_clicked(self,widget):
    self.cancelar=True
    


  #---------------------------------------------------------------------------#
  #-- Funcion de estado por defecto
  #-- Se invoca durante la grabacion
  #---------------------------------------------------------------------------#
  def burn_state(self,op,inc,total):

    #--------------------------------------------------
    #- Comienzo de la grabacion
    #--------------------------------------------------
    if op==libStargate.Picp.WRITING_START:
    
      self.grabado=0;
    
      #-- Barra de progreso a cero
      self.progressbar.set_fraction(0.0)
      
      #-- Cambiar texto de la barra de progreso
      self.progressbar.set_text("Grabando");

      #-- Actualizar barra de status
      self.statusbar.push(self.context_id, "GRABACION DEL PIC EN PROGRESO...")
      self.update()      
      return True
     
    
    #------------------------------------------------
    #--  Grabacion de una palabra
    #------------------------------------------------    
    elif op==libStargate.Picp.WRITING_INC:
    
      #-- Actualizar contador de bytes grabados
      self.grabado=self.grabado+inc;
    
      #-- Actualizar barra de progreso
      ratio = float(self.grabado)/float(total);
      if ratio>1.0: ratio=1.0;
      self.progressbar.set_fraction(ratio) 
      self.update()
      
      #-- Comprobar si se ha apretado boton de Cancelar
      if self.cancelar:
        return False
        
      return True
      
    #------------------------------------
    #-- Fin de la grabacion
    #------------------------------------    
    elif op==libStargate.Picp.WRITING_END:
    
      #-- Actualizar barra de status
      self.statusbar.push(self.context_id, "OK...")
      self.update()
      return True
      
    #----------------------------------
    #-- Comienzo de la verificacion
    #----------------------------------    
    elif op==libStargate.Picp.VERIFYING_START:
      
      #-- Actualizar barra de status
      self.statusbar.push(self.context_id, "Verificacion de la grabacion...")
      
      #-- Cambiar texto de la barra de progreso
      self.progressbar.set_text("Verificando");
      
      #-- Barra de progreso a cero
      self.progressbar.set_fraction(0.0)
      self.update()
      
      #-- Contador de bytes a 0
      self.grabado=0;
      
      return True
      
    #-------------------------------------
    #-- Error de verificacion
    #-------------------------------------    
    elif op==libStargate.Picp.VERIFYING_ERROR:
    
      #-- Cambiar texto de la barra de progreso
      self.progressbar.set_text("Error");
    
      #-- Actualizar barra de status
      self.statusbar.push(self.context_id, "ERROR de verificacion")
      self.update()
      return False
      
    #-----------------------------------
    #--  Verificacion de una palabra    
    #------------------------------------
    elif op==libStargate.Picp.VERIFYING_INC:
      #-- Actualizar contador de bytes verificados
      self.grabado=self.grabado+inc;
    
      #-- Actualizar barra de progreso
      ratio = float(self.grabado)/float(total);
      if ratio>1.0: ratio=1.0;
      self.progressbar.set_fraction(ratio) 
      self.update()
      
      #-- Comprobar si se ha apretado boton de Cancelar
      if self.cancelar:
        return False
      
      return True
      
    #--------------------------------------------
    #-- Fin de la verificacion
    #--------------------------------------------    
    elif op==libStargate.Picp.VERIFYING_END:
    
      #-- Cambiar texto de la barra de progreso
      self.progressbar.set_text("Completado");
    
      #-- Actualizar barra de status
      self.statusbar.push(self.context_id, "VERIFICACION OK")
      self.progressbar.set_fraction(1.0)
      self.update()
      
      return True
   
    #-----------------------
    #-- Evento desconocido  
    #-----------------------    
    else:
      print "DESCONOCIDO: %d" % op
      return False

    
  #---------------------------------------------------------
  #-- Funciones de gestion del interfaz
  #---------------------------------------------------------
  
  #------------------------------------------------------------------------#
  #-- Establecer estado interfaz. Esta funcion determina que partes del   
  #-- interfaz estan activas en cada momento, segun la situacion
  #-----------------------------------------------------------------------#
  def interfaz_estado(self,state):
  
    #-----------------------
    #- ESTADO INICIAL
    #-----------------------
    if state==GUI_INICIAL:
    
      #--- Activar combo del puerto serie
      self.entry_serial.set_sensitive(True);
      
      #-- FICHERO .HEX
      self.entry_file.set_sensitive(False)
      self.button_search.set_sensitive(False)
      self.button_grabar.set_sensitive(False)
      
      #-- FIRMWARE
      self.button_boot.set_sensitive(False)
      self.button_test.set_sensitive(False)
      self.button_test2.set_sensitive(False)
      self.button_check.set_sensitive(False)
      self.button_picp.set_sensitive(False)
    
      #-- AVANZADO
      self.entry_config.set_sensitive(False)
      self.button_write_config.set_sensitive(False)
      self.button_default_config.set_sensitive(False)
      
      #-- STARGATE
      self.check_ping.set_sensitive(False)
      
      #-- OTROS
      self.button_cancel.set_sensitive(False)
      
    #----------------------------
    #-- ESTADO OK_SERIAL
    #----------------------------    
    elif state==GUI_OK_SERIAL:
      
      #--- Activar combo del puerto serie
      self.entry_serial.set_sensitive(True);
      
      #-- FICHERO .HEX
      self.entry_file.set_sensitive(False)
      self.button_search.set_sensitive(False)
      self.button_grabar.set_sensitive(False)
      
      #-- FIRMWARE
      self.button_boot.set_sensitive(False)
      self.button_test.set_sensitive(False)
      self.button_test2.set_sensitive(False)
      self.button_check.set_sensitive(False)
      self.button_picp.set_sensitive(True)
    
      #-- AVANZADO
      self.entry_config.set_sensitive(False)
      self.button_write_config.set_sensitive(False)
      self.button_default_config.set_sensitive(False)
      
      #-- STARGATE
      self.check_ping.set_sensitive(True)
      
      #-- OTROS
      self.button_cancel.set_sensitive(False)
    
    #-------------------------------------
    #-- ESTADO OK_PICP
    #-------------------------------------
    elif state==GUI_OK_PICP:
      
      #--- Activar combo del puerto serie
      self.entry_serial.set_sensitive(True);
      
      #-- FICHERO .HEX
      self.entry_file.set_sensitive(True)
      self.button_search.set_sensitive(True)
      
      #-- Por hacer: condicional si hay fichero seleccionado
      if self.config_ok and self.file_ok:
        self.button_grabar.set_sensitive(True)
      else:
        self.button_grabar.set_sensitive(False)
      
      #-- FIRMWARE
      if self.config_ok:
        self.button_boot.set_sensitive(True)
        self.button_test.set_sensitive(True)
        self.button_test2.set_sensitive(True)
      else:
        self.button_boot.set_sensitive(False)
        self.button_test.set_sensitive(False)
        self.button_test2.set_sensitive(False)    
      
      self.button_check.set_sensitive(True)
      self.button_picp.set_sensitive(False)
    
      #-- AVANZADO
      self.entry_config.set_sensitive(True)
      
      #-- Si la palabra de configuracion actual es distinta de la 
      #-- que hay por defecto, se activa el boton de default
      if self.config_word!=DEFAULT_CONFIG_WORD:
        self.button_default_config.set_sensitive(True)
      else:
        self.button_default_config.set_sensitive(False)      
      
      if self.config_ok:
        self.button_write_config.set_sensitive(True)
      else:
        self.button_write_config.set_sensitive(False)
      
      
      #-- STARGATE
      self.check_ping.set_sensitive(True)
      
      #-- OTROS
      self.button_cancel.set_sensitive(False)
      
    #------------------------------
    #--  ESTADO PROGRESS
    #------------------------------
    elif state==GUI_PROGESS:
    
      #--- Desactivar combo del puerto serie
      self.entry_serial.set_sensitive(False);
    
      #-- Boton de cancel activado. Resto del interfaz desactivado
      self.button_cancel.set_sensitive(True);
      
      #-- FICHERO .HEX
      self.entry_file.set_sensitive(False)
      self.button_search.set_sensitive(False)
      self.button_grabar.set_sensitive(False)
      self.button_grabar.set_sensitive(False)
      
      self.button_boot.set_sensitive(False)
      self.button_test.set_sensitive(False)
      self.button_test2.set_sensitive(False)
      
      self.button_boot.set_sensitive(False)
      self.button_test.set_sensitive(False)
      self.button_test2.set_sensitive(False)    
      
      self.button_check.set_sensitive(False)
      self.button_picp.set_sensitive(False)
    
      #-- AVANZADO
      self.entry_config.set_sensitive(False)
      self.button_default_config.set_sensitive(False)
      self.button_write_config.set_sensitive(False)
      self.button_write_config.set_sensitive(False)
      self.check_ping.set_sensitive(False)
      

  #-------------------------------------
  #-- Boton de buscar fichero apretado  
  #-------------------------------------
  def button_search_clicked(self,widget):
  
    #-- Crear el dialogo del fichero
    dialog = gtk.FileChooserDialog(title=None,action=gtk.FILE_CHOOSER_ACTION_OPEN,
                                    buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,
                                    gtk.STOCK_OPEN,gtk.RESPONSE_OK))
    dialog.set_default_response(gtk.RESPONSE_OK)
    
    
    #-- Anadir los filtros: 
    filter = gtk.FileFilter()
    filter.set_name("Hex files")
    filter.add_pattern("*.hex");
    filter.add_pattern("*.HEX");
    dialog.add_filter(filter)
    
    filter = gtk.FileFilter()
    filter.set_name("All files")
    filter.add_pattern("*")
    dialog.add_filter(filter)
    
    #-- Ejecutar el dialogo
    response = dialog.run()
    
    #-- Analizar la respuesta
    if response == gtk.RESPONSE_OK:
      #-- Meter el fichero seleccionado en el entry
      file = dialog.get_filename()
      self.entry_file.set_text(file)
    
    dialog.destroy()
    
#-------------------------------------------------------------------------
#--
#--   METODOS PARA LA DESCARGA DEL FIRMWARE PICP. INTERFAZ CON LA LIBIRIS  
#--
#-------------------------------------------------------------------------

  #-----------------------------
  #-- Grabar el PICP
  #-----------------------------
  def picp_clicked(self,widget):
    self.download_program(libIris.Pic16_Firmware.picp) 

  #--------------------------------------------------------------
  #- Metodo principal para descargar un programa en la skypic
  #--------------------------------------------------------------
  def download_program(self,prog):
    #-- Poner la barra de progreso a 0
    self.progressbar.set_fraction(0.0) 
    self.update()
  
    #-- Desactivar flag de cancelacion
    self.cancelar=False
    
    #------------------------------------
    #-- Abrir puerto serie
    #------------------------------------
    #-- Primero obtener el nombre del dispositivo serie
    entry = self.entry_serial.child
    serialName = entry.get_text()
    
    try:
      self.iris = libIris.Pic16_Bootloader.Iris(serialName,logCallback=None)
    except libIris.Pic16_Bootloader.IrisError,msg:
    
      #-- Si hay error indicarlo en la barra de estado y abortar
      self.statusbar.push(self.context_id, "%s" % msg) 
      return
      
    #-- Hay que esperar a que detecte el Bootloader
    self.statusbar.push(self.context_id, "Pulse Reset en la skypic") 
    
    #-- desactivar el ping
    self.check_ping.set_active(False)
    self.update();

    #-- Pasar al interfaz de "PROGRESO"
    self.interfaz_estado(GUI_PROGESS)
    
    #-- Actualizar el interfaz
    self.update()
    
    try:
      self.iris.download(prog,stateCallback=self.state)
    except libIris.Pic16_Bootloader.IrisError,msg:
      self.statusbar.push(self.context_id, "%s" % msg)
      self.picp.flush()
      self.interfaz_estado(GUI_OK_SERIAL)
      #-- activar el ping
      self.check_ping.set_active(True)
      self.update()
      return
    
    #-- SERVIDOR PICP cargado
    #-- Re-abrir el puerto serie
    self.open_port()
    
    #-- Activar el ping
    self.check_ping.set_active(True)

    #-- Actualizar el estado del interfaz
    self.interfaz_estado(GUI_OK_SERIAL)
    

  #-----------------------------------------------------------------------
  #- Funcion de descarga de un fichero .hex. Usando el Bootloader
  #- El nombre del fichero Se toma del entry, se parsea 
  #-  y finalmente se descarga
  #-----------------------------------------------------------------------
  def download(self):
    
    #----------------------------------------
    #-- Abrir y parsear el fichero .hex
    #----------------------------------------
    #-- Obtener el nombre
    file = self.entry_file.get_text()
    
    #-- Realizar el parseo
    try:
      hr = libIris.IntelHex.HexReader (file)
    except libIris.IntelHex.ReaderError,msg:
      self.statusbar.push(self.context_id, "%s" % msg) 
      return      
    
    #----------------------------------
    #-- Realizar la descarga!!
    #---------------------------------- 
    self.download_program(hr.dataBlocks16())
  
  #------------------------------------------------------------------------
  #-- Funcion de retrollamada de libIris. Segun el estado de la descarga
  #-- Se hace una cosa u otra
  #------------------------------------------------------------------------  
  def state(self,op,inc,total):
    
    #-----------------------------
    #-- Comienzo de descarga
    #-----------------------------
    if op==libIris.Pic16_Bootloader.WRITING_START:

      #-- Barra de progreso a cero
      self.progressbar.set_fraction(0.0)

      #-- Cambiar texto de la barra de progreso
      self.progressbar.set_text("Descargado");

      #-- Actualizar barra de status
      self.statusbar.push(self.context_id, "Descargando firmware...")
      self.update()      
      return True
      
    
    #------------------------------
    #-- Incremento en la descarga  
    #------------------------------    
    elif op==libIris.Pic16_Bootloader.WRITING_INC:  
      self.progressbar.set_fraction(float(inc)/float(total)) 
      self.update()
      
      #-- Comprobar si se ha apretado boton de Cancelar
      if self.cancelar:
        return False
        
      return True
    
    #-------------------------------
    #-- Fin de la descarga
    #-------------------------------
    elif op==libIris.Pic16_Bootloader.WRITING_END: 
      self.progressbar.set_fraction(1.0)
      
      #-- Cambiar texto de la barra de progreso
      self.progressbar.set_text("Descargado");
      
      #-- Actualizar barra de status
      self.statusbar.push(self.context_id, "Firmware actualizado")
      self.update()  
      return True
   
    #---------------------------------------------------
    #-- Comienzo de la identificacion del bootloader    
    #---------------------------------------------------    
    elif op==libIris.Pic16_Bootloader.IDENT_START:
      return True
      
      
    #-----------------------------------------------------------------
    #-- Respuesta no recibida del bootloader tras un mini-timeout    
    #-----------------------------------------------------------------
    elif op==libIris.Pic16_Bootloader.IDENT_NACK:
    
      #-- Mientras que el tiempo total acumulado sea menor que el 
      #-- TIMEOUT indicado, continuar esperando
      self.update()
      
      #-- Si apretado boton de cancelar abortar...
      if self.cancelar:
        return False
      
      if total<=TIMEOUT:
       return True
      else :
        return False  

    

#------------------------
#-- Programa principal  
#------------------------
if __name__ == "__main__":
  Pyburn()
  gtk.main()
