Full text
! ! i! ! ! ! Trabajo'Fin'de'Grado'! GITT$(Telemática)! Diseño e implementación de un servidor web para mensajes publicitarios para redes ad hoc Autor : Antonio Acevedo Tutor : Daniel Gutierrez Dept. de Ingeniería Electrónica Escuela Técnica Superior de Ingeniería Universidad de Sevilla Sevilla, 2015
! ! ii! ! ! !
! ! Proyecto Fin de Grado GITT Diseño e implementación de un servidor web para mensajes publicitarios para redes ad hoc Autor:! Antonio&Acevedo&Báñez! ! ! ! ! ! Tutor:! Daniel'Gutiérrez!Reina! ! ! ! ! ! ! Dept.&de&Ingeniería)Electrónica! Escuela(Técnica(Superior(de(Ingeniería! Universidad*de*Sevilla! ! Sevilla,(2015! !
! ! iv! ! Trabajo'Fin'de'Grado:" Diseño'e'implementación'de'un'servidor'web'para'mensajes'publicitarios' para$redes$ad$hoc! ! ! ! ! ! Autor:! Antonio&Acevedo&Báñez! Tutor:! Daniel'Gutiérrez!Reina! ! ! El# tribunal# nombrado# para# juzgar# el# Proyecto# arriba# indicado,# compuesto# por# los# siguientes# miembros:! Presidente:! ! ! ! Vocales: ! ! ! Secretario:! ! ! ! ! Acuerdan(otorgarle(la(calificación(de:! ! Sevilla,(2015! ! ! !
! ! El#Secretario#del#Tribunal! !
! ! vii! ! ! ! ! A"mi!familia! A"mis!maestros! !
! ! ix! ! ! Agradecimientos En#primer#lugar#quiero#agradecer&todo& el& tiempo&que& me& ha&dedicado# el# tutor!de" este" proyecto," Daniel."Su"implicación"y"ganas"han"sido"esenciales"para"el"desarrollo!del$trabajo,$pero$sobre!todo$le! agradezco)su)paciencia,)su)trato)y)su)confianza)en!mí."! Mi#familia#me#ha#tenido#que#aguantar#durante#todo#este#tiempo,#en#especial'mi'madre,"con$la$que$ vivo$ y"nunca"han"dejado"de"apoyarme"y"velar"por"mí,"os"doy"las"gracias"por"todo."También"mi" hermana,( que( en( la( distancia( se( preocupa( por( mí( y( me( transmite( su( cariño.( A( todos( los( demás( gracias'por'vuestro'apoyo'y'cariño,'y'a'los'que$ya$no$están,$por$no$haberse$marchado$del$todo.$! Gracias'a'mis'amigos,'tanto'los'de'la'Universidad'como'los'de'siempre,'que'me'han'acompañado' todos%estos%años%y%esperan%tanto%como%yo%que%acabe%la%carrera.%Gracias&por&no&dejarme&caer&nunca&y& estar&siempre"a"mi"lado.! Antonio&Acevedo&Báñez! Sevilla,(2015!
! ! ! Índice'de'Figuras! ! ! xvi! PRnet! Red$de$paquetes$creada$durante$1973"1976%por%DARPA%en%el%área%de%San% Francisco)Bay,)para)probar)ARPENET! Raspberry)Pi! Es# un# ordenador# de# placa# reducida# o# (placa# única)# (SBC)# de# bajo# coste,# desarrollado(en(Reino(Unido,(se(caracteriza(por(su(pequeño(tamaño(y(su( bajo%consumo.! Sqlite3! SQLite' es' un' sistema' de' gestión' de' bases' de' datos,' contenida' en' una' relativamente*pequeña*biblioteca*escrita*en*C.! Serpent! Librería(de(socialización(compatible(con(Python,(Java(y(C#.! STRV"1b! Space&Technology&Research&Vehicle!o"STRV"fueron"una"serie"de"microsatélites" británicos*que*operaba*en*órbitas*elípticas*alrededor*de*la*Tierra.! Serializar! Consiste( en( un( proceso( de( codificación' de' un' objeto' en' un' medio' de' almacenamiento*(como*puede*ser*un*archivo,*o*un*buffer*de*memoria)*con* el#fin#de#transmitirlo#a#través#de#una#conexión#en#red.! Tethering! Se#denomina#anclaje#a#red#o#tethering!al#proceso#por#el#cual#un#dispositivo# móvil"con"conexión"a"Internet"actúa"como"pasarela"para"ofrecer"acceso"a"la" red$a$otros$dispositivos.! WiFi$Direct! Es#un#protocolo#que#permite%que%varios%dispositivos%WiFi#se#conecten#entre# sí#sin#necesidad#de#un#punto#de#acceso#intermedio.! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !
! ! 17! ! ÍNDICE DE FIGURAS ! ! ! Figura'2.1!–!Modelo&capas&cliente"servidor(WiFi.!22! Figura'2.2!–!Modelo%de%capas%cliente"servidor(Bluetooth.!24! Figura'3.1!–!Estructura(de(objetos(del(ORM.!27! Figura'3.2!–!Estructura(de(objetos(del(servicio(web.!28! Figura'3.3!–!Estructura(de(objetos(de(la(interfaz.!29! Figura'3.4!–!Diagrama'general'con'un'servidor!30! Figura'3.5!–!Diagrama'general'con'varios'servidores.!30! Figura'3.6!–!Imagen'del'modelo'B.!33! Figura'3.7!–!Dongle'usb'utilizado'Belkin'N300!33! Figura'3.8!–!Dongle'Bluetooth'utilizado'!35! Figura'3.9!–!Esquema(utilizado,(NAP.!36! Figura'3.10!–!Modelo&problemático.!36! Figura'4.1!–!Intercambio,de,mensajes,GET.!40! Figura'5.1!–!Ventana&principal&interfaz.!44! Figura'5.2!–!Ventana&creación&de&menú.!45! Figura'5.3!–!Ventana&de&creación(de(oferta.!46! Figura'5.4!–!Ventana&de&creación&de&ítem.!46! Figura'5.5!–!Ventana&cambio&de&servicio.!47! Figura'7.1!–!Esquema(del(escenario.!56! Figura'7.2!–!Pantalla&de&configuración&Android.!61! Figura'7.3!–!Vista&desde&navegador&móvil.!62! Figura'7.4!–!Gráfica(test(larga(duración.!63! Figura'8.1!–!Menú%de%ejemplo.!66! Figura'8.2!–!Panorámica*butacas.!67! Figura'8.3!–!Panorámica*evento.!67! Figura'8.4!–!Audio&guía.!68! ! ! ! ! ! ! ! ! !
! ! ! Objetivos*y*motivación*del*proyecto! ! ! 18! 1 OBJETIVOS Y MOTIVACIÓN DEL PROYECTO ! ! ! ! ! ! ! ste$ es$ el#primero,!de#un#conjunto!de#dos#proyectos#que#pretenden#realizar#un#prototipo#de# diseminación!de# mensajes# publicitarios# sobre# redes# ad# hoc," tanto" WiFi" como" Bluetooth." El" primer&proyecto&(este),!pretende'abordar'el'diseño!del$ servidor!o" diseminador" de#mensajes# publicitarios.-Todo-el#sistema!correrá&sobre&una&Raspberry&Pi,"mientras"que"el"segundo"se"ocupará" de#los#clientes#de#la#red#que#a#su#vez#también#podrán#intercambiar!mensajes'publicitarios'entre'sí," estos% clientes% correrán% sobre% smartphones!con$ sistema$ operativo$ Android.!Nuestro# servidor# estará# accesible(a(todo(aquel(dispositivo(capaz(de(conectarse(a!una!red$ad$hoc.! Se#hará#especial#hincapié!en#que#la#información#que#se"intercambiará"en"el"sistema"sólo#es#útil#en#el# lugar&donde&se&obtiene,!por$lo$tanto,$sólo#se#podrá#acceder#a#información*nueva*en*el*lugar*donde* esté!el#servidor#o#raíz#del#sistema#de#diseminación!del$sistema,$o$cuando$se$encuentre$con$un$cliente$ de#la#red#que#tenga#esta#información.#Estos#sistemas#son#muy#útiles#cuando#no#se#tiene#acceso#a# internet&directo&en&nuestro&terminal&móvil,&por&ejemplo,&cuando&estamos&de&viaje&en&el&extranjero&y& visitamos(un(museo,(podríamos(disfrutar(de(la(información(de(las(obras(de(arte$sin$tener$acceso$a$ internet.!O!por$otra$parte,$hacer$ofertas$especiales$a$los$clientes$que$estén$en!un#establecimiento,o, cerca%de%él.! En# particular,!en# este# proyecto# se# pretende# el# estudio# e!implementación+ de+ un+ sistema+ de+ diseminación* de* mensajes,* en* nuestro* caso* publicitarios,* sobre* dispositivos* móviles( reales.' Se' estudiarán+los+problemas+encontrados,!a"los"cuales"se"le"ha"dado!una$solución$razonada.!Trataremos) de# marcar# las# líneas# generales# del# diseño# del# sistema# para# poder# aprovechar# este# trabajo# para# diseminar)cualquier)tipo)de)información.! El#objetivo#del#servidor#del#sistema#es#crear#una#API#a#la#que$accederán$los$clientes$para$obtener$ nueva&información.&Además,!se#realizará#una!interfaz)gráfica)desde)la)cual)el)administrador)de)la) red,!podrá&añadir&nueva&información&a&la&red.&Esta&interfaz&será&de&carácter&distribuida,&es&decir,& podrá&acceder&al&servicio'para'modificar'la'información'del'mismo'desde'cualquier'ordenador'que' tenga&acceso&a&la&Raspberry'Pi!sobre&la&cual&está!siendo'ejecutado'el'servicio.! Los$ problemas$ de$ retrasmisión$ de$ paquetes$ en$ este$ tipo$ de$ redes$ se$ abordarán$ en$ el$ segundo$ proyecto.#En#éste,%nos%ocuparemos%de%servir%la%información%y%de%habilitar%una%interfaz%amigable%al% administrador*de*la*red.! ! ! ! ! ! ! ! ! ! E! ! Quizá&tenga&más&sentido&considerar&arquitecturas&radicalmente&diferentes."! "!Preston(Marshall,(ex(director(de(tecnología(inalámbrica(en(la(DARPA("! !
!! 19! Diseño e implementación de un servidor web para mensajes publicitarios para redes ad hoc ! 2 INTRODUCCIÓN uestro'sistema'generará&una&red&ad&hoc&entre&los&dispositivos&Android!y"el"servidor,!tanto%mediante% WiFi$como$mediante$Bluetooth.!El#objetivo#de#esta#red#es#el#intercambio#de#información#entre#todos# los$nodos$de$la$red,!que$en$nuestro$caso,!serán&móviles.&Este&es&uno&de&los&puntos&más$interesantes( del$ proyecto,$ ¿cómo# saltaremos# la# dificultad# de# que# todos% los% nodos% sean!independientes( e( iguales?,!a" excepción(del(nodo(raíz(o(servidor(de(la(red,(del(cual(emana(la(información(más"reciente"y"por"lo"tanto%más" válida%para%los%clientes%de%la%red.%Aunque%en%nuestro%caso%será%intercambio%de%publicidad%entre%los%nodos%de%la% red,% este$ concepto$ se$ puede,$ adaptar$ a$ cualquier$ tipo$ de$ información$ que$ pueda$ ser$ interesante!para! nosotros.! En#este#proyecto#se#pondrá#como#ejemplo#un#servidor#que#disemina#la#información#de#un#bar#o#restaurante.# Por$eso$trataremos$datos$como$menús,"ofertas"e"ítems&que&están&a&la&venta.! ! 2.1 Publicidad mediante redes ad hoc En#los#últimos!años%el%concepto%de%publicidad#ha#ido#evolucionando,#esto#ha"hecho"que"se"busquen" nuevas' formas' de# llegar!a" los$ consumidores.$ Al$ hacerlo$ mediante$ redes$ ad$ hoc$ se$ entiende$ que$ mejoramos&la&credibilidad&de&la&publicidad&alejándonos&del&correo%no%deseado!del$email$y$de$otras$ técnicas(publicitarias(mal(vistas(por(el(público.! En#marketing,!los$sistemas$ad$hoc$se$definen$como$una$herramienta(de(investigación(específica%para% crear% y% difundir$ mensajes$ publicitarios$ adecuados,!que$ dan$ a$ conocer$ productos$ a$ un$ segmento$ definido,(en(un(determinado(mercado.! Normalmente," en!comunicaciones,!las$ redes$ ad$ hoc$ son$ usadas$ para$ desplegar$ redes$ con$ gran$ rapidez(sin(necesidad(de(montar(o(tener(una$infraestructura!de#comunicaciones!previa.!La#red#de# comunicaciones* celulares* en* cambio,* necesita!de# una# gran# infraestructura!previa!para$ estar$ operativas," ya" que" están" pensadas" y" diseñadas" para" dar" soporte" de" forma" continuada" a" una" determinada) zona) a) un) número!determinado* de* usuarios.* La* calidad* de* servicio* ofrecida* a* los* usuarios'de'esta'red'puede'ser'mejorada'añadiendo'más!nodos%a%la%red%celular,%en%cambio,%en%las% redes% ad% hoc,% la% calidad% del% servicio% viene% predeterminada% a% la% tecnología% inalámbrica% que% utilicemos&y&a&las&perturbaciones&que&pueda&haber&en&la&capa&física,&ya&que&son&redes&de&un&ámbito& más!local%y%tenemos%una%mayor%restricción%de%espectro%que%las%redes%celulares%que%dan%servicio%a%los% teléfonos)móviles.!En#nuestro#caso#aprovecharemos#esta#característica)de)las)redes)ad)hoc)para)que) nuestros(mensajes(publicitarios(lleguen!al#máximo#número%de%clientes%con%un%único%punto%de%salida.! Este% tipo% de% aplicaciones% son% muy% interesantes,!ya# que# funcionan# aunque# no# se# tenga& acceso& a& internet.'Y'a'pesar'de'lo'que$se$puede$pensar$en$un$principio,!la#información#se#propaga#a#gran# velocidad!por$los$nodos$de$la$red.! En# nuestro# caso,!nuestro( servidor( servirá( los$ siguientes!tipos& de& información!u" objetos," Menús,! Ofertas!e"Ítems,"los"cuales"quedan"descritos"en"las"siguientes'tablas.! ! Tabla%2–1."Menú,"estructura"de"datos! Datos&contenidos! Descripción*! nombre! Cadena& de& caracteres! identificativa!del$menú! precio! Float&que&identifica&el&precio! descripción! Cadena& de& caracteres!para$ la$ descripción#del#menú! disponible! Booleano!que$ marca$ si$ un$ menú%esta%disponible%o%no! fecha_ini! Fecha& de& inicio& de& disponibilidad*! N!
! ! ! Introducción! ! ! 20! fecha_fin! modified! Fecha&&de&fin&de&disponibilidad! Fecha&de&última&modificación! imagen! primeros! segundos! postres! Imagen'guardada'en'base64! Colección!de#ítems! Colección!de#ítems! Colección!de#ítems! ! Tabla%2–2."Oferta,"estructura!de#datos! Datos&contenidos! Descripción*! nombre! Cadena& de& caracteres! identificativa!de#la#oferta! precio! Número' decimal!que$ identifica#el#precio! descripción! Cadena& de& caracteres!!para$ la$ descripción*del*menú! código! Cadena& de& caracteres!!para$ el$ código' promocional' de' la' oferta! disponible! Booleano" que" marca" si" una" oferta!está!disponible*o*no! fecha_ini! Fecha& de& inicio& de& disponibilidad"! fecha_fin! modified! Fecha&&de&fin&de&disponibilidad! Fecha&de&última&modificación! imagen! ítems! Imagen'guardada'en'base64! Colección!de#ítems! Por último, queda por definir el objeto ítem. Estos ítems se corresponden con los artículos que tiene el establecimiento a la venta. Tabla%2–3."Ítem,"estructura"de"datos! Datos&contenidos! Descripción*! nombre! Cadena& de& caracteres! identificativa!del$ítem! precio! Número' decimal!que$ identifica)el)precio! descripción! Cadena& de& caracteres!para!la# descripción*del*menú! disponible! Booleano'que'marca'si'un'ítem! está!disponible*o*no! imagen! modified! Imagen'guardada'en'base64! Fecha&de&última&modificación! Estos son los datos que hemos definido para nuestro servidor, más adelante diremos dónde y cómo se definen estos objetos, para así poder adaptar este servidor a la información que nos interese. El campo “modified” sirve como marca de tiempo para que los clientes sepan si tienen la última versión modificada del objeto en cuestión. Las imágenes van codificadas en base64, ya que si ofreciéramos la ruta del servidor, como esta imagen va a ir saltando de nodo en nodo, esta ruta nunca iba a ser la misma y cabía la posibilidad de que el cliente accediera a la información y acto seguido ya no pudiera acceder al servidor debido a que salió de su rango en la red. Esto supone una pérdida de rendimiento, pero es una medida de seguridad para que los intercambios de información sean lo más atómicos posibles.
!! 21! Diseño e implementación de un servidor web para mensajes publicitarios para redes ad hoc ! 2.2 Redes ad hoc y redes tolerantes a retrasos Para$hablar$del"origen!de#las$redes%ad% hoc%nos$debemos$remontar$a!1968,&cuando&se&iniciaron&los& trabajos( en( la( red( ALOHA!(se# pretendía# conectar( centros( educativos( en( Hawái).!Aunque& se& emplearon) estaciones) fijas,) el) protocolo) ALOHA) se) prestaba) a) la) gestión) distribuida) de!acceso& a& canales,( y( de( aquí( que( sentase( las( bases( para( el( posterior( desarrollo( de( esquemas( de( acceso( distribuido)a)canales)que)eran)adecuados)para)la)formación)de)redes)ad)hoc.)El)protocolo)ALOHA) propiamente*dicho*era*un*protocolo#de#salto#simple,"no"soportaba%el!encaminamiento."En"lugar"de" ello,%cada%nodo%tenía%que%estar%al%alcance%de%todos%los%demás%nodos%participantes.! De!la# red# ALOHA# y# el# anterior# desarrollo# de# conmutación# de# paquetes# en# redes# fijas,# DARPA# empezó!a" trabajar,!en# 1973,# en# la# PRnet# una# multi"red$ de$ salto$ múltiple.!En# este# contexto,# salto# múltiple(quiere&decir!que$los$nodos$cooperaban$en$la$retransmisión$del$tráfico$en$beneficio$mutuo$ para$llegar$a$ estaciones$distantes$que$ de$otra$manera$habrían$estado!fuera& de& su& alcance.& PRnet& proporcionó!mecanismos)para)gestionar)la)operación)de)forma)centralizada.! A"medida"que"disminuimos"la"gestión"centralizada,"nos"encontramos"acercándonos!en#la#dirección# de#una#operación#ad#hoc#pura,#que#también#puede$ser!de#saltos#simples#o#múltiples.! Las$redes$tolerantes&a&retrasos&surgen&como&evolución&natural&de&las&redes&ad&hoc&tradicionales,&al& introducirse+el"concepto"de!nodos%móviles.%En%este%contexto,!si#se#quiere#entregar#un#mensaje#a#un# nodo,%que%en%ese%momento%no%está!disponible*por*cualquier*motivo.*La*red!debe$de$ser$capaz$de$ retener%el%mensaje%hasta%poder%entregárselo%al%destinatario%cuando%vuelva%a%estar%disponible.%Esto% hace%que%a%diferencia%de%las%redes%ad%hoc,%estas%redes%no%necesiten%una%alta%densidad%de%nodos%para% funcionar.!Estas%redes%también%son%llamadas&redes&oportunistas&debido&a&que&los&nodos&únicamente) son$capaces$de$hablar$entre$ellos$en$determinados$periodos$de$tiempo,"debido"a"que"los"nodos"son" móviles(y(su(rango(de(transmisión(es(limitado.! Las!redes%tolerantes%a%retrasos%son%ideales%en%ambientes'hostiles'de'comunicación$como$por$ejemplo$ el#espacio,"submarinos"o"dispositivos"con"poco"alcance"para"transmitir"datos."Estas"redes"presentan" las$siguientes$características. • Retardo variable. • Tasas de error variables. • Los equipos se pueden mover. • Comunicación oportunista. Estas%redes%toman%gran%importancia%sobre!todo$en#los$últimos$años$debido$al$desarrollo$espacial.$La# evolución*de*los*estándares*para*sistemas*de"datos"en"el"espacio"ha"ido!en#paralelo#con#la#evolución# de#Internet.#Desde#finales#de#los$90$se$ha$buscado$por$parte$del$CCSDS$una$convergencia$entre$los$ protocolos(para!el!Internet&interplanetario&y&los&protocolos&de&Internet&existentes.&Cabe&destacar&la& exitosa(transferencia(de(ficheros(vía!FTP$hacia$la$Tierra$desde$el$STRV"1b#en#órbita,#ocurrida(el(2(de( enero%de%1996.! Mientras)que)los)protocolos)tipo)IP)son)viables) para) saltos)cortos,)como)por) ejemplo)desde) una) estación)de)tierra)a)un)satélite)en)órbita)o)de)un)equipo)de)exploración)al)satélite,)se)requiere)una)red) tolerante( a( los( retrasos" para" llevar" información" de" una" región" del" sistema" solar" a" otra.!Lo# que# queremos(decir(con(esto$es$que,$este$tipo$de$redes$no$nos$podemos$ceñir$únicamente$a$la$IP$para$ encaminar( los( datos( ya( que( si( los( retrasos( son( grandes( es( posible( que( al( llegar( el( paquete,!esa$ estación)ya)no)tenga)la)misma)IP)(en)el)caso)de)usar)IP)privadas).!Hoy$en$día$Google&y&la&Nasa&ya& están&trabajando&mano&a&mano&en&un&proyecto&que&busca&conectar&a&la&inmensidad&del&universo& para$ entablar$ una$ mejor$ comunicación$ con$ las$ naves$ espaciales% y% los% vehículos% que% exploran% el% planeta'Marte.! Nuestro( sistema( estará( sustentando( sobre( las( redes( ad( hoc( que( formaremos( mediante( WiFi( y( Bluetooth.) La) infraestructura) de) la) red) la) formarán) los) mismos) usuarios) de) la) misma.!Como$ los$ nodos%son%móviles%y%pueden%estar%o%no%estar,%nuestro%sistema%debe%ser%tolerante%a%retrasos.!Es"decir,! los$nodos$$deben$retener$la$información$durante$un$tiempo.!Este$tiempo$lo$estipularemos$a$nuestro$ antojo&y&será&el&que&determine&si&una&información&sigue&siendo&valiosa¶&el"sistema"o"no,"ya"que" una$vez$pasado$ese$tiempo$el$nodo$eliminará$la$información$y$no$la$retrasmitirá$más.!Este%tiempo% depende% en% gran% medida% del% tipo% de% información% que% estemos% manejando% en% nuestro% sistema.! Como$los$enlaces$pueden$ser$conectados$o$desconectados(en(cualquier(momento,(el(sistema(debe( ser$ capaz$ de$ hacer$ frente$ a$ estas!re"estructuraciones+ dinámicas,+ preferiblemente+ de+ una+ manera+ oportuna,)eficiente,)fiable,)robusta)y)escalable.!Es#decir,#los#nodos#deben#ser#capaces#de#retener#la#
! ! ! Introducción! ! ! 22! información* que# deben# retrasmitir# a# los# nuevos# nodos# que# descubran# siempre# y# cuando# la# información*siga*siendo*válida.&Por&lo&tanto,&éstos$también$deben$de$ser$capaces$de$determinar$si$ una$información$es$útil$o$no.! Nuestro( servidor( se( limitará( a( servir( información( a( los" clientes" de" la" red" que" se" lo" pidan.!Estos$ clientes(son(los(que(deben(comprobar!si#la#información#que#obtienen#es#más"reciente,!y"por"lo"tanto,! más"valiosa"que"la"que"tienen"en"ese"momento."Para"determinar"esto,!los$paquetes$de$publicidad$ (Menús!y"Ofertas),#van#marcados#con!las$fechas$de$disponibilidad,!un#booleano#que#determina#si#la# oferta' está!disponible* o* no,!y" de" una" marca" de" tiempo" para" saber" cuándo$ fue$ su$ última$ modificación.! 2.3 Tecnologías inalámbricas para redes ad hoc (Bluetooth y WiFi) Las$ redes$ ad!hoc$ WiFi,$ son$ un$ tipo$ de$ red$ inalámbrica$ en# la# cual# cada# nodo# participa# en# el# encaminamiento) de) los) mensajes.) No) se) basan!en# tablas# de# encaminamiento# clásicas,!ya# que# el# encaminamiento)de)estas)redes)suele)ser)dinámico,!debido&a!que$los$nodos$suelen$ser$móviles.(Por( lo#tanto,#estas#redes#trabajan#en#la#capa#2#o#capa#de#enlace#del#modelo!OSI,%ya%que%una%dirección%IP% sólo#es#útil#si#el#encaminamiento#hacia#el#nodo!destino(es#estático.! En# una# red# ad# hoc,!los$ dispositivos$ trabajan$ de$ la$ siguiente$ mantera.$ En# primer% lugar,% los# ordenadores( están( en( igualdad( de( condiciones.( La( conexión( es( establecida( por( una( duración( determinada.* Los* dispositivos* se* descubren* unos* a* otros* en* un* rango* para* formar* una* red$ o$ “network”."Estos"pueden"buscar"nodos"que"están"fuera"del"área#de#alcance#conectándose#con#otros# dispositivos(que(estén(conectados(a(la(red(y(estén(a(su(alcance," creando" una" red" multi"salto." Las" conexiones( son( posibles( por( múltiples( nodos.( Las( redes( ad( hoc( suelen( presentar( cambios( de( topología.)Estos)cambios)son)frecuentes'debido'a'su'movilidad.! Aunque&en&redes&ad&hoc&no&suelen&utilizar&direcciones&IP,!en#nuestro#sistema#necesitaremos#otorgar# IP#a#nuestros#clientes#ya#que#tienen#que#acceder#a#un#servidor#web#y#por#lo#tanto#para#llegar#a#la# capa$ de$ aplicación$ del$ servidor& web&necesitan&una& dirección&IP¶& identificarse&contra& nuestro& servidor( web.( Para( esto!usaremos( un( servidor( DHCP( que( irá!asignando( las( IP( de( manera( automática)a) los)usuarios)de) la)misma) red)ad) hoc.!La#pila#de#protocolos#quedaría#de#la#siguiente# manera. ! Figura''2.1!–!Modelo&capas&cliente"servidor(WiFi.! ! Para$ que$ dos$ dispositivos$ estén$ en$ la$ misma$ red$ ad$ hoc$ WiFi,$ necesitamos$ que$ ambos$ estén$ emitiendo(la(misma(red(WiFi(en(modo(ad(hoc.(Es(decir, mismo BSSID y mismo canal físico WiFi. Si ponemos algún tipo de cifrado a nuestra red, todos los nodos deben de tener el mismo cifrado. Las$tecnologías$de$radio$que$se$acercan$a$la$filosofía!ad#hoc#son,#Bluetooth,#el#modo#ad#hoc#IEEE#
!! 23! Diseño e implementación de un servidor web para mensajes publicitarios para redes ad hoc ! 802.11,'el'modo'sin'estación'PRnet,'la'red'móvil&ad&hoc&(MANET),&y&los&conceptos&tales&como&la&red& de#área#personal'(PAN)$o$la$comunicación$PAN$a$PAN,!se#adaptan#más#o#menos#enteramente#al# dominio&ad&hoc.&La&iniciativa&MANET&del&Grupo&de&Ingeniería&de&Internet%(IETF)&tiene&también&el& objetivo(de(proporcionar(servicios(a(través(de(infraestructura(fija(conectada(a(Internet.! La principal ventaja de este tipo de redes es la no necesidad de infraestructura previa, a cambio de esto hacemos que todos los dispositivos que estén en la red estén consumiendo mucha más energía ya que se tiene que encargar de encaminar los paquetes de la red. Otro gran problema a tener en cuenta, es que como la información pasa por los nodos de la red, los usuarios, estos pueden leer fácilmente toda la información de la red. Si transportamos datos sensibles esto es un gran problema, pero como en nuestro la red transporta publicidad, no tenemos por qué tenerlo en cuenta. Es importante, tener como alternativa a WiFi, Bluetooth por dos razones. En primer lugar, hoy en día todos los teléfonos tienen Bluetooth, y en segundo lugar, por el ahorro de energía que supone basar nuestra red en esta tecnología. Aunque hay que recordar que esto nos limitará el alcance de la red y el ancho de banda disponible. El rango de Bluetooth esta entorno a los 10 metros mientras que WiFi está entorno a los 100 metros, siempre hablando de espacios abiertos. El reducido rango que tiene Bluetooth es debido a que, en su origen estaba pensado para sustituir cables simplemente, como ejemplo el cable del ratón o de la impresora. Una ventaja de WiFi frente a Bluetooth, es que como WiFi generalmente funciona con encaminamiento IP, soporta hasta 255 clientes (normalmente) en su red, y sin encaminamiento IP, soportaría todos los que soportara su capa física hasta saturarse, en cambio, Bluetooth debido a las características del protocolo sólo se pueden incluir en la red 7 nodos de forma simultánea. Bluetooth sólo trabaja a 2.4GHz, como frecuencia de trabajo, mientras WiFi puede trabajar tanto a 2.4GHz como a 5GHz, esto es gracias a la última versión de WiFi que puede transmitir en las dos frecuencias. En la siguiente tabla podremos ver una comparación entre los anchos de banda WiFi y Bluetooth en sus diferentes versiones. !!!!!!!!!!!!!Tabla%3–5.#Ancho#de#banda#Bluetooth!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!Tabla$3–6.#Ancho#de#banda#WiFi Versión! Ancho& de& banda%! Versión(1.2! 1!Mbit/s! Versión(2.0!+"EDR! 3!Mbit/s! Versión$3.0!+"HS! 24!Mbit/s! Versión(4.0! 24!Mbit/s! Bluetooth ya trabaja en modo ad hoc por lo tanto no hay que hacer nada para que trabaje de este modo. Para hacer nuestro servidor web compatible con Bluetooth, simularemos un interfaz IP encima de Bluetooth tal como se ve en la figura 2.2, de manera que se podrá acceder directamente al servidor web mediante Bluetooth, sin tener que modificar nada del código del servidor. Más adelante se detallará como se logra hacer esto. Los dispositivos que se conecten por Bluetooth recibirán su dirección IP a través de un servidor DHCP de forma similar a las redes WiFi. La única diferencia a nivel usuario es que se tendrán que parear los dispositivos. El#proceso#de#pareado,#es#un#mecanismo#de#seguridad!de#protocolo#Bluetooth#para#hacer#que#dos# dispositivos( puedan( empezar( a( comunicarse( entre!ellos.& Este& proceso& funciona& de& la& siguiente& manera,'uno'de'los'dos'dispositivos'tiene'que'estar'visible'para'el'otro,'(normalmente'en'teléfonos' Android,( en( las( opciones% de% Bluetooth% existe% una% opción% de% aparecer% visible),% con% el% otro% dispositivos(que(queremos(parear(deberemos(de(buscar(el(otro(dispositivo(dándole(a(la(opción(de( escanear.(Una(vez(que(el(nombre(del(otro(dispositivo(nos(aparezca(en(la(pantalla,(al(seleccionarlo,) nos$ pedirá$ que$ introduzcamos$ en$ pin$ el# cual# será# un# código# secreto# que# tendrá# configurado# el# dispositivo(que(esta(puesto(como(visible(como(clave.(Cuando(lo(introduzcamos,!los$dos$dispositivos$ quedarán) pareados) y) ya) podrán) enviarse) información) de) forma& automática.& Aunque& es& posible& deshabilitar+este+proceso,+no+es+nada+recomendable+ya+que+la+seguridad+en+Bluetooth+es+muy+baja+y+ este$es$el$único$mecanismo$de$seguridad$que$ofrece$el$protocolo.! Versión! Ancho& de& banda%! WiFi$B$(802.11b)! 11"Mbit/s! WiFi$G$(802.11g)! 54!Mbit/s! WiFi$N$(802.11n)! 600!Mbit/s!
! ! ! Introducción! ! ! 24! ! ! Vale%la%pena%mencionar,%que%hace%poco%apareció%un%nuevo%protocolo%llamado%WiFi%Direct%el%cual%va% destinado)a)reemplazar)las)redes)ad)hoc)mencionadas"al"principio"de"este"apartado."Este%protocolo% establece(una(conexión(WiFi!Direct!entre%dos!o"más!dispositivos,)pero)esto)únicamente!es#posible,! entre%dispositivos%compatibles,%es%decir,%WiFi%Direct%es%un%protocolo,!no#una#norma,#por#lo#tanto,#el# uso$de$WiFi$Direct$depende$mucho$de$los$dispositivos$que$vayamos$a$usar,$ya$que!cada$fabricante$ puede%implementarlo%con%ligeras%diferencias%que%puede%hacerlos%incompatibles%entre%ellos.%!Aunque& de#momento#no#es#una#norma,#todo#indica#que#dentro#de#poco#se#convertirá#en#un#estándar!de#facto," por$ésto,%propondremos%en%la%sección%de%futuros%trabajos%la%implementación%de%WiFi%Direct& en& el& sistema.! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! Figura''2.2!–!Modelo&de!capas%cliente"servidor(Bluetooth.!
!! 25! Diseño e implementación de un servidor web para mensajes publicitarios para redes ad hoc ! 3 SERVIDOR PARA PUBLICIDAD AD HOC PROPUESTO n"este"capítulo"vamos"a"describir"las"partes"de"las"que"consta"nuestro(sistema,(entendiendo(por(nuestro( sistema'el'servidor'web'y'la'interfaz'distribuida'para'gestionarlo.,Aquí!trataremos(la(gestión(de(datos( en# el# servidor,# # las# comunicaciones# entre# la# interfaz# y# el# servidor# además# de# obviamente# el# mismo# servidor( web.( Además' se' comentarán' las' cuestiones' del' proyecto' que' más' difíciles' de' resolver,( detallando(los(elementos(seleccionados(para(constituir(el(sistema.!! En# la# descripción# del# sistema# seguiremos# el# siguiente# método,# mencionaremos# que# tecnología# o# librería# hemos&usado,&luego&pondremos&un"breve!ejemplo'de'funcionamiento,'el#cual!extrapolaremos!a"nuestro"caso" particular.*De*esta*forma*queremos*dar*una*visión*global*de*cómo*se*ha*ido*construyendo*el*servicio*web*y*los* módulos(que(tiene.! 3.1 Arquitectura del sistema Distinguiremos,dos,partes,bien,diferenciadas,,la,primera,será!el#servicio#web,!!que$se$ejecutará$en! nuestro(caso(en(una(Raspberry(Pi,#y#por#otra#parte&tendremos&una&interfaz&gráfica%que%nos%servirá% para$administrar$los$datos$del$servicio$web.$Esta$interfaz!gráfica!se#podrá&ejecutar&en&la&Raspberry& Pi"o"en"cualquier"PC"que"tenga"acceso%vía!IP#a#la#Raspberry#Pi.#! Para$poner$en$marcha$este$sistema%hemos%usado%una%Raspberry%Pi"con"sistema"operativo"Raspbian." Hemos&configurado&la&Raspberry&Pi"para"que"tenga"IP"estática!en#su#interfaz#eth0#(en#nuestro#caso# 192.168.1.115," pero" podíamos" haber$ utilizado$ cualquier$ otra$ dirección$ IP$ del$ mismo$ rango).# Necesitamos*también*un*dongle'WiFi$USB!y"un"dongle'Bluetooth(USB!compatibles,con,Raspbian.! Para$el$desarrollo$del$sistema$hemos$elegido$Python$debido$a$que$Raspbian$está!muy$enfocado$al$ uso$de$este"lenguaje!de#programación.,En!segundo(lugar,!y!aún"más"importante,!por$las$virtudes$de$ este$lenguaje, tales como • Simple • Open Source • Lenguaje Orientado a Objetos • Lenguaje de Alto Nivel • Sintaxis clara La"sintaxis"clara"es"una"gran"virtud,!ya#que#está!demostrado,!que$un$programador$se$lleva$más"de"la" mitad&del&tiempo&leyendo&código,&por&lo&que&su&lectura&sea&amena&es&una&gran&baza¶&elegirlo.! Python' es' un' lenguaje' interpretado,' esta' característica' le' hace' muy' interesante' en' prototipos' de' investigación,!tales#como#el#que#se#presenta#en#este#proyecto.! El# servicio# REST# lo# hemos# desarrollado# con# la# librería# WSME,!la# cual# nos# permite# desplegar# un# servicio(web( simple( con(gran(facilidad,( además(de# forma# sencilla!podemos'cambiar'los'tipos'de' datos&que&devuelve&nuestro$ servicio.$ Tenemos&que&resaltar&que&hemos%tenido%problemas%con%esta% librería," usando" el" protocolo& SOAP,!por$ eso$ al$ final$ nuestro$ servicio" devuelve" datos" en" formato" JSON.! A"continuación"se"muestra"un"ejemplo'de'servicio'web'básico.! Ejemplo(3–1.!! from%wsme%import%WSRoot,%expose!#"importamos"la"librerías"necesarias! class%MyService(WSRoot):!!!!!!!#"Definimos"la"clase"que"hereda"de"WSRoot! !!!!@expose(unicode,.unicode)..#.El#primer#parámetro#es#el#tipo#que#devuelve#la#! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!#"función,!el#segundo#es#el#tipo#de#argumento#! E!
! ! ! Servidor(para(publicidad(ad(hoc(propuesto! ! ! 32! 3.2 Configuración del servidor Aquí%definiremos%como%configuraremos%el%servidor%empezando%definiendo%el%hardware%y%el%SO%que% utilizaremos,- ademásde- losque- soncompatibles.!Después& seguiremos& con& los& interfaces& inalámbricos,!y"por"último"los"módulos&necesarios¶&la&ejecución(de(nuestro(sistema.! 3.2.1 Hardware y SO Como$soporte$para$el$servidor$hemos$elegido$una$Raspberry$Pi.$Es#un#ordenador#de#placa#reducida# de# bajo# coste,# desarrollado# en# Reino# Unido# por# la# Fundación# Raspberry# Pi,# con# el# objetivo# de# estimular* la* enseñanza* de#ciencias#de#la#computación#en#las#escuelas." Lo" más!interesante( de( este( mini"ordenador'es'su'precio'que'según'el'modelo!puede%variar%entre%20%y%30%euros,%aunque%hay%que% decir& que& también& necesitaremos& una& tarjeta& SD& de& memoria& para& poder& almacenar& el& sistema% operativo)que)vayamos)a)usar)y)un)cable)USB!o"cargador"de"móvil"para"poder"usarla.!Este%cargador% de# móvil# deberá# de# dar# más!de# 1000!mA!para$ que$ la$ Raspberry$ Pi$ funcione$ correctamente," normalmente)en)la)web)oficial)recomiendan)entre)500mA)y)700mA)(según#el#modelo)#pero#estos# datos&se&dan¶&poner&en&marcha&solamente&la&Raspberry&Pi&como&tal&y&nosotros&tenemos&varios& dispositivos(conectados(a(ella."Nosotros"hemos"conseguido"poner"a"funcionar"nuestra"Raspberry"Pi" conectándola* a* los* USB!que$ tienen$ los$ routers!modernos,) por) lo) que) usar) estos) USB!como$ alimentación*es*una*opción*muy*interesante.! La#fundación#da#soporte#para#las#descargas#de#las#distribuciones#para#arquitectura#ARM,#Raspbian# (derivada(de(Debian),(RISC(OS(5,(Arch(Linux(ARM((derivado(de(Arch(Linux)&y&Pidora&(derivado&de& Fedora);)y)promueve)principalmente)el)aprendizaje)del)lenguaje'de'programación'Python.'Otros& lenguajes) también) soportados) son) Tiny) BASIC,12) C,) Perl5) y) Ruby.!Existen( varios( modelos( de( Raspberry)Pi.!! Tabla%3–1."Modelos,"Raspberry"Pi! Nombre" RPI$Model$A" RPI$Model$ A+" RPI$Model$B" RPI$Model$ B+" RPI$2$Model$ B" SoC! BROADCOM(BCM2835! BROADCOM( BCM2835! BROADCOM( BCM2835! BROADCOM( BCM2835! BROADCOM( BCM2836! CPU! ARM11!ARMV6! 700#MHZ! ARM11! ARMV6! 700#MHZ! ARM11! ARMV6! 700#MHZ! ARM11! ARMV6! 700#MHZ! ARM11! ARMV7! ARM$ CORTEX"A7! 4"NÚCLEOS" 900#MHZ! GPU! BROADCOM( VIDEOCORE( IV! 250$MHZ.$ OPENGL'ES' 2.0! BROADCOM( VIDEOCORE( IV! 250$MHZ.$ OPENGL'ES' 2.0! BROADCOM( VIDEOCORE( IV! 250$MHZ.$ OPENGL'ES' 2.0! BROADCOM( VIDEOCORE( IV! 250$MHZ.$ OPENGL'ES' 2.0! BROADCOM$VIDEOCORE$ IV! 250$MHZ.$OPENGL$ES$2.0! RAM! 256$MB! 256$MB! 512$MB! 512$MB! 1GB! Puertos( USB! 1! 1! 2! 4! 4! GPIO! 26! 40! 26! 40! 40! Ethernet! No! No! Si! Si! Si! Peso!en# g.! 45! 23! 45! 45! 45! Podemos' afirmar' sin' miedo' a' equivocarnos,!que$ también!funcionará* en* el* modelo!B+" y!en# el# modelo&2&B,!ya#que#sus#características#son!iguales(o!superiores.!Las$características$que$nos$interesa$ de#este#tipo#de#mini"ordenadores(son(su(reducido(tamaño(y(su(bajo%consumo%energético%ya%que%ésto$ hace%que%la%podemos%colocar%en%cualquier!lugar,'incluso&sin&usar&una&toma&de&corriente&&ya&que&este$ tipo% de% ordenadores!pueden$ ponerse$ en$ marcha$ con$ la$ corriente$ de$ un$ USB$ de$ un$ router$ o$ un$ ordenador'cercano.!
!! 33! Diseño e implementación de un servidor web para mensajes publicitarios para redes ad hoc ! ! ! ! ! ! ! ! ! ! ! ! ! !! ! ! ! Como$sistema$operativo$hemos$decidido$usar"Raspbian,"ya"que"es"la"versión"oficial"que"provee"la# Fundación)Raspberry)Pi.)Es)una)variante)de)Debian,)cualquier)variante)de)Debian)compatible)con)la) Raspberry)Pi)seria)adecuada)para)ejecutar)nuestro)servidor,)en)concreto)existe)una)distribución$de$ Raspbian%con$los$paquetes$indispensables$para$el$SO$por$lo$cual$también$sería!compatible'y'mucho' más$ligera$que$Raspbian.! Todo$el$sistema$es$compatible$con$Windows,$OS$X$y$Ubuntu.$Aunque$es$posible$que$en$algunos$ sistemas' operativos!tengamos)que)tener)cuidado)en# cómo!se# definen# las# variables# de# consola,!ya# que$de$un$sistema$a$otro,!puede%variar%o%incluso%como%se%instalan!algunos(paquetes.(Aun(así,!este$ proyecto(se(centrará(en(la(instalación(y(configuración(en(una(Raspberry(Pi(con(Raspbian(instalado.! 3.2.2 WiFi Para$el#interfaz#WiFi#hemos#decidido#usar#redes#ad#hoc,#al#igual#que#lo#harán"los"clientes,"de"esta" forma&ambos&ya&serán&responsables&de&la&infraestructura&de&la&red.&Las&redes&ad&hoc&son&un&tipo&de& red$descentralizada!en#la#que#todos#los#nodos#son#iguales,#en#nuestro(caso,"el!nodo$del$servidor$será$ un#poco#diferente#ya#que#será#del#cual#sale#la#información#nueva.! ! ! ! ! ! ! ! ! ! Lo#primero#que#tenemos#que#definir#es#el#dongle%WiFi%que%vamos%a%utilizar,%el%cual%debe%de"ser"USB" y"compatible"con"la"Raspberry"Pi"y"con"Raspbian,"ya"que"todos%no%lo%son,%lo%mismo%pasará!con$el$ dongle% Bluetooth.% Aquí% tenemos% una% de% las% muchas% listas% de% dispositivos% compatibles% que% se% ! Figura''3.6'–!Imagen%del%modelo%B! ! Figura''3.7!–!Dongle'USB!utilizado!Belkin'N300!
! ! ! Servidor(para(publicidad(ad(hoc"propuesto! ! ! 34! pueden&encontrar&por&internet.! http://elinux.org/RPi_USB_Wi"Fi_Adapters! En# general,!cualquier) dispositivo) que) sea) compatible) con) Ubuntu) lo) será) con) Raspbian.) Como) estamos' en' Raspbian,!para$ configurar$ la$ red$ WiFi!lo# tenemos# que# hacer# con# el# archivo# /etc/network/interfaces,"en"el"cual"definiremos"la"red"de"la"siguiente"manera.! auto lo iface lo inet loopback iface eth0 inet dhcp allow-hotplug wlan0 iface wlan0 inet static address 192.168.0.1 netmask 255.255.255.0 wireless-channel 1 wireless-essid Pi wireless-mode ad-hoc iface default inet dhcp De# esta# manera,# estamos# definiendo# una# red# WiFi# en# modo# ad# hoc,# llamada# Pi,# por# el# canal# 1,# poniendo'como'dirección'IP!la#192.168.0.1.#También#se#puede#ver#como#definimos#que#el#interfaz# Ethernet' obtenga'su'IP!mediante( DCHP$de$la$red$externa.$Recordamos$que$en$nuestro$ caso$ese$ servidor( DHCP(externo(lo( tenemos( configurado(para(que(nos( de(la( dirección( 192.168.1.115( en(el( interfaz) eth0.!Para$ que$ los$ cambios$ surjan$ efecto$ deberemos$ reiniciar$ el$ sistema$ o$ introducir$ los$ siguientes%comandos.! sudo ifdown wlan0 sudo ifup wlan0 Una$vez$hecho$ésto$ya$estará$disponible$la$red$WiFi.! Por$ último,$ como$ los$ clientes$ $ de$ nuestra$ red$ necesitan$ una$ dirección$ IP!para$ conectarse$ contra$ nuestro( servidor( web,( necesitaremos( un( servidor( DHCP( que( sirva% direcciones% en% la% red% WiFi.! Hemos&optado&por&el&servidor&isc"dhcp"server%que%instalaremos.! sudo apt-get install isc-dhcp-server Es#normal#que#durante#la#instalación#aparezca#algún#error.!En#concreto,#los#errores#aparecen##al#final# de#la#instalación#al"intentar"arrancar"el"servidor"por"primera"vez,"pero"al"modificar"su"archivo"de" configuración+ y+ volver+a+ iniciarlo+funcionará+ correctamente.+ Esto+es+ debido+a+ que+ el+ archivo+ por+ defecto'no'contiene'una'configuración'válida'para'nuestro'sistema.!Una$ vez$ instalado& tendremos& que$ modificar!sus# ficheros# de# configuración.# En# /etc/dhcp/dhcpd.conf!tendremos) que) añadir) la) definición(de(la(red(que(sobre(la(cual(queremos(servir(IP.! … subnet 192.168.0.0 netmask 255.255.255.0 { range 192.168.0.2 192.168.0.254; option routers 192.168.0.1; option broadcast-address 192.168.0.255; } … Hemos&elegido&que&las&direcciones&vayan&desde&la&192.168.0.0,&a&la&192.168.0.254,&esto&se&consigue& con$ la$ primera$ línea$ del$ ejemplo$ anterior,$ aplicando$ una$ máscara$ de$ 255.255.255.0$ a$ la$ subnet! seleccionada,+que+en+nuestro+caso+es+la+192.168.0.0,+aunque+podría+haber+sido+cualquier!otra.&Luego& en#las#opciones#indicamos#que#el#router#será#la#dirección#192.168.0.1#y#que#por#lo#tanto#puede#servir# el#resto#de#direcciones#excepto#esa,#en#último#lugar&indicamos&que&la&dirección&de&difusión&será&la&
!! 35! Diseño e implementación de un servidor web para mensajes publicitarios para redes ad hoc ! 192.168.0.255.)! En#este#segundo#tenemos#que#especificar#sobre#que$interfaz$estamos$escuchando,$en#/etc/default/isc" dhcp"server.! … INTERFACES="wlan0" … Una$vez$configurado$reiniciaremos$el$servicio¶&que#los#cambios#surtan#efecto.!! sudo service isc-dhcp-server restart En# estos# dos# últimos# archivos# que# hemos# modificado# existen# amplios# ejemplos,# de# diferentes# configuraciones,en,los,comentarios,del,mismo,archivo,por,lo,que,es,muy,interesante,leerlos.! 3.2.3 Bluetooth A"la"hora"de"configurar""nuestro"interfaz"Bluetooth,!primero'necesitamos'un'dongle%compatible%con% nuestro(sistema,(como(pasaba(en(el(interfaz(WiFi,!existen'listas'como"la#siguiente!donde%podemos% ver$que$dongles&son&compatibles.! http://elinux.org/RPi_USB_Bluetooth_adapters! ! ! ! ! ! ! ! ! ! ! ! ! Para$la$elección,!tenemos'que'tener'en'cuenta'que'nosotros'vamos'a'simular'un'interfaz'IP'sobre'el' interfaz) Bluetooth# y# por# lo# tanto# tendremos# que# elegir# un# dispositivo# que# sea# compatible,# para# configurarlo+como+NAP.!NAP$quiere$decir$Network(Access(Point,"es"decir"punto"de"acceso"a"la"red,! que$ normalmente$ será$ cableada,$ cuando$ hablamos$ de$ NAP$ generalmente$ hablamos$ de$ interconexiones* de* grandes* redes.* En* nuestro* caso,* es* mejor* llamarlo* AP* Access% Point," ya" que" nuestro(dispositivo(Bluetooth(tendrá(que(hacer(lo(mismo(que(cualquier(AP(WiFi(del(mercado.(El( propósito(de(un(NAP(o(AP,(es(el(de(interconectar(dos(redes,(en(nuestro"caso"interconectaremos"la" red$Bluetooth$con$la$red$del$interfaz$Ethernet$de$nuestra$Raspberry$Pi.!! Recomendamos*no*comprarlos*en*tiendas*de*dudosa*calidad,!ya#que#es#muy#fácil#que#ocurra#que#si# se# compran# dos$ Bluetooth$ del$ mismo$ modelo,$ estos$ vengan$ con$ la# misma# MAC.# Ésto$ nos$ ha$ sucedido(durante(el(desarrollo(de(este(proyecto,!y"aunque"existe"la"posibilidad"de"cambiar"la"MAC" mediante(software,(no(es(algo(fácil(de(hacer(en(Raspbian.! Existe'un'proyecto'llamado'Ecodroidlink,'del'cual'podemos'encontrar'todo'su"código"en"Github,! (https://github.com/ykasidit/ecodroidlink)!cuyo%objetivo%es%convertir%una%Raspberry%Pi%en%un%NAP%a% través'de'un'dispositivo'Bluetooth,'siendo'compatible'entre#Raspberry#Pi"!Raspberry$Pi,$Raspberry$ Pi"Android'y'Raspberry'Pi"PC.!Ésto$lo$hace$utilizando$la$librería$nativa$de$Linux$llamada$Bluez.!A" través'de'este'interfaz,'se#podrán#conectar#7#dispositivos#al#mismo#tiempo,"como"máximo,"por"las" ! Figura''3.8!–!Dongle'Bluetooth'utilizado!
! ! ! Servidor(para(publicidad(ad(hoc(propuesto! ! ! 36! limitaciones+propias+del+protocolo+Bluetooth.!Por# ésto,!en#la#documentación#de#este#proyecto#nos# proponen&usarlo¶&crear&una&conexión&inalámbrica&especial,&la&cual&no&se&verá!mermada&por&la& saturación*de*dispositivos*en*el*medio.! El#proyecto#por#defecto,#crea#el#NAP#con#el#interfaz#eth0,#de#manera%que%los%dispositivos%que%se% conecten& al& NAP& Bluetooth& obtendrán& IP& de& la& red& externa& en& nuestro& caso% 192.168.1.X,$que$se$la$ ofrecerá'el!servidor!DHCP%del%router%en%nuestro%caso.!! ! ! ! ! ! ! ! ! ! El# mismo# proyecto# nos!permite' cambiar' el' interfaz' que' queremos' hacer' accesible.' Pero' existen' problemas*al#intentar#crear#un#bridge!con$el$wlan0,$por$lo$tanto$hemos$optado$por$usar$el$proyecto$ tal$ cual$ sin$ modificar$ el$ interfaz$ al$ que$ hacemos$ el$ NAP.!Un# Puente& de& red!o" bridge!es# una# interconexión* de* redes* de* computadoras* que* opera* en* la* capa* 2* (nivel* de* enlace* de* datos)* del* modelo& OSI.& Interconecta& segmentos& de& red& (o& divide& una& red& en& segmentos)& haciendo& la& transferencia* de* datos* de* una* red* hacia* otra* con* base* en* la* dirección* física' de' destino' de' cada' paquete.(En(definitiva,(un(bridge(conecta(segmentos(de(red(formando(una(sola(subred.(Funciona(a( través'de'una'tabla'de'direcciones'MAC'detectadas'en'cada'segmento'al'que'está'conectado.! ! ! ! ! ! ! ! ! ! Para$hacer$accesible,$el$servidor$web$que$está!escuchando*en*el*interfaz*WiFi*para*los*usuarios*de* Bluetooth(que(tendrán(IP(de(la(red(externa(del(router,(utilizaremos(Iptables(para(asignar(el(puerto( 80#de#nuestro#interfaz#eth0,#al#puerto#80#del#interfaz#wlan0#donde#está!escuchando*nuestro*servidor* web.!Ésto$hace$que$nuestro$servidor$web$este$visible$para$todos$los$clientes#del#router,#si#queremos# que$éstos$no$puedan$acceder$al$servidor$tendríamos$que$configurar$Iptables$en$nuestro$router$para$ ello.! Para"poner"a"funcionar"Ecodroidlink,"necesitaremos+usar+los+siguientes+comandos.! git clone https://github.com/ykasidit/ecodroidlink.git cd ecodroidlink/ sudo ./edl_main & sudo sysctl net.ipv4.ip_forward=1 sudo iptables -t nat -A PREROUTING -d 192.168.1.115 -p tcp --dport 80 -j DNAT --to-destination 192.168.0.1:80 sudo iptables -t nat -A POSTROUTING -j MASQUERADE ! Figura''3.9!–!Esquema(utilizado,(NAP! ! Figura''3.10!–!Modelo&problemático!
!! 37! Diseño e implementación de un servidor web para mensajes publicitarios para redes ad hoc ! Una$ vez$ hecho$ ésto,!deberemos( parear( nuestro( móvil( con( la( Raspberry( Pi( con( el( código( 0000,( y! raspberrypi"0" !el# nombre# de# la# red# Bluetooth# (este%código!también(es(configurable(en(el(proyecto( Ecodroidlink).!Deberemos$habilitar$en$nuestro$dispositivo$móvil$$en$los$setting!de#la#red#Bluetooth,# el#acceso#a#internet.! Para$que$los$dispositivos$sean$compatibles$deben$de$tener$Android$4.0$o$superior,"aunque"en"la"web" del$proyecto$encontraremos$más"detalles"sobre"la"compatibilidad"del"mismo.! 3.2.4 Requerimientos del sistema En#este#apartado#comentaremos#los#paquetes#que#instalamos#con#pip!para$que$el$proyecto$pueda$ funcionar.!Pip!es#un#sistema#de#gestión#de#paquetes#se!utiliza'para'instalar'y'gestionar'paquetes'de' software) escrito) en) Python.) Muchos) paquetes) se) pueden) encontrar) en) el) índice) de) paquetes) de) Python'(PyPI).!En#la#siguiente#lista#aparecen#los#paquetes#seguidos#de#la#versión#necesaria.#En#esta# lista&no&aparece!WxPython!ya#que#según#el#sistema#operativo#varia#la#forma#de#instalación.! § setuptools>=5.7! Es# una# colección# de# mejoras# para# el# módulo# distutils!de# Python,# que# permiten# a# un# desarrollador( construir( y( distribuir( paquetes( de( Python( de( forma( sencilla,( en( especial$ cuando' dependen' de' otros' paquetes' de' Python' para' funcionar.' Es' una' librería' básica' de' Python'cualquier'proyecto'mediano'la'necesita'directa'o'indirectamente'por'las'librerías'que' use.! La#elección##de#este#paquete#estaba#condicionada#por#la#elección#del!resto&de&librerías&ya&que& es#necesaria#para#su##ejecución.! § bottle>=0.12.7! Es#una#librería#ligera#y#rápida##de#tipo#WSGI,#que!permite'atender'peticiones'HTTP,"mapear" funciones) a) URLs,) e) incluso) uso) de) templates!con$ paquetes$ como$ mako." Esta" librería" la" usamos#para#servir#la#información.! Hemos&elegido&esta&librería&porque&está!escrita(totalmente(en(Python(y(eso(nos(garantizaba( casi%totalmente%la%compatibilidad%entre%sistemas.!No"necesita"dependencias"de"otras"librerías" para$ funcionar$ y$ además$ era$ la$ que$ nos$ venía" por" defecto" en" los" ejemplos" de" uso" de" la" documentación, de, WSME.!Además,!es# una# forma& de& montar& un& servidor& HTTP!bastante' ligera,!lo#que#nos!viene%bien%para%ejecutarlo%en%una%Raspberry%Pi.! § WSME==0.6.1! Es#la!librería!encargada(de!transformar)la)información$en$la$forma$requerida$por$el$servicio$ que$queramos$usar.$Una$vez$transformada,!bottle!sirve&la&información.! La# alternativa!a" usar" WSME" y" bottle," era" Django" que" es" una" solución" bastante" válida% y% robusta,!pero%a%la%vez%un%tanto%pesada%para%una%Raspberry!Pi,#ya# que# es# un# sistema# con# bastante' limitaciones.' Al' igual' que' bottle,!esta% librería% también% está!escrita( totalmente( en( Python'por'lo'cual'era'ideal!para$nuestro$proyecto.$Además$esta$librería$nos$ ofrecía!unas% características) que) Django!no# daba,# por# ejemplo,& poder& cambiar& el& formado& con& el& que& devolvemos(la(información,(cambiando(solamente(una(línea(de(código.! § peewee>=2.3.0! Es# un# ORM# bastante# ligero# y# simple# escrito# en# Python,# lo# usamos# para# guardar# la# información* del* servicio.* Lo* elegimos* porque* está!escrito& en& Python& y& es& totalmente& compatible..!Además,(es#compatible#con#SQLite!y"MySql,"y!de#otras#muchas#bases#de#datos.# Es#una#librería#muy#ligera#ideal#para#usarla#en#una#Raspberry#Pi"y"además"es"una"librería" ampliamente)validada)por)la)comunidad)de)usuarios%de%Raspberry%Pi,#por#lo#que#la#hacía" una$opción$bastante$sólida.! § Pyro4==4.27! Es#una#librería!que$nos$permite$construir$aplicaciones$en$las$que$los$objetos$pueden$hablar$ entre%sí%en%la%red,%con%muy%poco%esfuerzo%de%programación.%Pyro!está%escrito%en%Python!y"por" lo#tanto#se#ejecuta#en#muchas#plataformas#y#versiones#de#Python,#incluyendo#Python#3.x."! Esta%librería%la%usamos%para%crear%la%interfaz%de%administración%distribuida.! Pyro4!es#la#alternativa#a#RMI#en#Python.#Está!escrita!totalmente(en(Python!y!tiene$una$gran$
! ! ! Servidor(para(publicidad(ad(hoc(propuesto! ! ! 38! comunidad) detrás.) Dispone) de) librerías!para$ crear$ clientes$ en$ otros$ lenguajes$ de$ programación+como+Java+o+.Net.+Era+una+opción+muy+sólida+ya+que+ya+se# había%trabajado% con$ RMI,!y" la" forma" de" trabajar" de" Pyro4!es# bastante# similar# una# vez# conocidas!las$ principales*diferencias.! ! § serpent==1.7! Es#una#librería#de#serialización#compatible#también#con#Java#y#.Net.#Esta#librería#la#usamos# para$serializar$la$información$que$intercambiamos$entre$servidor$y$cliente$(administrador).$ Esta% librería% es% usada% por$ Pyro4," aunque" también" podemos" elegir" otras" opciones" para" la" serialización,,pero%esta%es%la%opción%por%defecto%para%Pyro4.! Esta%librería%fue%elegida%simplemente%porque%es%la%que%viene%por%defecto%como%serializador% en#Pyro4!y"además"las"otras"opciones"que"teníamos"para"serializar,"tienen"conocidos"fallos"de" seguridad,*que*están*advertidos*en*la*documentación*para*desarrolladores*de*Pyro4.! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !
!! 39! Diseño e implementación de un servidor web para mensajes publicitarios para redes ad hoc ! 4 DESCRIPCIÓN DEL SERVICIO WEB MEDIANTE API REST n"esta"sección"mostraremos"todas"las"direcciones)de)nuestra)API!REST%con%un%ejemplo%de%los%datos%que% devolverían!cada$URL.!Las$URLs,!que$son$accesibles,!funcionan(de(la(siguiente(manera.!En#primer#lugar,# el#objeto#que#define#el#servicio#web,#que$en#nuestro#caso#sería"la"clase"ControladorWS"que"se"instancia" en#el#objeto#servicio#en#uno#de#los#métodos#de#la#clase#ControladorPyro,#tiene#definidos#unos#métodos## getMenus,) getOferta…!que$ serán" mapeados" a" las" siguientes" URL" de" forma!automática." Ya" que" únicamente!hay$que$definir$el$método.! 4.1 Introducción API REST Un#servicio#REST#es#un#tipo#de#arquitectura#software#para#sistemas#hipermedia#distribuidos#como# la#World&Wide&Web."El#término%se%originó%en#una#tesis#doctoral#sobre#la#web#escrita#por#Roy#Fielding,# uno$de$los$principales$autores$de$la$especificación&del&protocolo&HTTP.! Si#bien#el#término#REST#se#refería#originalmente#a#un#conjunto"de"principios"de"arquitectura,"hoy"en" día!se# usa# en# el# sentido# más# amplio# para# describir# cualquier# interfaz# web# simple# que# utiliza# XML/JSON&y&HTTP.&Es#posible#diseñar!servicios(web(de(acuerdo(con(el(estilo(arquitectural*REST!y" también( es( posible( diseñar( interfaces( XML!HTTP$ de$ acuerdo$ con$ el$ estilo$ de$ llamada$ a$ procedimiento+ remoto+ (RPC),+ pero+ sin+ usar+ SOAP.+ Estos+ dos+ usos+ diferentes+ del+ término+ REST,! causan& cierta& confusión&en&las&discusiones&técnicas,&aunque&RPC&no&es&un&ejemplo&de&REST.!Los$ sistemas'que'siguen'los'principios'REST'se'llaman'con'frecuencia$RESTfull.! La#API#REST#ha"disfrutado"de"una"gran"escalabilidad"debido""a:! • Un#protocolo#cliente/servidor#sin#estado:#cada$mensaje$HTTP$contiene$toda$la$información$ necesaria(para(comprender(la(petición.(Como(resultado,(ni(el(cliente(ni(el(servidor(necesitan( recordar'ningún'estado'de'las'comunicaciones'entre'mensajes.'! • Un# conjunto# de# operaciones# bien# definidas# que# se# aplican$ a$ todos$ los$ recursos$ de$ información:+HTTP+en+sí+define+un+conjunto+pequeño+de+operaciones,+las+más+importantes+ son$ POST,$ GET,$ PUT$ y$ DELETE.$ Con$ frecuencia$ estas$ operaciones$ se$ equiparan$ a$ las$ operaciones)CRUD)en)bases)de)datos.! • Una$ sintaxis$universal$para# identificar# los# recursos.# En# un# sistema# REST,# cada# recurso# es# direccionable+únicamente+a+través+de+su+URI.! • El#uso#de#hipermedios,#tanto#para#la#información#de#la#aplicación#como#para#las#transiciones# de# estado# de# la# aplicación:# la# representación# de# este" estado" en" un" sistema" REST" son" típicamente"HTML"o"XML.! En#definitiva,!una$API!REST%no%es%más!que$un!servicio(web(sobre(el(cual!se"pueden"usar"los"verbos" de#HTTP!(POST,'GET,'PUT'y'DELETE),'para'acceder'a'él.!Además'de'no'guardar'el'estado'de'los' clientes.)El#no#tener#estado#es#una#desventaja#clara,"ya"que!tener%que%pasar%el%estado%en%cada%llamada% es,$como$mínimo$tedioso,$pero$la$contrapartida$es$clara,!escalabilidad.*Para*mantener*un*estado*se* requiere&algún&sitio&(generalmente&memoria)&donde&guardar&todos&los$estados$de$todos$los$clientes.$ A"más"clientes,"más"memoria,"hasta"que"al"final"podemos"llegar"a"no"poder"admitir"más"clientes,"no" por$falta$de$CPU,$sino$de$memoria.$Es$algo$parecido$a$lo$que$ocurre$con$la$web$tradicional$(que$ también( es( “sin% estado”).# Sea$cual$sea$la$tecnología$con$la$que$desarrolles$para$web,$seguro$ que$ conoces&que&puedes&crear&lo&que&se&llama&una&“sesión”.&Los&datos&de&la&sesión&se&mantienen&entre& peticiones)y)son)por)cada)usuario.)El)clásico)ejemplo)de)sesión)puede)ser)un)carrito)de)la#compra,# entre%las%distintas%peticiones%web,%la%información%del%carrito%de%compra%se%mantiene.! En#nuestro#caso#únicamente)utilizaremos,peticiones,HTTP,GET,ya,que,nuestro,servicio!sólo#sirve# información,+ debido+ a+ que+ el+ único+ que+ puede+ administrar+ la+ información( ( del( servidor( es( el( administrador*que*lo*hará*mediante*nuestra*interfaz*distribuida*construida*con*Pyro4!y"WxPython.! A"continuación,"mostramos"el"esquema"básico"de"funcionamiento"de# una# API# REST.# Los# clientes# sólo# pueden# acceder# a# una# serie# de# URLs!mediante( peticiones( HTTP( y( es( el( propio( servidor( el( E!
! ! ! Descripción*del*servicio*web*mediante*api*REST! ! ! 40! encargado)de)hacer)las)consultas)a)la)base)de)datos)que)sean)necesarias.)Estableciendo)así,)una)capa) de#seguridad#entre!los$clientes$y$los$datos.! ! ! ! ! ! ! ! 4.2 Descripción del servicio Aquí% veremos% todas% las% direcciones% accesibles% de% nuestro% servicio% web,% es% interesante% fijarse% en# como$ se$ corresponden$ los$ datos$ de$ ejemplo$ con$ las$ estructuras$ de$ datos$ presentadas$ en$ la$ introducción* de* este* trabajo.!Los$ arrays!de# objetos$ están$encerrados$ entre$ corchetes$ ($[…]$)$y$los$ objetos'JSON%están&encerrados&entre&llaves&(&{…}&),"en"cada"caso"los"elementos"dentro"de"ellos"están" separados( por( comas.!En# estos# ejemplos# no# aparece# ninguna# imagen,!esto% es% debido% a% que% una% imagen'codificada$en$base64!son$demasiados$caracteres$para$poner$un$ejemplo.! ! § http://<dir>/ws/getMenus.json" Devuelve&todos&los&menús!que$están!disponibles*en*la*fecha*actual.! [! {"postres":![{"disponible":!true,!"modificado":!"2015;03;14T11:15:40.439098",! "precio":!3.0,!"descripcion":!"Tarta!de!queso!casera",!"nombre":!"Tarta!de! queso",!"id":!6}],!! "disponible":!true,!! "modificado":!"2015;03;14T11:15:40.427848",!! "fecha_fin":!"2015;03;31T00:00:00",!! "precio":!12.0,!! "segundos":![{"disponible":!true,!"modificado":!"2015;03;14T11:15:40.436135",! "precio":!3.0,"descripcion":!"Tortilla!de!patatas!tradicional",!"nombre":! "Tortilla",!"id":!5}],!! "primeros":![{"disponible":!true,!"modificado":!"2015;03;14T11:15:40.433318",! "precio":!2.0,!"descripcion":!"Ensalada!de!lechuga!y!tomate",! "nombre":!"Ensalada",!"id":!4}],!! "fecha_ini":!"2015;03;01T00:00:00",!! "descripcion":!"Bebidas!incluidas",!! "nombre":!"Menu!de!Marzo",!! "id":!1},! {!…!}! ]! ! Figura''4.1!–!Intercambio,de,mensajes,GET!
!! 41! Diseño e implementación de un servidor web para mensajes publicitarios para redes ad hoc ! ! § http://<dir>/ws/getMenu.json" Devuelve&el&último&menú!creado!disponible.! {! "postres":![{"disponible":!true,!"modificado":!"2015;03;14T11:15:40.439098",! "precio":!3.0,!"descripcion":!"Tarta!de!queso!casera",!"nombre":!"Tarta!de! queso",!"id":!6}],!! "disponible":!true,!! "modificado":!"2015;03;14T11:15:40.427848",!! "fecha_fin":!"2015;03;31T00:00:00",!! "precio":!12.0,!! "segundos":![{"disponible":!true,!"modificado":!"2015;03;14T11:15:40.436135",! "precio":!3.0,"descripcion":!"Tortilla!de!patatas!tradicional",!"nombre":! "Tortilla",!"id":!5}],!! "primeros":![{"disponible":!true,!"modificado":!"2015;03;14T11:15:40.433318",! "precio":!2.0,!"descripcion":!"Ensalada!de!lechuga!y!tomate",! "nombre":!"Ensalada",!"id":!4}],!! "fecha_ini":!"2015;03;01T00:00:00",!! "descripcion":!"Bebidas!incluidas",!! "nombre":!"Menu!de!Marzo",!! "id":!1! }! ! § http://<dir>/ws/getItems.json" Devuelve$todos$los$ítems!creados(disponibles.! [! {"disponible":!true,!! "modificado":!"2015;03;14T11:13:31.359864",!! "precio":!2.0,!! "descripcion":!"Ensalada!de!lechuga!y!tomate",!! "nombre":!"Ensalada",!! "id":!1},!! {"disponible":!true,!! "modificado":!"2015;03;14T11:13:49.056204",!! "precio":!3.0,!! "descripcion":!"Tortilla!de!patatas!tradicional",!! "nombre":!"Tortilla",!! "id":!2},!! {!…!}! ]! ! § http://<dir>/ws/getOfertas.json" Devuelve&todas&las&ofertas&disponibles&en&la&fecha&actual.! [! {"disponible":!true,!! "modificado":!"2015;03;14T11:16:31.035404",!! "ítems":![{"disponible":!true,!"modificado":!"2015;03;14T11:16:31.039735",! "precio":!2.0,!"descripcion":!"Ensalada!de!lechuga!y!tomate",!"nombre":! "Ensalada",!"id":!7}],!"precio":!1.0,!! "fecha_fin":!"2015;03;31T00:00:00",!! "fecha_ini":!"2015;03;01T00:00:00",!! "descripcion":!"Ensalada!a!mitad!de!precio",!! "nombre":!"Oferta!de!Marzo",!!
! ! ! Descripción*de*la*interfaz*gráfica*de*usuario! ! ! 48! § Incluye(su(propio(servidor(de(nombrado,(lo(cual(es(muy(útil(a(la(hora(de(crear(aplicaciones( distribuidas.$! § Es#compatible#con#IPV6.!! § Existen(librerías(para(crear(clientes(en(.NET(y(Java.(! § Es#una#librería#madura#con#más!de#10#años#de#existencia.! Vamos& a& aprovechar& para& hablar& del& funcionamiento& de& Pyro4!con$ un$ poco$ más!de# detalle.# La# arquitectura)de)cliente/administrador!distribuido!que$hemos$montado,$se$sustenta$en$dos$objetos$$y$ un#servidor#de#nombrado.!Tendremos)un)objeto)en)la)parte)del)servicio)que)se)estará)ejecutando)en) nuestra(Raspberry(Pi,(el(cual(contendrá(todos(los(métodos(de(CRUD!que$necesitemos$sobre&nuestra& base%de%datos.!En#computación#CRUD#es#el#acrónimo#de#Crear,#Obtener,#Actualizar#y#Borrar#(del# original(en(inglés:(Create,'Read,'Update'and'Delete).#Se#usa#para#referirse#a#las#funciones#básicas#en# bases%de%datos%o%la%capa%de%persistencia%en%un"software.! Es#importante!remarcar&que&en#este$objeto,!los$valores$que$devuelvan$sus$métodos$deben$de$estar$ serializados,+ ya+ que+ estos+ viajaran+ a+ través+ de+ la+ red+ hasta+ el+ cliente+ que+ lo+ esté!invocando." Este" concepto' tardamos' mucho' en' tenerlo' claro' y' nos' hizo$ gastar$ mucho$ tiempo$ de$ desarrollo$ en$ pruebas( por( eso( queremos( dejarlo( claro.( Para( ello( necesitamos( un( serializador,( en( nuestro( caso( usamos& Serpent,"que"es"el"que"trae"por"defecto"Pyro4!aunque&podríamos&haber&usado&otro&de&los& muchos'compatibles'con'esta"librería.! Una$ vez$ definido$ este$ objeto,!debemos' registrarlo' en' un' servidor' de' nombrado' para' que' el' administrador*pueda*encontrarnos.*Al*registrarlo*lo*registraremos*con*el*nombre*que*queremos*que* sea$conocido$nuestro$servicio$web.$Este$nombre$es$el$que$deberá%usar%el%cliente%para%encontrar%el% objeto&de&administración.! En#nuestro#caso#hemos#creado#un#segundo#objeto,#en#el#cliente,#que#aunque#no#es#necesario,#nos#es# muy$ útil$ para$ adaptar$ determinados$ datos$ entre$ la$ interfaz$ y$ el$ servicio.$ Además$ nos$ facilita" enormemente'el'proceso'de'reconexión'contra'otro'servicio.!Este%objeto%simplemente%encapsula%el% objeto&que&instanciamos&con&la&referencia&que&nos&da&el&servidor&de&nombrado&en&nuestro&cliente.! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !
!! 49! Diseño e implementación de un servidor web para mensajes publicitarios para redes ad hoc ! 6 GUÍA DE INSTALACIÓN aquí% tendremos% una" guía" completa" de" cómo" instalar" nuestro" servidor." Aunque" el" proceso" es" semiautomático,!explicaremos,bien,como,configurarlo,para,diferentes,topologías,y,explicaremos,las, diferentes)opciones)que)tenemos)a)la)hora)de)ponerlo)en)marcha.! El#proyecto#viene#con#unos%scripts%que%nos%sirven%para%hacer%la%instalación%de%forma%automática.%Explicaremos% que$hacen$estos$y$cuando$es$adecuado$usarlos,$ya$que$no$siempre$será$buena$idea$hacer$uso$de$ellos,$porque! están&programados&de&manera&muy&situacional¶&la&arquitectura$que$planteamos$desde$un$principio$en$ este$proyecto.!Es"decir,"una"Raspberry"Pi"y"un"ordenador"externo"que"la"administra"con"la"interfaz"gráfica"de" administración.! 6.1 Requisitos previos En#primer#lugar#definiremos#los#requisitos#mínimos,"o"por"así!decirlo,!lo#mínimo!que$tenemos$que$ tener% para% poder% proceder% a% la% instalación% de% nuestro% servidor.% Aunque% en% la% configuración% diferenciaremos,levemente,cuando,queramos,ejecutar,o,no,la,interfaz,,para,los,requisitos,previos, diremos(que(la(interfaz(tiene(los(mismos(requisitos)que)el)servicio)REST)en)cuestión,)incluso)menos.! Primero!necesitaremos+ una+ conexión+ a+ internet+ para+ descargar+ el+ proyecto+ con+ todas+ sus+ dependencias.*Luego*necesitaremos*el*ordenador*en*cuestión*en*el*cual*se*va*a*instalar*el*servicio.! Este$ordenador&no&tiene&por$qué!tener% ninguna%característica%especial,%sólo#necesitamos#que#tenga# una$conexión$a$internet$o$a$la$red$a$la$que$queramos$prestar$servicio$ya$sea$mediante$WiFi$o$cable$ Ethernet,!eso$ no$ es$ importante.$ El$ ordenador$ puede$ ser$ cualquier$ modelo$ de$ Raspberry$ Pi$ con$ Raspbian( instalado,( o( cualquier( ordenador# con# Ubuntu,# Windows# o# MAC# OSX.# !Aunque& recomendamos(no(utilizar(Windows(ya(que(es(un(poco(más!difícil'de'configurar.! Una$ vez$ que$ tenemos$ un$ ordenador$ con$ al$ menos$ un$ interfaz$ de$ red$ por$ el$ cual$ pueda$ servir$ nuestra(API!REST%y%uno%de%los%sistemas%operativos%mencionados.!Tendremos(que(asegurarnos(de( que$tengan!Git$y!Python'2.7.X'instalado'y'si'no'lo'está!proceder'a'su'instalación.'Desde'la'página! oficial' de' Python' podremos' descargarnos' el' intérprete!de# Python# para# casi# cualquier# sistema# operativo.*! https://www.python.org/downloads/! En#el#caso#de#Ubuntu#también#se#puede#instalar#usando#la#herramienta#“apt"get”.&En&Raspbian&ya& viene%con%el%sistema%operativo)por)lo)tanto)no)es)necesario)instalarlo.! Necesitamos* Git* para* poder* descargar* el* proyecto* de* Github.* Para* instalar* Git* necesitaremos* descargarlo*de*su*sitio*web.! http://git"scm.com/! Para$ los$ usuarios$ de$ Windows!recomendamos% utilizar% la% consola% nueva% que% se% instala% con% la% instalación) de) Git) en) Windows.) En# Raspbian# no# tenemos# ningún# problema# ya# que# el# sistema# operativo)ya)lo)trae)por)defecto.)! Lo#siguiente#que#necesitamos(es(la(herramienta(pip!que$nos$permite$instalar$librerías$de$Python$de$ forma&fácil&y&rápida.&Para&comprobar&si&está!instalada(con(nuestra(instalación(de(Python(abrimos( una$ terminal$ e$ intentaremos& ejecutar& el& comando& pip," si" la" terminal" nos" advierto" de" que" es" un" comando' desconocido,$ ésto$ querrá!decir" que" no" tenemos" pip!instalado.* Para* instalar* * pip," descargaremos*el*código*fuente*del*siguiente*link*y*lo*guardaremos*con*el*nombre*get"pip.py.! https://bootstrap.pypa.io/get"pip.py! Acto% seguido% lo% ejecutaremos( con( el( intérprete!de# comandos# de# Python.# Una# vez# terminado# la# ejecución(ya(tendremos(disponible(el(comando(en(nuestra(consola(para(poder(ejecutarlo.(En(el(caso( de#Raspbian#o#incluso#Ubuntu#también#podremos#instalarlo#usando#el#siguiente#comando.! sudo apt-get install python-pip Con$todo$lo$mencionado$hasta$ahora$tenemos$los$requisitos$para$la#instalación#de!nuestro(servicio( en#un#entorno#normal#con#redes#normales,#es#decir#sin#redes#ad#hoc#y#sin#Bluetooth.! A!
! ! ! Guía%de%instalación! ! ! 50! Si#quisiéramos#usar#Bluetooth!(sólo# en#sistemas$Raspbian$y$Ubuntu)$tendríamos$que$instalar$las$ dependencias) del) sistema) que) vamos) a) utilizar) y) descargar) el) proyecto) Ecodroidlink$ en$ otro$ directorio(diferente.! sudo apt-get install git-core python-dbus python-gobject bluez bridge-utils git clone https://github.com/ykasidit/ecodroidlink.git Si# quisiésemos# usar# redes# ad# hoc# tendríamos# crearla,# esto# en# cada# sistema# operativo# es# bastante# diferente,)por)lo)que)no)vamos)a)entrar)en)detalles.)Si)quieres)hacerlo)en)un)sistema)Ubuntu)puedes) coger&como&modelo&el&archivo(“configura.sh”(del(proyecto(el(cual(está!hecho%para%la%configuración% tanto%de%interfaces%como%de%descarga%de%paquetes!con$pip!(instalación*de*pip!incluida).*En"Raspbian" con$interfaz$Bluetooth$y$WiFi.! Para$usar$la$interfaz!distribuida)tendremos)que)instalar(la(librería(gráfica,!tendremos)que)instalar) WxPython,"para"ello"!existe&una&magnífica%web%donde%explica%paso%por%paso,!como$instalarlo$en$los$ distintos' sistemas' operativos.' En' el' caso' de' Raspbian' debemos' de# seguir!los$ pasos$ del$ sistema$ operativo)Debian&ya&que&Raspbian&esta&basado&en&Debian.! http://wiki.wxpython.org/How%20to%20install%20wxPython! 6.2 Instalación Para$empezar$tendremos$que$descargar$el$proyecto$de$Github$usando$Git.$Podemos&hacerlo&usando& el#siguiente#comando! git clone https://github.com/xXcoronaXx/servidor-adhoc.git Una$vez$hecho$esto$tendremos$todo$el$código$de$proyecto$descargado$en$una$carpeta$que$se$habrá$ creado.( A"partir"de"ahora"tendremos"que"proceder"a"la"instalación&de&los&paquetes&o&librerías&de& Python'que'necesita'nuestro'servidor'para'funcionar.'Para'ello'nos'movemos'a'la'carpeta'donde' esta%el%código%y%ejecutamos%el%siguiente%comando.! sudo pip install -r requeriments.txt Este%comando%lo%que%hace%es,%instalar%con$ pip!todos%los%paquetes%que%se%encuentran%escritos%en%el% archivo(requeriments.txt,!a"partir"de"la"versión"indicada"en"el"mismo"archivo."Una"vez"terminada"la" ejecución,!tendremos) todas) las) librerías) instaladas.) Recordamos) que) la) librería) para) la) interfaz! gráfica(la(hemos( incluido(en!los$ requisitos$ previos$ y$ que$ sólo# es# necesaria# su# instalación# en# este# ordenador'si'vamos'a'ejecutar'la'interfaz'en'el'mismo'equipo'donde'se'ejecuta'el'servidor.! Con$ esto$ ya$ podríamos$ ejecutar$ nuestro$ servidor$ (a$ falta$ de!configurarlo),) sin) usar) Bluetooth$ ni$ redes%ad%hoc,%es%decir,%en%una%interfaz%de%red%normal.! Para$el$proceso$de$instalación$en$una$Raspberry$Pi,"hemos"creado"un"archivo"llamado"configura.sh" el#cual,!nos$sirve$para$hacer$una$instalación$automática$y$configuración"también"de"los"interfaces"y" paquetes(necesarios(para(ejecutar(el(servidor(en(las(condiciones(que(se(vienen(exponiendo(durante( todo$este$documento.!Es"decir,"mediante"redes"ad"hoc"y"Bluetooth,"con"la"Raspberry"Pi"conectada"a" un# router# que# le# asigne# la# IP# mediante( un( servidor( DHCP.( Este( archivo( también( se( puede( usar( aunque& no& se& vayan& a& usar& ni& Bluetooth& ni& WiFi,& pero& instalará& paquetes& no& necesarios( en( la( Raspberry)Pi.)Durante(la(ejecución(del(fichero(configura.sh(mostrará(algunos(errores(al(instalar(el( servidor& DHCP,& esto& es& completamente& normal,& luego& todo& funciona& bien.!Estos% errores% son% los% mismos%que%comentamos%que%aparecerían%durante%la%instalación%del%servidor%DHCP.! Es#importante#recalcar#que#si#vamos#a#usar#WiFi#ad#hoc,!es#necesario#instalar#un#servidor&DHCP&en& nuestro(equipo(que(sirva(IP(sobre(la(red(ad(hoc.!La#configuración#de#este#tipo#de#redes#es#un#poco# compleja,!ya#que#cada#sistema#operativo#la#hace#a#su#manera.#En#cualquier#caso,!si#leemos#el#archivo# de#configura.sh#podréis#ver#groso#modo#como"se"configura"este"tipo"de"redes"en"entornos"Linux." Simplemente)hay)que)modificar)el)archivo)interfaces)del)sistema)operativo)e)instalar)un)servidor) DHCP%que%se%encargue!de#gestionar#las#IP#sobre#la#red.#El#servidor#DHCP#en#realidad#en#una#red#ad# hoc$no$hace$falta$pero$en$nuestro$caso$necesitamos$que$los$clientes$tengan$una$IP$para$que$puedan$
!! 51! Diseño e implementación de un servidor web para mensajes publicitarios para redes ad hoc ! acceder&al&servicio&web.! Con$ todo$ ésto$ya$tendríamos$la$instalación$completa$y$tenemos$que$pasar$a$la$configuración$del$ servicio(que(en(función(de(las(características!de#nuestros#interfaces#y#de#donde#queramos#ejecutar# nuestra(interfaz(variará(un(poco.! 6.3 Configuración En#esta#sección#propondremos#una#serie#de#entornos#en#los#cuales#indicaremos,"cuál" debe" ser" su" configuración+ tanto+ en+ el+ servicio+ como+ en+ la+ interfaz,+ además# de# cómo# se# deben# de# poner# a# funcionar.!En# esta# sección# se# presupone# que# tiene# el# proyecto# principal# descargado# y# todos# los# requisitos)previos)satisfechos.! Los$archivos$que$hay$que$modificar$para$la$configuración$de$los$diferentes$escenarios$son$el$archivo# setting.py!de#la#raíz#del#proyecto#y# el#archivo#setting.py!de#la#carpeta#interfaz.#El# primero#son#las# opciones(de(configuración(del(servicio(web,!y"el"segundo"son"las"opciones"de"configuración"de"la" interfaz)de)administración.! ! Raspberry)Pi)(Bluetooth)+!WiFi$ad$hoc)" Este%es%el%caso%estándar%propuesto,%en%la%memoria%de%este%trabajo%y%por%ello%hay%un%archivo%llamado% start.sh'que'se'encarga'de'poner'el'servicio'en'marcha''siempre'que'nuestra'interfaz'Ethernet'tenga' la#dirección#192.168.1.115#y# nuestra#interfaz$WiFi$tenga$la$dirección$192.168.0.1.$En$caso$de$tener$ otras&direcciones,"sólo#habría#que#entrar#en#ese#script#y#modificar#las#direcciones.#El#setting.py!del$ servicio(web(debería(quedar(de(la(siguiente(forma.! DATABASE = 'ws.db' OBJETO_PYRO = 'servidor1.configura' DIRECCION_PYRO = '192.168.1.115' DIRECCION_PYRO_LOCAL = '192.168.1.115' KEY = 'the_same_string_for_server_and_client' # Configuracion WS WEBPATH = '/ws' PROTOCOL = 'restjson' DIRECCION_WS = '192.168.0.1' PUERTO_WS = 80 # Configuracion Caster DIRECCION_BLUETOOTH = "192.168.1.115:80" MENSAJE = "Servidor de publicidad de restaurante" PUERTO_DIFUSION = 44444 TIEMPO_ANUNCIOS = 30 DATABASE'es'el'nombre'del'archivo'donde'se'guardará!la#base#de#datos#sqlite3.#OBJETO_PYRO#es# el# nombre# que# usaremos# para# identificar* el* servicio* web* en* el* servidor* de* nombrado.* DIRECCION_PYRO+es+la+dirección+del+servidor+de+nombrado.+DIRECCION_PYRO_LOCAL+es+la+ dirección( desde( la( cual( el( objeto( que( hacemos( público!para$ administrar$ nuestro$ servicio$ será$ accesible.) KEY) es) una) clave% de% seguridad% todos% los% sistemas% deben% de% usar% la% misma.% Luego!las$ opciones(que(nos(quedan(son(para(configurar(la(API!REST,&a!partir!de#que#path!estarán(disponibles( los$métodos,$la$dirección$en$ la$que$estará$accesible$el$servicio$web$y$su$puerto.$Una$vez# con# los# setting!a" punto" podremos" proceder" a" ejecutar# el# servicio# en# este# caso!tentemos' que' ejecutar' el' siguiente(comando.! sudo sh start.sh Este% archivo% activa!el# Bluetooth# y# hace# que# nuestro# servicio# sea# accesible# mediante# Bluetooth.! Arranca%el%servidor!de#nombrado#en#la#interfaz#Ethernet#y#por#último#arranca#nuestra#API$REST.& Para$la$ejecución$del$servidor$de$nombrado$sí!vamos&a&usar&únicamente&un&servidor&y&la&interfaz&de& administración+en+local.!Podemos&ejecutarlo&sin&indicar&ninguna&dirección,"ya"que!así!se#ejecutará! por$defecto$en$localhost.!En#el#caso#de#ejecutarlo#en#local#tendríamos!que$modificar$las$direcciones$
! ! ! Guía%de%instalación! ! ! 52! de#pyro#y#ponerlas#como#“localhost”.! Para$ejecutar$la$interfaz$tenemos$dos$opciones$de$configuración$según$queramos$ejecutarla$en$local,! o" en" remoto." Es" muy" importante" tener" los" setting!de# la# interfaz# bien# puestos,# porque!si# no,!no# funcionará.! Para$ejecutarla$en$local.! OBJETO_PYRO = 'servidor1.configura' DIRECCION_PYRO = '@localhost' PROXY_PYRO = 'PYRONAME:'+OBJETO_PYRO+DIRECCION_PYRO KEY='the_same_string_for_server_and_client' Para ejecutarla en remoto. OBJETO_PYRO = 'servidor1.configura' DIRECCION_PYRO = '@192.168.1.115' PROXY_PYRO = 'PYRONAME:'+OBJETO_PYRO+DIRECCION_PYRO KEY='the_same_string_for_server_and_client' DIRECCION_PYRO+en"este"caso"debe"de"ser"la"dirección"donde"se"está!ejecutando*el*servidor*de* nombrado.)Una)vez)tenemos)los)setting!a"punto,!tenemos'que'irnos'al'directorio'interfaz!y"ejecutar"el" siguiente(comando.!Acto%seguido%se%abrirá%la%interfaz%y%se%conectará%automáticamente'al'objeto'pyro' que$tenemos$definido$en$los$setting."Es"muy"importante"que"este"objeto"este"accesible,!porque!si#no,! no#funcionará#al#intentar#conectares#a##otro#servicio.! python InterfazServidor.py " Raspberry)Pi)(Bluetooth)" En# este# caso,# para# poner!a" funcionar" el" Bluetooth" necesitaremos" que" estemos" conectados" por" Ethernet'a'una'red'que'nos'sirva'direcciones'mediante'DHCP,'en'teoría,'es'posible'configurar'el' proyecto( para( que( la( misma( Raspberry% Pi% sirva% las% IP% pero% por% falta% de% tiempo% no% lo% hemos% conseguido.)Pondremos(nuestro(servicio(accesible(directamente(en(el(interfaz(Ethernet.(En(el(caso( anterior!nuestro( servicio( web!también( es( accesible( en( la( IP( de( la( interfaz( Ethernet," pero" esto" es" debido&a&que&hacíamos&accesible&la&IP&del&WiFi&en&el&interfaz&Ethernet&usando&Iptables.!Entonces( para$este$caso$los!setting!del$servicio$serán$los$siguientes.! DATABASE = 'ws.db' OBJETO_PYRO = 'servidor1.configura' DIRECCION_PYRO = '192.168.1.115' DIRECCION_PYRO_LOCAL = '192.168.1.115' KEY = 'the_same_string_for_server_and_client' # Configuracion WS WEBPATH = '/ws' PROTOCOL = 'restjson' DIRECCION_WS = '192.168.1.115' PUERTO_WS = 80 # Configuracion Caster DIRECCION_BLUETOOTH = DIRECCION_WS MENSAJE = "Servidor de publicidad de restaurante" PUERTO_DIFUSION = 44444 TIEMPO_ANUNCIOS = 30 Suponiendo)claro)que)la)IP)que)tenemos)en)la)interfaz)Ethernet))es)la)192.168.1.115.!Como$en$el$caso$ anterior( si( fuésemos( a( usar( un( único( servidor( y!quisiéramos* usar* la* interfaz* sólo,!en# local,! deberíamos*de*poner*las*direcciones*pyro*como*“localhost”.!Con$estos$setting,!para$poner$el$servicio$ en#marcha#tenemos# que#ejecutar# los# siguientes#comandos.#Primero# nos# situamos#en# el#directorio# donde%tenemos%descargado%el%proyecto%Ecodroidlink.! sudo ./edl_main
!! 53! Diseño e implementación de un servidor web para mensajes publicitarios para redes ad hoc ! Con$esto$encenderemos$el$Bluetooth.$Ahora!nos$vamos$a$donde$está!el#código#de#nuestro#proyecto# y"ejecutamos"lo"siguiente.! export PYRO_HMAC_KEY='the_same_string_for_server_and_client' python -m Pyro4.naming --host 192.168.1.115 & sudo python service.py Con$esto$nuestro$servicio,!estará'accesible'en#la#dirección#192.168.1.115#y#para#todo#el#que#se#conecte# a" nuestro" Bluetooth.!Si# fuéramos!a" usar" la" interfaz" sólo" en" local" y" sólo# con# un# único# servidor# podríamos)arrancar)el)servidor)de))nombrado)sin)la)opción)“"!"!host!“,#para#que#se#ejecute#de#forma# automática)en)localhost.)Para)ejecutar)la)interfaz)tenemos)dos)opciones)de)setting.! Para ejecutar la interfaz en local. OBJETO_PYRO!=!'servidor1.configura'! ! ! DIRECCION_PYRO!=!'@localhost'! ! ! PROXY_PYRO!=!'PYRONAME:'+OBJETO_PYRO+DIRECCION_PYRO! KEY='the_same_string_for_server_and_client'! Para ejecutar la interfaz en remoto. OBJETO_PYRO!=!'servidor1.configura'! ! ! DIRECCION_PYRO!=!'@192.168.1.115'! ! ! PROXY_PYRO!=!'PYRONAME:'+OBJETO_PYRO+DIRECCION_PYRO! KEY='the_same_string_for_server_and_client'! Con$esto$ya$podríamos"irnos"a"la"carpeta"de"la"interfaz"y"ejecutar"la"interfaz.! python InterfazServidor.py ! Raspberry)Pi)(WiFi)o)WiFi)ad)hoc))" En#este#caso,!las$configuraciones$tanto$para$WiFi$como$para$WiFi$ad$hoc$son$idénticas$lo$único$que$ hay$ que$ tener$ en$ cuenta$ que$ con$ ad!hoc$ hay$ que$ instalar$ un$ servidor$ DHCP$ y$ con$ figurarlo,$ mientras)que)en)el)WiFi)normal)no)es)necesario.)Lo)único)que)puede)cambiar)son)las)IP)que)usamos) ya#que#en#un#entorno#WiFi#normal#es#posible#que#la#Raspberry#Pi#no#sea#el#punto#de#acceso.!Los$ setting!del$servicio$web$deberían$ser$algo$parecido$a$estos! DATABASE = 'ws.db' OBJETO_PYRO = 'servidor1.configura' DIRECCION_PYRO = '192.168.1.115' DIRECCION_PYRO_LOCAL = '192.168.1.115' KEY = 'the_same_string_for_server_and_client' # Configuracion WS WEBPATH = '/ws' PROTOCOL = 'restjson' DIRECCION_WS = '192.168.0.1' PUERTO_WS = 80 # Configuracion Caster DIRECCION_BLUETOOTH = "192.168.1.115:80" MENSAJE = "Servidor de publicidad de restaurante" PUERTO_DIFUSION = 44444 TIEMPO_ANUNCIOS = 30 Todo$suponiendo$que$red$WiFi#esta#en#el#rango#192.168.0.X,"y"la"red"Ethernet"192.168.1.X."En"el"caso" de# que# no# queramos# conectar# nuestra# Raspberry# Pi# por# Ethernet# tendríamos# que!usar% la% IP% 192.168.0.1(para(los(objetos(pyro,(para(que(la(interfaz(de(administración(se(pueda(conectar(a"través! del$WiFi.!Para$ejecutar$el$servicio$tenemos$que$ejecutar$los$siguientes$comandos,$situándonos$en$el$ directorio(del(proyecto.! export PYRO_HMAC_KEY='the_same_string_for_server_and_client'
! ! ! Guía%de%instalación! ! ! 54! python -m Pyro4.naming --host 192.168.1.115 & sudo python service.py En# el# caso" de" únicamente" usar% la% interfaz% WiFi,% a% la% hora% de% arrancar% el% servidor% de% nombrado% tendremos)que)cambiar)la)opción)de)host,!a"la"IP"que"tengamos"en"la"interfaz"WiFi!(en" este" caso" suponemos( que( tenemos( la( IP( 192.168.0.1).!Ahora& tenemos& dos# opciones# para# los# setting!de# la# interfaz)que)son)las)siguientes.! Para$ejecutarla$en$local.! OBJETO_PYRO = 'servidor1.configura' DIRECCION_PYRO = '@localhost' PROXY_PYRO = 'PYRONAME:'+OBJETO_PYRO+DIRECCION_PYRO KEY='the_same_string_for_server_and_client' ! Para$ejecutarla$en$remoto.! OBJETO_PYRO = 'servidor1.configura' DIRECCION_PYRO = '@192.168.1.115' PROXY_PYRO = 'PYRONAME:'+OBJETO_PYRO+DIRECCION_PYRO KEY='the_same_string_for_server_and_client' Ahora&sólo#tendríamos#que#movernos#a##la#carpeta#de#la!interfaz)y)ejecutar.)Si)únicamente!usamos&la& interfaz)WiFi,!tendremos)que)cambiar)la)DIRECCION_PYRO)a"la"dirección"que"tenga"en"la"interfaz$ WiFi$el$equipo$donde$se$esté!ejecutando*nuestro*servicio*web.! python InterfazServidor.py ! Raspberry)Pi)(Solo)Ethernet)" Este%caso%es%idéntico%al%caso%de%solo%Bluetooth,!con$la$única$diferencia$de$que$no$hace$falta"poner"a" ejecutar(el(proyecto(Ecodroidlink(ya(que,!este$solo$sirve$para$manejar$la$interfaz$Bluetooth.$Por$lo$ tanto,!solo$tenemos$que$copiar$los$setting!de#ese!apartado,!situarnos)en)el)directorio)el)proyecto,!y" ejecutar(el(servicio(de(la(siguiente(manera.! export PYRO_HMAC_KEY='the_same_string_for_server_and_client' python -m Pyro4.naming --host 192.168.1.115 & sudo python service.py En#la#interfaz,#pasa#exactamente%lo%mismo%copiamos%los%setting,"nos"situamos"en"el"directorio"de"la" interfaz)y)ejecutamos.! python InterfazServidor.py Agregar&servicios&al&conjunto&administrable" En# este# apartado,!queremos( dejar( claro( cómo!habría& que& configurar& los!servicios( que( queramos$ agregar!para$ poder$ administrarlos$ todos,!desde$ la$ misma$ interfaz$ de$ administración.$ Estos$ conjuntos( van( ligados( cada( uno( a( su( servidor( de( nombrado.( Es( decir,( una( interfaz,( podrá( administrar)todos)los)servicios)que)se)registren)en)el)mismo)servidor)de)nombrado."! Por$ejemplo,$un$servicio$que$se$quiera$agregar$al$primer$ejemplo$de$escenario$tendría$que$tener$ estos%setting.! DATABASE = 'ws.db' OBJETO_PYRO = 'servidor2.configura' DIRECCION_PYRO = '192.168.1.115'
!! 55! Diseño e implementación de un servidor web para mensajes publicitarios para redes ad hoc ! DIRECCION_PYRO_LOCAL = '192.168.1.116' KEY = 'the_same_string_for_server_and_client' # Configuracion WS WEBPATH = '/ws' PROTOCOL = 'restjson' DIRECCION_WS = '192.168.0.2' PUERTO_WS = 80 # Configuracion Caster DIRECCION_BLUETOOTH = "192.168.1.116:80" MENSAJE = "Servidor de publicidad de restaurante" PUERTO_DIFUSION = 44444 TIEMPO_ANUNCIOS = 30 Como$ veis$ la$ DIRECCION_PYRO_LOCAL$ cambia$ a$ la$ IP$ que$ tengamos$ en$ nuestro$ interfaz$ Ethernet,(OBJETO_PYRO(cambia(para(que(no(tengamos(conflictos(de(nombres(en(el(servidor(de( nombrado(y(por(último(DIRECCION_WS(también#cambia#como#es#lógico#(estamos#suponiendo#que# están&en&la&misma&red&los&servicios&web&por&eso&tienen&que&tener&IP&diferentes).! Para$administrar$este$servicio$tendríamos$que$arrancar$la$interfaz$de$administración,!ir#a#la#opción# de#reconectar#y#elegir#el!servicio(“servidor2.configura”,(acto(seguido(se(actualizará(la(interfaz(con( todos%los%datos%de%ese%servicio.!! Recordamos*que*este*proyecto*tiene*la*limitación*de*que*la*interfaz*distribuida*no*tiene*la*capacidad* de# cambiar# de# servidor# de# nombrado# una# vez,!la# aplicación# ha# sido# iniciada," por" lo" que," recomendamos%que%si%se%quiere%cambiar%de%servidor%de%nombrado,%primero%se%cierre%la%aplicación,% luego&se&modifiquen&en&los&setting!la#dirección#del#servidor#de#nombrado#al#que#queremos#acceder# para$ posteriormente" volver" a" iniciar" la" interfaz.!Al# cambiar# de# servidor# de# nombrado# estaremos# cambiando) de) conjunto) administrable) y,# por!lo# tanto,!únicamente!podremos( ver( los( objetos( que( estén®istrados&en&ese&servidor&de&nombrado.! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !
! ! ! Ejemplo(de(uso.(Demostrando(y(validando(la(aplicación! ! ! 56! 7 EJEMPLO DE USO. DEMOSTRANDO Y VALIDANDO LA APLICACIÓN quí$veremos$primero$como$será$el$escenario$en$el$que$vamos$a$realizar$las$pruebas.$A$continuación$ enumeraremos)las)pruebas)que)realizaremos,)mostrando)los)resultados.)Por)último,!incluiremos+una+ prueba'de'esfuerzo!desarrollada%con%JMeter.! 7.1 Preparación del escenario Para$preparar$nuestro$ escenario$necesitaremos,$un$ router,$dos$ Raspberry$ Pi$ y$ un$ ordenador.$En$ nuestro(caso( el( ordenador(será(un(Macbook(Pro(que(se( conectará(a( la(red(WiFi(del(router.!Este$ ordenador'será'el'que'ejecute!la#interfaz#para#administrar#los#servicios#que#están#corriendo#en#los# mini"ordenadores.!Ambas&Raspberry&Pi&deben&de&tener&descargado&el&proyecto&y&ejecutado&el&script& de#configuración.#! Nuestro(ordenador(debe(de(tener(instalados(los(requerimientos(del#proyecto#(requeriment.txt)#y#la# librería(grafica(para(poder(cargar(la(interfaz((WxPython),#la#instalación#de#la#misma#puede#variar# según& el& sistema& operativo,& por& lo& tanto,& recomendamos& seguir& los& pasos& indicados& para& su& instalación!en#su#página#web#oficial.! ! ! ! ! ! ! ! ! ! ! ! ! Las$Raspberry$Pi$estarán$conectadas$por$cable$al$router,$ambas$tendrán$asignadas$IP$estáticas&por& configuración+ en+ el+ router.+ La+ primera+ tendrá+ la+ dirección% 192.168.1.115% y% la% segunda!tendrá' la' 192.168.1.116.%La%primera%tendrá%dongle%Bluetooth%y%WiFi,%mientras%que%la%segunda%no%tendrá%nada,% por$lo$tanto$la$segunda$solo$servirá$su$servicio$por$el$interfaz$Ethernet.$La$primera$Raspberry$Pi$ será%la%encargada%de%ejecutar%el%servidor%de%nombrado%mientras$que$la$otra$tendrá$que$registrar$sus$ servicios!en#la#primera.! Los$setting!de#la#interfaz#en#el#ordenador#del#administrador#en#este#contexto#serán.! # variables globales para conectarse con el servidor OBJETO_PYRO = 'servidor1.configura' # nombre del objeto que buscamos DIRECCION_PYRO = '@192.168.1.115' PROXY_PYRO = 'PYRONAME:'+OBJETO_PYRO+DIRECCION_PYRO A! ! Figura''7.1#–!Esquema(del(escenario!
!! 57! Diseño e implementación de un servidor web para mensajes publicitarios para redes ad hoc ! KEY='the_same_string_for_server_and_client' Los$setting!del$servicio$en$la$primera$Raspberry$Pi"serán:! DATABASE = 'ws.db' OBJETO_PYRO = 'servidor1.configura' DIRECCION_PYRO = '192.168.1.115' DIRECCION_PYRO_LOCAL = '192.168.1.115' KEY = 'the_same_string_for_server_and_client' # Configuracion WS WEBPATH = '/ws' PROTOCOL = 'restjson' DIRECCION_WS = '192.168.0.1' PUERTO_WS = 80 # Configuracion Caster DIRECCION_BLUETOOTH = "192.168.1.115:80" MENSAJE = "Servidor de publicidad de restaurante" PUERTO_DIFUSION = 44444 TIEMPO_ANUNCIOS = 30 El#servicio#en#esta#se#arrancará#con#el#script#start.sh.! Los$setting!del$último&servidor(ad(hoc!serán&los&siguientes.!Nótese!como$hemos$cambiado$el$nombre$ del$OBJETO_PYRO$para$que$no$coincida$con$el$del$primer$servicio.! DATABASE = 'ws.db' OBJETO_PYRO = 'servidor2.configura' DIRECCION_PYRO = '192.168.1.115' DIRECCION_PYRO_LOCAL = '192.168.1.116' KEY = 'the_same_string_for_server_and_client' # Configuracion WS WEBPATH = '/ws' PROTOCOL = 'restjson' DIRECCION_WS = '192.168.1.116' PUERTO_WS = 80 # Configuracion Caster DIRECCION_BLUETOOTH = "192.168.1.116:80" MENSAJE = "Servidor de publicidad de restaurante" PUERTO_DIFUSION = 44444 TIEMPO_ANUNCIOS = 30 Vemos&como&DIRECCION_PYRO&tiene&que&ser&la&dirección&en&la&que&se&encuentre&el&servidor&de& nombrado.) En# este# caso," para!arrancar% su% servicio% nos% pondremos% en% la% carpeta% donde% hemos% descargado)el)proyecto)y)ejecutaremos.! sudo python service.py Es#importante#arrancar#primero#el#script#start.sh!de#la#número%uno%ya%que%este%enciende%el%servidor% de#nombrado,#si#no#hay#servidor#de#nombrado,#el#segundo#servicio#mostrará#un#error#de#ejecución# al#iniciarse.!! Una$vez$que$tenemos$ los$servicios" ejecutándose" correctamente," podremos" ejecutar" la" interfaz" de" administración+ cuando+ queramos.+ Hay+ que+ decir+ que+ es+ muy+ importante+ que+ tengamos+ bien+ configurada+ la+dirección+del+ servidor+de+ nombrado+y+ el+primer+ nombre+de+ objeto+que+ queremos+ administrar,*ya!que,!si#no#lo#están#el#programa#no#funcionará!correctamente.!
! ! ! Ejemplo(de(uso.(Demostrando(y(validando(la(aplicación! ! ! 64! Tabla%6–3."Resumen"de"datos"7"user/seg!carga%continua! Muestras" Media" Min" Max" Desv.& Estándar" %"Error" Rendimiento" Kb/seg" 702! 1136! 171! 1494! 115,30! 0,85%! 6,1/seg! 7,50! ! Tabla%6–4."Resumen"de"datos!10#user/seg!carga%continua! Muestras" Media" Min" Max" Desv.& Estándar" %"Error" Rendimiento" Kb/seg" 717! 1762! 111! 56706! 3786,88! 1,53%! 5,6/seg! 6,96! ! Aquí% podemos% determinar% que% el% máximo% rendimiento% que% alcanza% nuestro% sistema% es% de% 6% peticiones)por)segundo.)Vemos"como"el"sistema"guarda"el"tipo"hasta"los"7"usuarios"por"segundo," esto%es%lógico,%ya%que%su%máximo%rendimiento%está!en#torno#a#las#6#peticiones#por#segundo.#Hasta# aquí%las%tasas%de%error%están%por%debajo%del%1%,%pero%a%partir%de%los%10%usuarios%por%segundo" los" errores%llegan%al% 1,5%%y%aunque%el% tiempo% medio% de%respuesta% sube%hasta%casi%los% 2%segundos%el% sistema'sigue'aguantando'el'tipo'aunque'con'dificultades.'! Por$último,"veremos"los"datos"extraídos!de#situaciones#de#alta#carga#puntual#sobre#el#servicio.#En# primer&lugar&veremos&los&datos&de&un& pico&de&30!peticiones)en)un) segundo)y)en) segundo)lugar) veremos'40!peticiones)en)un)segundo.! Tabla%6–5."Resumen"de"datos"30"user/seg!carga%puntual! Muestras" Media" Min" Max" Desv.& Estándar" %"Error" Rendimiento" Kb/seg" 30! 2089! 189! 4557! 1257,49! 0%! 6,0/seg! 7,33! ! Tabla%6–6."Resumen"de"datos"40"user/seg!carga%puntual! Muestras" Media" Min" Max" Desv.& Estándar" %"Error" Rendimiento" Kb/seg" 40! 2970! 191! 5720! 1796,90! 15,0%! 6,3/seg! 8,74! ! Los$tiempos$de$respuesta$medios$suben$incluso$hasta$los$3$segundos.$A$la$vista$de$estos$datos$y$de$ los$anteriores$yo$diría$que$30# usuarios!seria&el&pico&máximo&sostenible&que&podría&soportar&en&un& funcionamiento+normal+del+sistema,+ya+que+vemos+que+a"partir!de#los#40#usuarios#la#tasa#de#error# sube%hasta%el%15%,%es%más!si#prolongamos#unos#10#segundos#el#pico#de#40#usuarios#es#muy#probable# que$nuestro$servidor$se$colapse$y$se$caiga.! Para$concluir$este$apartado,$diremos$que$los$resultados$de$las$pruebas!son$bastante$aceptables$para$ el#entorno#en#el#cual#se#va#a#usar#este#servidor.!Ya"que"como"hemos"dicho"antes,!en#entornos#WiFi# nunca% se% va% a% alcanzar% ese% nivel% de% concurrencia.% Hay% que% recordar% que% probablemente% si% hubiésemos* hecho* estas* pruebas* con* una* Raspberry( Pi( 2( model( B,( el( rendimiento( obviamente( hubiera$sido$mejor$y$si$hubiéramos$probado$el$rendimiento$en$un$ordenador$normal,$las$pruebas$ hubieran)sido)superadas)de)manera)muy)satisfactoria)pero)no)serían!unas%pruebas%fiables%ya%que%lo% que$se$buscaba$con$estos$tests$de$esfuerzo$es$probar$$nuestro$sistema$en$un$ambiente$semirreal%con% carga%continua%y%con%los%típicos%picos%de%peticiones%ya%conocidos%en%el%tráfico%de%internet.! ! ! !
!! 65! Diseño e implementación de un servidor web para mensajes publicitarios para redes ad hoc ! 8 FUTUROS TRABAJOS Y CONCLUSIONES DEL PROYECTO nalizaremos+ el+ contenido+ global" del" proyecto," proponiendo" mejoras" al" proyecto." Sobre" todo" este" apartado'irá!enfocado(a(conseguir(generalizar(este(proyecto(para(cualquier(caso(de(uso,!minimizando& el#trabajo#de#adaptación#al#mismo.!!Los$futuros$trabajos$que$propondremos$serán$fruto$directo#de#las# conclusiones) del) proyecto.!También( propondremos( algunos( escenarios( o( ideas( de( negocios( mencionando)las)virtudes)y)problemas)de)nuestro)proyecto.)! Con$este$último&apartado&vamos&a&concluir&la#memoria#de#este#proyecto,#con#el#cual#esperamos#haber#dejado& clara% la% arquitectura% del% sistema,% como% funciona% y% que% tendríamos% que% hacer% para% adaptarlo% a% las% características!de#nuestro#escenario#en#particular.! ! 8.1 Futuros trabajos Como$hemos$visto$hay$dos$tipos$de$objetos$muy$similares$en$nuestro$sistema.$Los$objetos%del%ORM% y"los"objetos!del$servicio.$Pues$bien,$un$gran$aporte$al$proyecto$sería"un"script"que"lea"el"archivo"de" Models.py!y"cree"automáticamente"el"archivo"webservice.py!que$es$donde$se$encuentran$los$objetos$ que$usa$el$servicio$y$la$definición$del$servicio.'Este%script%crearía%automáticamente%un%clon%de%los% objetos'del'ORM'para'nuestro'servicio'y'un'método'para'cada'tipo'de'objeto'que'devolvería'todos' los$objetos$de$la$base$de$datos.$Es$posible$que$luego$no$hagan$falta$todos$esos$métodos.$Se$dejará$a$ criterio'del'programador'elegir'cuales'son'necesarios'y'cuáles!no.! En#segundo#lugar,#sería" interesante# crear# un# script#que# defina#una!interfaz!que$ pueda$editar$los$ objetos'de#nuestro#ORM,#de#una#forma#similar#a#la#que#lo#hace#Django.#Seguramente#esta#interfaz$ resultante) no) sería" funcional" en" cualquier" caso." Sería" tarea" del" programador" adaptar" de" forma" óptima&la&interfaz&generada&de&forma&automática.&El&script&tendría&que&leer&el&archivo&de&objetos&del& ORM$y$generar$el$archivo$InterfazServidor.py!que$está!en!la#carpeta#interfaz.! En#tercer#lugar,#proponemos#cambiar#el#actual#WiFi#ad#hoc#y#el#Bluetooth#por#la#nueva#tecnología# WiFi$Direct.$Ya$que$lo$que$hacemos$con$nuestro$sistema$actual$es$‘simular’$un$WiFi$Direct$sobre$un$ WiFi$ad$hoc.$Además$todos$los$expertos!apuntan&a&que&WiFi&Direct&sustituirá&a&Bluetooth&en&este& tipo%de%aplicaciones.%! Adaptar'la'interfaz'inalámbrica'a'este'tipo'de'tecnología'también'ayudará'a'una'implementación' del$cifrado$de$la$red$WiFi,$más!eficaz'y'más!fácil&de&configurar.! Algo% muy% importante% en% cualquier% servidor% es% disponer% de% un% log!de# todo# lo# que# ocurre# en# el# servidor,)para)poder)revisarlo)ante)cualquier)incidente,)esto)es)algo)que)se)nos)ha)quedado)en)el) tintero,(pero(es(algo(muy(fácil(y(rápido!de#implementar,#gracias#a#módulo#de#“Logging”"ya"existente" en#el#núcleo!de#Python.! Para$ hacer$ todo$ esto,$ todo$ el$ código$ esta$ subido$ a$ Github,!donde% se% puede% trabajar% en% grupo% cómodamente* sin* pisar* el* trabajo* de* los* demás.* Así!si# existe# algún# bug!no# detectado# durante# el# desarrollo(de(este(proyecto#podrá#ser#subsanado#por#cualquier#persona#en#el#futuro.! Por$otro$lado,$también$sería"muy"interesante,"desarrollar"un"modelo"de"negocio"entorno"a"toda"esta" tecnología*y*aunque*este*no*era*el*objetivo*de*este*proyecto*en*el*siguiente*apartado*daremos*unas! nociones'o'ejemplos'básicos'de'posibles'modelos'de'negocio.! Nos$ha$faltado!testear&que&todas&las&peticiones&han&sido&respondidas&de&forma&correcta,!es#decir,#con# los$datos$que$le!correspondía+a+cada+una,+aunque+esto+se+da+por+supuesto+ya+que+el+servidor+ha"sido" testeado'bastantes'veces'a'mano.'Al'igual'que'tampoco'hemos'hecho'un'desarrollo'TDD!que$por$ supuesto'hubiera'sido'lo'más!adecuado,(pero(este(tipo(de(cosas(se(nos(ocurrieron(con(el(proyecto(ya( bastante'avanzado.'Desarrollo(guiado(por(pruebas(de(software,&o&Test"driven'development'(TDD)%es% una$práctica$de$ingeniería!de#software!cuyo%propósito%es%lograr%un%código%limpio%que%funcione.%La% idea%es%que%los%requisitos%sean%traducidos%a%pruebas,%de%este%modo,%cuando%las%pruebas%pasen%se% garantizará)que)el)software'cumple'con'los'requisitos'que'se'han'establecido.! A!
! ! ! Futuros'trabajos'y'conclusiones)del)proyecto! ! ! 66! 8.2 Posibles escenarios Aquí% propondremos% posibles% aplicaciones% de% nuestro% servicio.% Considerando% sus% virtudes% comerciales*y*viendo*que*partes*del*sistema*no*funcionarían!bien%o%tendríamos%que%optimizar.!En# todo$este$apartado$se$supondrá$que$las$actualizaciones$del$apartado$anterior,$han$sido$ejecutadas$ con$éxito$y$están$operativas.!En#este#apartado#se#pondrá#de#manifiesto#las#virtudes#del#sistema#y# veremos'que'es'bastante'versátil.! Existen(muchas(posibilidades&de&modelo&de&negocio&y&la&verdad&es&que&es&bastante&difícil&decidir& cuál" es" la" más" acertada," pero" aun" así!vamos& mencionar& las& que& nos& parecen& más" válidas.!Por$ ejemplo,(existe(el(modelo(de(negocio(por(subscripción,(es(decir,(cobrar(por(prestar(un(servicio,¶& esto%tendríamos%que%tener%varios%servidores%desplegados%por%la%ciudad%y%los%negocios%que%quieran% usar%nuestro%sistema%para%repartir%publicidad%se%subscribirían,%pero%esta%no%sería"una"buena"opción" ya#que#hoy#en#día!la#gran#mayoría#de#los#usuarios#de#móviles#tienen#tarifas#de#datos#y#cualquier# negocio'podría'dar'el'mismo'servicio'que'damos'nosotros'por'internet.'Por'lo'que'hay'que'buscar' lugares(donde(los(usuarios(tengan(problemas(para(acceder(a(internet.! Por$otro$lado$podríamos,$liberar$el$código"(ya"está!libre)'y'convertirlo'en'un'proyecto'open'source.' De#manera#que#los#ingresos#vendrían#de#vender#las#Raspberry#Pi"configuradas"y"a"dar"soporte"de" pago%a%los%usuarios%que%lo%soliciten,%al%igual%que%se%hace%en%muchísimos%proyectos%de%software.%Con% el"tiempo"podríamos"ir"añadiendo"especificaciones"de"pago"manteniendo"un"producto"básico!que$ los$ clientes$ puedan$ usar$ de$ forma$ gratuita.$ Para$ hacer$ esto$ habría$ que$ hacer$ un$ esfuerzo$ muy$ grande'en'hacer'el'producto'fácil'de'usar'y'muy'fácilmente'instalable'para#cualquier#persona,#para# que$se$popularice$de$forma$rápida.$De$esta$manera,$serían#los#propios#usuarios#los#que#encontrarían# la#forma#adecuada#de#uso#de#nuestro#sistema#en#su#caso#particular#y#podrían#adaptarlo#fácilmente#a# nuestro(servicio(técnico(especializado.! Ahora&propondremos&una&serie&de&escenarios.! " Cafetería/Restaurante" Negocios(de(hostelería(en(general.(Este(ejemplo(es(obvio(ya(que(es(el(mismo(que(proponemos(en( esta%memoria.%Es%ideal%para%que%los%negocios%de%este%tipo%den%a%conocer%sus%productos"y"ofertas"a"los" clientes(que(se(encuentran(cerca(o(dentro(del(local.(! Incluso(se(podría(modificar(un(poco(el(ejemplo(que(hemos(propuesto,(para(que(el(servidor(diera( códigos( promocionales( limitados,( a( los( primeros( 100( usuarios( por( ejemplo,( esto( premiaría( a" los" usuarios' que' acuden' al' restaurante' y' ayudaría' a' su' fidelización.' Gracias' a' que,' el' servicio' es' fácilmente*editable,*el*dueño*del*local*podría*poner*en*promoción*los*artículos*más"perecederos!en# ese#momento#y#así!asegurarse'que'no'va'a'malgastar'sus'productos(por(que(cumplan(la(fecha.!Todo$ esto%hecho%de%una%manera%innovadora,!que$lo$diferenciará$de$la$competencia$dando$una$sensación$ de#negocio#moderno.! ! " " " " " " " " " " " ! Figura''8.1'–!Menú!de#ejemplo!
!! 67! Diseño e implementación de un servidor web para mensajes publicitarios para redes ad hoc ! Cines/Estadios/Teatros" Cualquier)negocio)que)venda)entradas%en%general%podría%usar%nuestro%sistema%para%poner%a%la%venta% a"un"precio"más" atractivo" las" últimas!entradas(para(los(usuarios!que$estén$cerca$del$local.$Así$se$ potenciaría+que+la+gente+visitara+más"las"tiendas"a"la"espera"de"conseguir"alguna"entrada"de#última& hora%a%una%oferta%especial.! Para#este#tipo#de#negocios#será#más"atractivo"llenar"la"sala"aunque"sea"haciendo"menos"caja,"pero" dando% una% buena% imagen% de% empresa% ante% los% consumidores., Incluso, en, días, entre, semana,!se# podría' poner' en' promoción' las' entradas( para# esas# salas# para# las# que!han$ comprado$ un$ par$ de$ personas(las(entradas!solamente.! ! ! ! ! ! ! ! ! ! ! ! ! ! Eventos/Conciertos" Los$festivales$y$conciertos$tienen$como"características"diferenciadoras,!que$por$lo$general,!!hay$una$ gran%afluencia% de%público&que&pueden&saturar&las&telecomunicaciones&normales.&Por&este&motivo,! sería& interesante& usar& nuestros& sistemas& para& ofrecer& información& general& sobre& el& mismo& evento& además'de'publicidad,!claro.'De'esta'manera,!los$usuarios$no$tendrían$que$acceder$a$internet$para$ saber&cuál!es#el#próximo#grupo#o#donde#están#los!baños.!A"este"tipo"de"eventos"suelen"acudir"gente" joven,!que$agradecerían$bastante$darle$un$toque$moderno$a$este$tipo$de$cosas.! ! ! ! ! ! ! ! ! ! ! ! ! Figura''8.2'–!Panorámica*butacas! ! Figura''8.3'–!Panorámica*evento!
! ! ! Futuros'trabajos'y'conclusiones'del'proyecto! ! ! 68! Hoteles/Sitios&Turísticos/Museos" Los$sitios$turísticos!tales&como&los"hoteles"y"museos"tienen"en"común"que"generalmente"están"llenos" de#turistas' extranjeros,+que+por+estar+fuera+de++su+país,!no#tienen#acceso#a#internet#a#través#de#sus# teléfonos#móviles#por#carecer#de#tarifa#de#datos#en#el#país#de#destino.!Por$lo$tanto,$es$una$opción$ muy$buena$ofrecerles$información$sobre$el$sitio$donde$están$y$sobre$curiosidades$cercanas$a$través$ de#nuestro#sistema.#! Si#pensamos#un#poco#esto,!en#los#Museos#ya#se#hace#con#los#audio"guías,"pero"con"nuestro"sistema" sería" mucho" más" cómodo!ya# que# podríamos# usar# nuestros# propios# teléfonos# móviles.# De# esta# forma,!se#crearía#una#experiencia#más"placentera"para"el"turista"y"más"barata"en"general"para"ambos.! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! 8.3 Conclusiones El#objetivo#de#este#trabajo#era#diseñar#e#implementar,!un#servidor#que#fuera#capaz#de#diseminar# mensajes'publicitarios'a'través!de#redes#ad#hoc.!En!nuestro(caso,!este$servidor$debía"simular"el"caso" de#mensajes$publicitarios$para$un$restaurante$o$bar,"pero"a"la"vez"debía"intentar"dar"un"esquema" general'a'seguir'para'poder'aprovechar!este$trabajo$para$otro$tipo$de$datos.!Nuestro(servicio(debía" de#funcionar#sobre#una#Raspberry#Pi#y#ser#administrable#mediante#una"interfaz"gráfica.! Hemos&conseguido&construir&un&sistema&eficiente,&haciendo!que$varias$librerías!de#Python#trabajen# juntas' con' el' fin' de' crear' una' API' REST,' cuyos' datos' son' administrables' mediante' una' interfaz' gráfica(distribuida(que(es(capaz(de(ejecutarse#en#múltiples!sistemas'operativos.!Nos$hemos$servido$ de#un# proyecto#externo#llamado#Ecodroidlink#para# simular#un# interfaz# IP#sobre#nuestro#interfaz# Bluetooth,) para) hacer) accesible!nuestro( servicio( sin( tener( que( escribir( código!específico!para$ Bluetooth.! Los!objetivos)se)han)cumplido,)incluso$hemos$diseñado$un$sistema$de$interfaz$distribuida,$capaz$de$ administrar)muchos)servidores)a)la)vez,)lo)cual)no)estaba)contemplado)en)un)principio.)La)única) pega% es% que% el% proyecto,!debía!de# desarrollar!los$ clientes!en!móviles!Android,!no# se# ha# podido# desarrollar( todavía.( Por( lo( tanto,!no# hemos# podido# testear# el# servidor# con# clientes# reales,# únicamente!con$ los$ navegadores$ de$ nuestros$ dispositivos.!Este% proyecto% estaba% pensado% para% ! Figura''8.4#–!Audio!guía!
!! 69! Diseño e implementación de un servidor web para mensajes publicitarios para redes ad hoc ! desarrollar(cliente(y(servidor(en(paralelo$y$la$falta$de$un$compañero$diseñando$los$clientes$se$ha$ notado.! Este% proyecto% proporciona% un% esquema% de% desarrollo% a% seguir% para% diseñar% servicios% webs! administrables,!mediante( una( interfaz( remota( distribuida.!Ahorrando( trabajo," dejando" al" programador( que$ sólo# defina# los# objetos# que# quiere# que# use# su# servicio.!Este% servicio% web% es% compatible+con+cualquier+sistema+que#tenga#instalado#Python#2.7.X,#por#lo#tanto#es#muy#portable,# aunque&se&ha&diseñado¶&que&funcionara&en&una&Raspberry&Pi.! Esperamos) haber!dado$ una$ solución$ claramente$ modularizada$ al$ problema,!de# manera# que# se# puedan'identificar!esos$ módulos!y"a"la"vez"poder"modificarlos,"eliminarlos"o"intercambiarlos"por" otros.&! La#idea#de#este#proyecto'al'completo,'se'presentó!al#concurso#de#emprendimiento"de"la"US,"en"mi" opinión,' se' presentó!de# una# manera# muy# general# sin# hacer# hincapié!en# los# puntos# fuertes# del# proyecto.)En)primer)lugar,!este$tipo$de$servicios$se$pueden$desplegar$en$cualquier$sitio,$aunque$no$ tengamos) internet,) por) lo) tanto) se) podría) haber" vendido" la" idea" para" festivales" con" afluencia" de" extranjeros*los*cueles$no$tienen$tarifa$de$datos$en$el$móvil,$para$consumir$datos$propios$del$mismo$ festival)o)actividad)con)la)que)esté!relacionado.+Se+podría+haber+vendido+como,+un+diseminador+de+ ofertas!de#última&hora&con&el&cual&premiar&a&los&clientes&que&vayan&a&visitar&la&tienda.&Lo&que&quiero& decir&con&esto,&es&que&&se&debería&haber&explotado&más"las"características!oportunistas*de*este*tipo*de* comunicaciones.+! Por$último,$hablaremos$de$las$limitaciones$del$sistema.!El#sistema#no#tiene#una#gran#capacidad#para# resistir& & un& gran& volumen& de& clientes,& pero& como& estos& clientes!estarán!normalmente) en) redes! inalámbricas," la" propia" capa" física% de% estas% redes% limita% más" a" los" clientes" que" la" capacidad" de" respuesta%de%nuestro%servidor.%Por%lo%tanto"esto"es"una"limitación"pero"sólo#en#el#caso#de#usar#este# proyecto(en(el(caso(de(redes(cableadas,(suponiendo(siempre(que(el(servidor(se(ejecute(sobre(una( Raspberry) Pi,) ya) que) en) un) servidor) normal) la) capacidad) del) sistema$ crece$ notablemente$ hasta$ niveles'aceptables.! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !
! ! ! Bibliografía! ! ! 70! 9 BIBLIOGRAFÍA ! [1]$Juan"Carlos'Cano,'Carlos'T.'Calafate,'Manuel'P.'Malumbres,'Pietro'Manzoni."Redes%Inalámbricas% Ad#Hoc#como#Tecnología#de#soporte¶&la&Computación&Ubicua."Universidad#Politécnica#de#Valencia.# Disponible*en:* http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.108.2021&rep=rep1&type=pdf!! [2]$ Armando( Mercado,( Rafaelgil# Berríos# Figueroa,# Paul# Chan# Ye.# Redes% inalámbricas% ad% hoc." "!." Disponible*en:*http://facultad.bayamon.inter.edu/cgonzalezr/ELEN4618/Adhoc.pdf!! [3]$ Carles' Gómez' i' Montenegro,!Josep& Paradells& Aspas.& REDES% AD"HOC:% EL% PRÓXIMO% RETO.% BURAN,!(21)%JUNIO%2004.%(30"37)! [4]$Pietro'Manzoni,'Juan"Carlos'Cano.'Redes%Inalámbricas%Ad%Hoc:%una%nueva%tecnología%para%dar%soporte% a" las" aplicaciones" ubicuas." Revista" del" Instituto" de" Infomática" (N4).$ (5"9).$ Disponible$ en:$ https://drive.google.com/file/d/0BymsRrb8MMH7SGZUY21xcXZ4N1k/view!! [5]$ JMerter& [en& línea].& JMeter& sitio& oficial.& [Consulta:!Febrero& 2015].& Disponible" en:! http://jmeter.apache.org/!" [6]$ Bluez& [en& línea].& HowTo% set% up% common% PAN% scenarios% with% BlueZ'ʹs% integrated% PAN% support." [Consulta:!Octubre(2014].(Disponible(en:!http://bluez.sourceforge.net/contrib/HOWTO"PAN!! [7]$Novel&Devices&[en&línea].&SETTING'UP'A'RASPBERRY'PI'AS'A'DHCP'SERVER.![Consulta:!Junio& 2014].'Disponible'en:!http://www.noveldevices.co.uk/rp"dhcp"server!! [8]$ Wikipedia( [en( línea].( Red$ ad$ hoc$ inalámbrica." [Consulta:!Junio& 2014].& Disponible& en:! http://es.wikipedia.org/wiki/Red_ad_hoc_inal%C3%A1mbrica!! [9]$ Alberto$ Griggio![en$ línea].$ WxGlade$ tutorial.$ [Consulta:!Junio& 2014].& Disponible& en:! http://wxglade.sourceforge.net/tutorial.php!! [10]% Robin& Dunn![en$ línea].$ WxPython$ documentación.$ [Consulta:!Agosto& 2014].' Disponible' en:! http://www.wxpython.org/docs/api/wx"module.html!! [11]$ Matías& La& Orden." “Wifi$ Direct:$ ¿Que$ es,$ como$ funciona$ y$ para$ que$ sirve?”.!BlogOpeda* [blog].* [Consulta:!Febrero&2015]."Disponible"en:!http://www.blogopeda.com/wifi"direct"que"es/!! [12]% Charles( Leifer![en$ línea].$ Peewee$ ORM$ documentación.$ [Consulta: Septiembre 2014]. Disponible en: https://peewee.readthedocs.org/en/latest/ [13]%Christophe*de*Vienne![en$línea].$WSME$documentación.$[Consulta:!Junio&2014].&Disponible&en:! http://pythonhosted.org//WSME/!! [14]% Irme!de# Jong# [en# línea].# Serpent# documentación.# [Consulta:!Agosto& 2014].& Disponible& en: https://pypi.python.org/pypi/serpent [15]% Irmen& de& Jong![en$ línea].$ Pyro4$ documentación.$ [Consulta:!Agosto& 2014].& Disponible' en:! https://pythonhosted.org/Pyro4/!!! [16]% Antonio& Acevedo& Báñez& [en& línea].& servidor"adhoc!(Repositorio* de* código* del* proyecto)." [Consulta:!Abril&2015].&Disponible&en:!https://github.com/xXcoronaXx/servidor"adhoc! [17]% Kasidit' Yusuf' [en' línea].' Ecodroidlink.' [Consulta:! !Enero& 2015].& Disponible& en:! https://github.com/ykasidit/ecodroidlink!!
! ! 71! ! 10 ANEXO .DESCRIPCIÓN DEL CÓDIGO Aquí%se%encuentra%el%código%fuente%con%el%que%fue%presentado%este%proyecto,%pero%recomendamos% que$si$quieren$ver$su$código$completo$y$mas$reciente$visiten$su$repositorio$en$Github,$encontrarán$ la#URL#en#la#bibliografía.! Service.py5 #!;*;!coding:!UTF;8!;*;! from!webservice!import!*! from!admin!import!*! from!setting!import!*! import!json! import!socket! import!time! ! ! !! def!broadcast():! !threading.Timer(!TIEMPO_ANUNCIOS!,broadcast).start()! !MESSAGE!=!json.dumps({'Nombre':!OBJETO_PYRO,!'IP':!DIRECCION_WS+':'+str(PUERTO_WS),! 'IP_2':DIRECCION_BLUETOOTH!,'Mensaje':!MENSAJE})! !UDP_IP!=!'<broadcast>'!! !UDP_PORT!=!PUERTO_DIFUSION! !sock1!=!socket.socket(socket.AF_INET,!socket.SOCK_DGRAM)! !sock1.bind(('',0))! !sock1.setsockopt(socket.SOL_SOCKET,!socket.SO_BROADCAST,!1)! !sock1.setsockopt(socket.SOL_SOCKET,!socket.SO_REUSEADDR,!1)! !sock1.sendto(MESSAGE,!(UDP_IP,!UDP_PORT))! !sock1.close()! !try:! ! ! sock2!=!socket.socket(socket.AF_INET,!socket.SOCK_DGRAM)! ! ! sock2.bind(('192.168.0.1',0))! ! ! sock2.setsockopt(socket.SOL_SOCKET,!socket.SO_BROADCAST,!1)! ! ! sock2.setsockopt(socket.SOL_SOCKET,!socket.SO_REUSEADDR,!1)! ! ! sock2.sendto(MESSAGE,!('192.168.0.255',!UDP_PORT))! ! ! sock2.close()! !except!Exception,!e:! ! ! print!e! !! def!main():! !#!Create!a!database!instance!that!will!manage!the!connection!and! !#!execute!queries! !try:! ! ! db.create_tables([Menu_db,!Oferta_db,!Item_db])! ! ! print!'Base!de!datos!creada...'! !except!Exception,!e:! ! ! print!'La!base!de!datos!ya!existe...'! !database!=!SqliteDatabase(DATABASE,!threadlocals=True)! !#!creando!algunos!objetos!y!mostrando!los!existentes...! !nuevoItem!=!Item_db(disponible=True,!precio='10.5',!nombre='sqlite3',descripcion='item!
! ! ! Anexo&.Descripción&del&código! ! ! 72! desde!la!db',!imagen='nmnmnmnm',!primeros=0,!segundos=0,!postres=0,!ofertas=0)! !#nuevoItem.save()! !nuevoMenu!=!Menu_db(disponible=True,!precio='10.5',!nombre='sqlite3',descripcion='item! desde!la!db',fecha_ini=date(1960,!1,!15),fecha_fin=date(1999,!1,!15),codigo='0123456789'! ,imagen='nmnmnmnm')! !#nuevoMenu.save()! !print!';;;Menus;;;'! !for!x!in!Menu_db.select():! ! ! print!str(x.id)+'!'+x.nombre+'!;!'+str(x.fecha_fin)! !print!';;;'! !print!';;;Ofertas;;;'! !for!x!in!Oferta_db.select():! ! ! print!str(x.id)+'!'+x.nombre+'!;!'+str(x.fecha_fin)! !print!';;;'! !print!';;;Items;1;;'! !for!x!in!Item_db.select().where(! Item_db.segundos==0,Item_db.primeros==0,Item_db.postres==0,Item_db.ofertas==0!):! ! ! print!str(x.id)+'!'+x.nombre! !print!';;;'! !#!fin!base!de!datos! !! !#!servicio!Pyro4! !Pyro4.config.HMAC_KEY!=!KEY! !configura!=!ControladorPyro()! !#print!Pyro4.config.dump()! !! !daemon!=!Pyro4.Daemon(host=DIRECCION_PYRO_LOCAL)! !#daemon!=!Pyro4.Daemon(host="192.168.0.18",!port=5150);! !uri!=!daemon.register(configura)! !ns!=!Pyro4.locateNS(host=DIRECCION_PYRO)! !print!uri! !ns.register(OBJETO_PYRO,!uri)! !! !#arrancamos!el!hilo!del!servicio!web! !configura.run()! ! ! !#!arrancamos!hilo!para!anuncio!del!servidor! !broadcast()! ! ! !print!'Pyro4!ready!!'! !daemon.requestLoop()!! !#fin!del!servicio!Pyro4! !! !! if!__name__=="__main__":! !main()! 5 5 5 5 5 5
!! 73! Diseño e implementación de un servidor web para mensajes publicitarios para redes ad hoc ! webservice.py #!;*;!coding:!UTF;8!;*;! from!wsme!import!*! from!wsme.types!import!*! !! import!time! from!datetime!import!date! !! from!Models!import!*! from!peewee!import!*! !! from!setting!import!*! !! class!Item(object):! !id!=!int! !disponible!=!bool! !precio!=!float! !nombre!=!unicode! !descripcion!=!unicode! !imagen!=!bytes!#imagen! !modificado!=!date!! !! !def!showImage(self):! ! ! fh!=!open("imageToSave.jpg",!"wb")! ! ! fh.write(self.imagen.decode('base64'))! ! ! fh.close()! !! !def!setImage(self,!f):! ! ! with!open(f,!"rb")!as!imageFile:! ! ! ! img!=!base64.b64encode(imageFile.read())! ! ! return!img! !! !def!__init__(self,!id!=!None!,!disponible!=!False,precio!=!None,!nombre!=!None,! descripcion!=!None,!imagen!=!None,!modificado!=!None!):! ! ! if!id:! ! ! ! self.id!=!id! ! ! if!disponible:! ! ! ! self.disponible!=!disponible! ! ! if!precio:! ! ! ! self.precio!=!precio! ! ! if!nombre:! ! ! ! self.nombre!=!nombre! ! ! if!descripcion:! ! ! ! self.descripcion!=!descripcion! ! ! if!imagen:! ! ! ! self.imagen!=!imagen! ! ! if!modificado:! ! ! ! self.modificado!=!modificado! !! class!Menu(Base):! !id!=!int! !precio!=!float! !disponible!=!bool! !nombre!=!unicode!
! ! ! Anexo&.Descripción&del#código! ! ! 80! date(int(fecha_ini[0]),int(fecha_ini[1]),int(fecha_ini[2]))! ! ! ! oferta.fecha_fin!=! date(int(fecha_fin[0]),int(fecha_fin[1]),int(fecha_fin[2]))! ! ! ! oferta.imagen!=!imagen! ! ! ! oferta.save()! ! ! ! return!True! ! ! except!Exception,!e:! ! ! ! return!False! !! !#añade!item!a!los!primeros!del!menu!duplicandolo! !def!addItemMenuP(self,!item,!menu):! ! ! try:! ! ! ! print!'añadiendo!primero'! ! ! ! i!=!Item_db.select().where(Item_db.id==item).get()! ! ! ! m!=!Menu_db.select().where(Menu_db.nombre==menu).get()! ! ! ! print!i.nombre+'!'+m.nombre! ! ! ! nuevoItemA!=!Item_db(disponible=i.disponible,!precio=i.precio,! nombre=i.nombre,descripcion=i.descripcion,!imagen=i.imagen,!primeros=m.id,!segundos=0,! postres=0,!ofertas=0)! ! ! ! nuevoItemA.save()! ! ! ! return!True! ! ! except!Exception,!e:! ! ! ! return!False! !! !#añade!item!a!los!segundos!del!menu!duplicandolo! !def!addItemMenuS(self,!item,!menu):! ! ! try:! ! ! ! print!'añadiendo!segundo'! ! ! ! i!=!Item_db.select().where(Item_db.id==item).get()! ! ! ! m!=!Menu_db.select().where(Menu_db.nombre==menu).get()! ! ! ! print!i.nombre+'!'+m.nombre! ! ! ! nuevoItemA!=!Item_db(disponible=i.disponible,!precio=i.precio,! nombre=i.nombre,descripcion=i.descripcion,!imagen=i.imagen,!primeros=0,!segundos=m.id,! postres=0,!ofertas=0)! ! ! ! nuevoItemA.save()! ! ! ! return!True! ! ! except!Exception,!e:! ! ! ! return!False! !! !#añade!item!a!los!postres!del!menu!duplicandolo! !def!addItemMenuD(self,!item,!menu):! ! ! try:! ! ! ! print!'añadiendo!postre'! ! ! ! i!=!Item_db.select().where(Item_db.id==item).get()! ! ! ! m!=!Menu_db.select().where(Menu_db.nombre==menu).get()! ! ! ! print!i.nombre+'!'+m.nombre! ! ! ! nuevoItemA!=!Item_db(disponible=i.disponible,!precio=i.precio,! nombre=i.nombre,descripcion=i.descripcion,!imagen=i.imagen,!primeros=0,!segundos=0,! postres=m.id,!ofertas=0)! ! ! ! nuevoItemA.save()! ! ! ! return!True! ! ! except!Exception,!e:! ! ! ! return!False! !!
!! 81! Diseño e implementación de un servidor web para mensajes publicitarios para redes ad hoc ! !#añade!item!a!una!oferta!duplicandolo! !def!addItemOferta(self,!item,!oferta):! ! ! try:! ! ! ! print!'añadiendo!item!a!oferta'! ! ! ! i!=!Item_db.select().where(Item_db.id==item).get()! ! ! ! m!=!Oferta_db.select().where(Oferta_db.nombre==oferta).get()! ! ! ! print!i.nombre+'!'+m.nombre! ! ! ! nuevoItemA!=!Item_db(disponible=i.disponible,!precio=i.precio,! nombre=i.nombre,descripcion=i.descripcion,!imagen=i.imagen,!primeros=0,!segundos=0,!postres=0,! ofertas=m.id)! ! ! ! nuevoItemA.save()! ! ! ! return!True! ! ! except!Exception,!e:! ! ! ! return!False! !! !#!borra!primero!de!un!menu! !def!delItemMenuP(self,!item,!menu):! ! ! try:! ! ! ! print!'borrando!primero'! ! ! ! m!=!Menu_db.select().where(Menu_db.nombre==menu).get()! ! ! ! i!=!Item_db.select().where(Item_db.nombre==item,! Item_db.primeros==m.id).get()! ! ! ! i.delete_instance()! ! ! ! return!True! ! ! except!Exception,!e:! ! ! ! return!False! !! !#!borra!segundo!de!un!menu! !def!delItemMenuS(self,!item,!menu):! ! ! try:! ! ! ! print!'borrando!segundo'! ! ! ! m!=!Menu_db.select().where(Menu_db.nombre==menu).get()! ! ! ! i!=!Item_db.select().where(Item_db.nombre==item,! Item_db.segundos==m.id).get()! ! ! ! i.delete_instance()! ! ! ! return!True! ! ! except!Exception,!e:! ! ! ! return!False! !! !#!borra!postre!de!un!menu! !def!delItemMenuD(self,!item,!menu):! ! ! try:! ! ! ! print!'borrando!postre'! ! ! ! m!=!Menu_db.select().where(Menu_db.nombre==m).get()! ! ! ! i!=!Item_db.select().where(Item_db.nombre==item,! Item_db.postres==m.id).get()! ! ! ! i.delete_instance()! ! ! ! return!True! ! ! except!Exception,!e:! ! ! ! return!False! !! !#!borra!item!de!una!oferta! !def!delItemOferta(self,!item,!oferta):! ! ! try:!
! ! ! Anexo&.Descripción&del&código! ! ! 82! ! ! ! print!'borrando!item!oferta'! ! ! ! i!=!Item_db.select().where(Item_db.nombre==item,! Item_db.ofertas==oferta).get()! ! ! ! i.delete_instance()! ! ! ! return!True! ! ! except!Exception,!e:! ! ! ! return!False! !! !#!devuelve!el!primer!item! !def!getItem(self):! ! ! b!=!Item_db.select()! ! ! return!serpent.dumps(b[0],indent=False)! !! !#borra!el!item! !def!delItem(self,!idItem):! ! ! try:! ! ! ! print!'Borrando!Item!pyro'! ! ! ! b!=!Item_db.select().where(Item_db.id==idItem).get()! ! ! ! b.delete_instance()! ! ! ! return!True! ! ! except!Exception,!e:! ! ! ! return!False! !! !#devuelve!todos!los!item! !def!getItems(self):! ! ! try:! ! ! ! print!'Item!pyro'! ! ! ! i!=![]! ! ! ! #!busca!todos!lo!items!que!no!estan!asignados!a!ningun!meno!o!oferta! ! ! ! for!x!in!Item_db.select().where(!Item_db.segundos!==!0,!Item_db.primeros! ==!0!,Item_db.postres!==!0!,Item_db.ofertas!==!0!):! ! ! ! !! i.append(x)! ! ! ! return!serpent.dumps(i,indent=False)! ! ! except!Exception,!e:! ! ! ! return!False! !! !#devuelve!un!menu! !def!getMenu(self,!menu):! ! ! try:! ! ! ! print!"Menu!pyro"! ! ! ! x!=!Menu_db.select().where(Menu_db.nombre==menu).get()! ! ! ! p!=![]!#primeros! ! ! ! s!=![]!#segundos! ! ! ! d!=![]!#postres! ! ! ! m!=!{}! ! ! ! m['nombre']!=!x.nombre! ! ! ! m['disponible']!=!x.disponible! ! ! ! m['precio']!=!float(x.precio)! ! ! ! m['descripcion']!=!x.descripcion! ! ! ! m['fecha_ini']!=!x.fecha_ini! ! ! ! m['fecha_fin']!=!x.fecha_fin! ! ! ! m['imagen']!=!x.imagen! ! ! ! for!y!in!Item_db.select().where(Item_db.primeros==x):! ! ! ! ! p.append(y)!
!! 83! Diseño e implementación de un servidor web para mensajes publicitarios para redes ad hoc ! ! ! ! for!y!in!Item_db.select().where(Item_db.segundos==x):! ! ! ! ! s.append(y)! ! ! ! for!y!in!Item_db.select().where(Item_db.postres==x):! ! ! ! ! d.append(y)! ! ! ! m['primeros']!=!p! ! ! ! m['segundos']!=!s! ! ! ! m['postres']!=!d! ! ! ! return!serpent.dumps(m,indent=False)! ! ! except!Exception,!e:! ! ! ! return!False! !! !#borra!el!menu! !def!delMenu(self,!menu):! ! ! try:! ! ! ! print!"Borrando!Menu!pyro"! ! ! ! m!=!Menu_db.select().where(Menu_db.nombre==menu).get()! ! ! ! for!i!in!Item_db.select().where(Item_db.primeros==m.id):! ! ! ! ! i.delete_instance()! ! ! ! for!i!in!Item_db.select().where(Item_db.segundos==m.id):! ! ! ! ! i.delete_instance()! ! ! ! for!i!in!Item_db.select().where(Item_db.postres==m.id):! ! ! ! ! i.delete_instance()! ! ! ! m.delete_instance()! ! ! ! return!True! ! ! except!Exception,!e:! ! ! ! return!False! !! !#devuelve!un!array!de!menus! !def!getMenus(self,!fecha):! ! ! try:! ! ! ! print!"Menus!pyro"! ! ! ! fecha!=!str(fecha).split(';')! ! ! ! fecha!=!date(int(fecha[0]),int(fecha[1]),int(fecha[2]))! ! ! ! menus!=![]! ! ! ! for!x!in!Menu_db.select().where(!Menu_db.fecha_ini!<=!fecha!!).where(! Menu_db.fecha_fin!>=!fecha!):! ! ! ! ! p!=![]!#primeros! ! ! ! ! s!=![]!#segundos! ! ! ! ! d!=![]!#postres! ! ! ! ! m!=!{}! ! ! ! ! m['nombre']!=!x.nombre! ! ! ! ! m['disponible']!=!x.disponible! ! ! ! ! m['precio']!=!float(x.precio)! ! ! ! ! m['descripcion']!=!x.descripcion! ! ! ! ! m['fecha_ini']!=!x.fecha_ini! ! ! ! ! m['fecha_fin']!=!x.fecha_fin! ! ! ! ! m['imagen']!=!x.imagen! ! ! ! ! for!y!in!Item_db.select().where(Item_db.primeros==x):! ! ! ! ! ! p.append(y)! ! ! ! ! for!y!in!Item_db.select().where(Item_db.segundos==x):! ! ! ! ! ! s.append(y)! ! ! ! ! for!y!in!Item_db.select().where(Item_db.postres==x):! ! ! ! ! ! d.append(y)! ! ! ! ! m['primeros']!=!p!
! ! ! Anexo&.Descripción&del&código! ! ! 84! ! ! ! ! m['segundos']!=!s! ! ! ! ! m['postres']!=!d! ! ! ! ! #!agregamos!el!menu! ! ! ! ! menus.append(m)! ! ! ! return!serpent.dumps(menus,indent=False)! ! ! except!Exception,!e:! ! ! ! return!False! ! ! ! !#devuelve!una!oferta! !def!getOferta(self,!oferta):! ! ! try:! ! ! ! print!"Oferta!pyro"! ! ! ! x!=!Oferta_db.select().where(Oferta_db.nombre==oferta).get()! ! ! ! i!=![]!#items! ! ! ! m!=!{}! ! ! ! m['nombre']!=!x.nombre! ! ! ! m['disponible']!=!x.disponible! ! ! ! m['precio']!=!float(x.precio)! ! ! ! m['descripcion']!=!x.descripcion! ! ! ! m['fecha_ini']!=!x.fecha_ini! ! ! ! m['fecha_fin']!=!x.fecha_fin! ! ! ! m['imagen']!=!x.imagen! ! ! ! for!y!in!Item_db.select().where(Item_db.ofertas==x):! ! ! ! ! i.append(y)! ! ! ! m['items']!=!i! ! ! ! return!serpent.dumps(m,indent=False)! ! ! except!Exception,!e:! ! ! ! return!False! !! !#!borra!la!oferta! !def!delOferta(self,!oferta):! ! ! try:! ! ! ! print!"Borrando!Oferta!pyro"! ! ! ! o!=!Oferta_db.select().where(Oferta_db.nombre==oferta).get()! ! ! ! for!i!in!Item_db.select().where(Item_db.ofertas==o.id):! ! ! ! ! i.delete_instance()! ! ! ! o.delete_instance()! ! ! ! return!True! ! ! except!Exception,!e:! ! ! ! return!False! !! !#devuelve!un!array!de!ofertas! !def!getOfertas(self,!fecha):! ! ! try:! ! ! ! print!"Ofertas!pyro"! ! ! ! fecha!=!str(fecha).split(';')! ! ! ! fecha!=!date(int(fecha[0]),int(fecha[1]),int(fecha[2]))! ! ! ! ofertas!=![]! ! ! ! for!x!in!Oferta_db.select().where(!Oferta_db.fecha_ini!<=!fecha!! ).where(!Oferta_db.fecha_fin!>=!fecha!):! ! ! ! ! i!=![]!#items! ! ! ! ! m!=!{}! ! ! ! ! m['nombre']!=!x.nombre! ! ! ! ! m['disponible']!=!x.disponible!
!! 85! Diseño e implementación de un servidor web para mensajes publicitarios para redes ad hoc ! ! ! ! ! m['precio']!=!float(x.precio)! ! ! ! ! m['descripcion']!=!x.descripcion! ! ! ! ! m['fecha_ini']!=!x.fecha_ini! ! ! ! ! m['fecha_fin']!=!x.fecha_fin! ! ! ! ! m['imagen']!=!x.imagen! ! ! ! ! for!y!in!Item_db.select().where(Item_db.ofertas==x):! ! ! ! ! ! i.append(y)! ! ! ! ! m['items']!=!i! ! ! ! ! #!agregamos!la!oferta! ! ! ! ! ofertas.append(m)! ! ! ! return!serpent.dumps(ofertas,indent=False)! ! ! except!Exception,!e:! ! ! ! return!False! !! !#simple!ping! !def!online(self):! ! ! return!True! !! !def!run(self):! ! ! def!servicio_Rest():! ! ! ! #!creamos!el!servicio!!! ! ! ! servicio!=!ControladorWS(webpath!=!WEBPATH)! !! ! ! ! servicio.addprotocol(PROTOCOL)! !! ! ! ! bottle.mount(WEBPATH,!servicio.wsgiapp())! !! ! ! ! logging.basicConfig(level=logging.DEBUG)! !! ! ! ! database2!=!SqliteDatabase(DATABASE,!threadlocals=True)! !! ! ! ! #!Arrancamos!el!servcio!web!para!la!aplicacion! ! ! ! bottle.run(host=DIRECCION_WS,!port=PUERTO_WS)! ! ! ! #!fin!de!arranque!del!servicio!REST! ! ! #!manejo!de!hilos!en!pyro4! ! ! thread!=!threading.Thread(target=servicio_Rest)! ! ! thread.setDaemon(True)! ! ! thread.start()! Models.py #!;*;!coding:!UTF;8!;*;! from!peewee!import!*! import!datetime! !! #!parametro!soporte!multi!hilo! db!=!SqliteDatabase('ws.db',!check_same_thread=False)! !! class!Menu_db(Model):! !nombre!=!CharField(unique=True,!null=False)! !precio!=!FloatField()! !disponible!=!BooleanField()! !descripcion!=!CharField()! !fecha_ini!=!DateTimeField()!
! ! ! Anexo&.Descripción&del&código! ! ! 86! !fecha_fin!=!DateTimeField()! !imagen!=!CharField()! !modified!=!DateTimeField()! !! !class!Meta:! ! ! database!=!db! ! ! !def!save(self,!*args,!**kwargs):! ! ! self.modified!=!datetime.datetime.now()! ! ! return!super(Menu_db,!self).save(*args,!**kwargs)! !! class!Oferta_db(Model):! !nombre!=!CharField(unique=True,!null=False)! !precio!=!FloatField()! !disponible!=!BooleanField()! !descripcion!=!CharField()! !fecha_ini!=!DateTimeField()! !fecha_fin!=!DateTimeField()! !#codigo!=!!CharField()! !imagen!=!CharField()! !modified!=!DateTimeField()! !! !class!Meta:! ! ! database!=!db! !! !def!save(self,!*args,!**kwargs):! ! ! self.modified!=!datetime.datetime.now()! ! ! return!super(Oferta_db,!self).save(*args,!**kwargs)! !! class!Item_db(Model):! !nombre!=!CharField(null=False)! !precio!=!FloatField()! !disponible!=!BooleanField()! !descripcion!=!CharField()! !imagen!=!CharField()!#imagen!base64! !modified!=!DateTimeField()! !primeros!=!!ForeignKeyField(Menu_db,!related_name='primeros')! !segundos!=!ForeignKeyField(Menu_db,!related_name='segundos')! !postres!=!ForeignKeyField(Menu_db,!related_name='postres')! !ofertas!=!ForeignKeyField(Oferta_db,!related_name='items')! ! ! !class!Meta:! ! ! database!=!db! !! !def!save(self,!*args,!**kwargs):! ! ! self.modified!=!datetime.datetime.now()! ! ! return!super(Item_db,!self).save(*args,!**kwargs)!
!! 87! Diseño e implementación de un servidor web para mensajes publicitarios para redes ad hoc ! Start.sh #! #!Arranca!el!servivio!en!Raspberry!pi! #! #!Written!by!Antonio!Acevedo!Bañez!<[email protected]>! #!git!clone!https://github.com/xXcoronaXx/servidor;adhoc.git! echo!'**!Arrancando!Bluetooth!**'! cd!..! cd!ecodroidlink/! sudo!./edl_main!&! sleep!4! echo!'**!Arrancando!servidor!de!nombrado...!**'! export!PYRO_HMAC_KEY='the_same_string_for_server_and_client'! python!;m!Pyro4.naming!;;host!192.168.1.115!&! sleep!2! cd!..! cd!servidor;adhoc/! echo!'**!Arrancando!servicio!REST!**!'! sudo!python!service.py!&! sleep!4! echo!'**!Configurando!Iptables...!**'! sudo!sysctl!net.ipv4.ip_forward=1! sudo!iptables!;t!nat!;A!PREROUTING!;d!192.168.1.115!;p!tcp!;;dport!80!;j!DNAT!;;to;destination! 192.168.0.1:80! sudo!iptables!;t!nat!;A!POSTROUTING!;j!MASQUERADE! echo!'**!Servicio!up!!**'! " InterfazServidor.py" #!;*;!coding:!UTF;8!;*;! #! #!generated!by!wxGlade!0.6.8!on!Sat!Aug!!9!15:23:42!2014! #! !! import!wx! import!wx.calendar! !! from!datetime!import!timedelta! import!datetime!as!datetime! #!begin!wxGlade:!dependencies! import!gettext! #!end!wxGlade! from!ServicioPyro!import!*! #!begin!wxGlade:!extracode! #!end!wxGlade! !! class!MyFrame(wx.Frame):! !!!!def!__init__(self,!*args,!**kwds):! !!!!!!!!#!begin!wxGlade:!MyFrame.__init__! !!!!!!!!kwds["style"]!=!wx.DEFAULT_FRAME_STYLE! !!!!!!!!wx.Frame.__init__(self,!*args,!**kwds)! !!!!!!!!! !!!!!!!!#!Menu!Bar! !!!!!!!!self.main_menubar!=!wx.MenuBar()!
! ! ! Anexo&.Descripción&del&código! ! ! 88! !!!!!!!!self.Inicio!=!wx.Menu()! !!!!!!!!self.main_menubar.Append(self.Inicio,!_(u"Inicio"))! !!!!!!!!reconectar!=!self.Inicio.Append(wx.ID_ANY,!u'Reconectar',!u'Conectarse!a!otro! servicio')! !!!!!!!!self.Inicio.Append(wx.ID_EXIT,!u'Salir',!u'Salir!de!la!aplicación')! !!!!!!!!self.SetMenuBar(self.main_menubar)! !!!!!!!!self.Bind(wx.EVT_MENU,!self.listar_servidores,!reconectar)! !!!!!!!!#!Menu!Bar!end! !!!!!!!!self.button_1!=!wx.Button(self,!wx.ID_ANY,!_(u"Crear"))! !!!!!!!!self.sizer_2_staticbox!=!wx.StaticBox(self,!wx.ID_ANY,!_(u"Menu"))! !!!!!!!!self.calendar_ctrl_1!=!wx.calendar.CalendarCtrl(self,!wx.ID_ANY,! style=wx.calendar.CAL_MONDAY_FIRST)! !!!!!!!!self.button_3!=!wx.Button(self,!wx.ID_ANY,!_(u"Crear"))! !!!!!!!!self.sizer_3_staticbox!=!wx.StaticBox(self,!wx.ID_ANY,!_(u"Ofertas"))! !!!!!!!!self.calendar_ctrl_2!=!wx.calendar.CalendarCtrl(self,!wx.ID_ANY,! style=wx.calendar.CAL_MONDAY_FIRST)! !!!!!!!!self.list_ctrl_1!=!wx.ListCtrl(self,!wx.ID_ANY,!style=wx.LC_REPORT!|!wx.SUNKEN_BORDER)! !!!!!!!!self.list_ctrl_2!=!wx.ListCtrl(self,!wx.ID_ANY,!style=wx.LC_REPORT!|!wx.SUNKEN_BORDER)! !! !!!!!!!!#!configuramos!las!columnas!de!las!ofertas!y!de!los!menus! !!!!!!!!self.list_ctrl_1.InsertColumn(0,u"Nombre")! !!!!!!!!self.list_ctrl_1.SetColumnWidth(0,180)! !!!!!!!!self.list_ctrl_1.InsertColumn(1,u"Precio")! !!!!!!!!self.list_ctrl_1.InsertColumn(2,u"Disponible")! !! !!!!!!!!self.list_ctrl_2.InsertColumn(0,u"Nombre")! !!!!!!!!self.list_ctrl_2.SetColumnWidth(0,180)! !!!!!!!!self.list_ctrl_2.InsertColumn(1,u"Precio")! !!!!!!!!self.list_ctrl_2.InsertColumn(2,u"Disponible")! !!!!!!!!#!rellenamos!las!listas! !!!!!!!!for!data!in!servicio.Menus:! !!!!!!!!!!!!pos!=!self.list_ctrl_1.InsertStringItem(0,data['nombre'])! !!!!!!!!!!!!self.list_ctrl_1.SetStringItem(pos,1,str(data['precio']))! !!!!!!!!!!!!self.list_ctrl_1.SetStringItem(pos,2,str(data['disponible']))! !!!!!!!!for!data!in!servicio.Ofertas:! !!!!!!!!!!!!pos!=!self.list_ctrl_2.InsertStringItem(0,data['nombre'])! !!!!!!!!!!!!self.list_ctrl_2.SetStringItem(pos,1,str(data['precio']))! !!!!!!!!!!!!self.list_ctrl_2.SetStringItem(pos,2,str(data['disponible']))! !! !!!!!!!!self.__set_properties()! !!!!!!!!self.__do_layout()! !! !!!!!!!!#!bindeamos!los!eventos!de!botones!y!boton!derecho!sobre!las!listas! !!!!!!!!self.list_ctrl_1.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK,!self.showPopupMenu)! !!!!!!!!self.createContextMenu()! !!!!!!!!self.list_ctrl_2.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK,!self.showPopupMenu2)! !!!!!!!!self.createContextMenu2()!!!#!creamos!el!context!menu!que!aparecera!con!el!boton! derecho! !!!!!!!!self.calendar_ctrl_1.Bind(wx.calendar.EVT_CALENDAR_SEL_CHANGED,self.dia_menu)! !!!!!!!!self.calendar_ctrl_2.Bind(wx.calendar.EVT_CALENDAR_SEL_CHANGED,self.dia_oferta)! !! !!!!!!!!self.Bind(wx.EVT_BUTTON,!self.crear_menu,!self.button_1)! !!!!!!!!self.Bind(wx.EVT_BUTTON,!self.crear_oferta,!self.button_3)! !!!!!!!!#!end!wxGlade!
!! 89! Diseño e implementación de un servidor web para mensajes publicitarios para redes ad hoc ! !! !!!!def!__set_properties(self):! !!!!!!!!#!begin!wxGlade:!MyFrame.__set_properties! !!!!!!!!self.SetTitle(_("Servidor!"+servicio.servicioActual))! !!!!!!!!self.SetSize((697,!657))! !!!!!!!!#!end!wxGlade! !! !!!!def!__do_layout(self):! !!!!!!!!#!begin!wxGlade:!MyFrame.__do_layout! !!!!!!!!sizer_1!=!wx.BoxSizer(wx.VERTICAL)! !!!!!!!!grid_sizer_1!=!wx.GridSizer(2,!2,!0,!0)! !!!!!!!!grid_sizer_3!=!wx.GridSizer(2,!1,!0,!0)! !!!!!!!!self.sizer_3_staticbox.Lower()! !!!!!!!!sizer_3!=!wx.StaticBoxSizer(self.sizer_3_staticbox,!wx.HORIZONTAL)! !!!!!!!!grid_sizer_5!=!wx.GridSizer(1,!1,!0,!0)!#!cambiado! !!!!!!!!grid_sizer_2!=!wx.GridSizer(2,!1,!0,!0)! !!!!!!!!self.sizer_2_staticbox.Lower()! !!!!!!!!sizer_2!=!wx.StaticBoxSizer(self.sizer_2_staticbox,!wx.HORIZONTAL)! !!!!!!!!grid_sizer_4!=!wx.GridSizer(1,!1,!0,!0)!#!cambiado! !!!!!!!!grid_sizer_4.Add(self.button_1,!0,!wx.ALIGN_CENTER_HORIZONTAL!|! wx.ALIGN_CENTER_VERTICAL!|!wx.SHAPED,!0)! !!!!!!!!sizer_2.Add(grid_sizer_4,!1,!wx.EXPAND,!0)! !!!!!!!!grid_sizer_2.Add(sizer_2,!1,!wx.EXPAND!|!wx.SHAPED,!0)! !!!!!!!!grid_sizer_2.Add(self.calendar_ctrl_1,!0,!wx.ALL!|!wx.EXPAND!|! wx.ALIGN_CENTER_HORIZONTAL,!5)! !!!!!!!!grid_sizer_1.Add(grid_sizer_2,!1,!wx.EXPAND,!0)! !!!!!!!!grid_sizer_5.Add(self.button_3,!0,!wx.ALIGN_CENTER_HORIZONTAL!|! wx.ALIGN_CENTER_VERTICAL!|!wx.SHAPED,!0)! !!!!!!!!sizer_3.Add(grid_sizer_5,!1,!wx.EXPAND,!0)! !!!!!!!!grid_sizer_3.Add(sizer_3,!1,!wx.EXPAND!|!wx.SHAPED,!0)! !!!!!!!!grid_sizer_3.Add(self.calendar_ctrl_2,!0,!wx.ALL!|!wx.EXPAND!|! wx.ALIGN_CENTER_HORIZONTAL,!5)! !!!!!!!!grid_sizer_1.Add(grid_sizer_3,!1,!wx.EXPAND,!0)! !!!!!!!!grid_sizer_1.Add(self.list_ctrl_1,!1,!wx.EXPAND,!0)! !!!!!!!!grid_sizer_1.Add(self.list_ctrl_2,!1,!wx.EXPAND,!0)! !!!!!!!!sizer_1.Add(grid_sizer_1,!1,!wx.EXPAND,!0)! !!!!!!!!self.SetSizer(sizer_1)! !!!!!!!!self.Layout()! !!!!!!!!#!end!wxGlade! !! !!!!def!dia_menu(self,!event):! !!!!!!!!fecha!=!self.calendar_ctrl_1.GetDate().FormatISODate()! !!!!!!!!fecha!=!datetime.datetime.strptime(fecha,!"%Y;%m;%d")! !!!!!!!!servicio.updateMenus(!str(fecha.date())!)!!!!!!!!!!!!#!actualizamos!los!items!del! servidor! !!!!!!!!self.list_ctrl_1.DeleteAllItems()! !!!!!!!!for!data!in!servicio.Menus:! !!!!!!!!!!!!pos!=!self.list_ctrl_1.InsertStringItem(0,data['nombre'])! !!!!!!!!!!!!self.list_ctrl_1.SetStringItem(pos,1,str(data['precio']))! !!!!!!!!!!!!self.list_ctrl_1.SetStringItem(pos,2,str(data['disponible']))! !!!!!!!!event.Skip()! !! !!!!def!dia_oferta(self,!event):! !!!!!!!!fecha!=!self.calendar_ctrl_2.GetDate().FormatISODate()!
! ! ! Anexo&.Descripción&del&código! ! ! 96! !!!!!!!!!!!!self.text_ctrl_3!=!wx.TextCtrl(self,!wx.ID_ANY,!"",!size=(200,;1))! !! !!!!!!!!#!variables!para!guardar!los!items!asignados!y!la!codificacion!en!base64!de!la!imagen! !!!!!!!!#!en!ellos!se!listan!las!id!de!la!base!de!datos! !!!!!!!!self.primeros!=![]! !!!!!!!!self.segundos!=![]! !!!!!!!!self.postres!=![]! !!!!!!!!self.img!=!0! !!!!!!!!#!variables!temporales!para!los!items!que!se!asignan!y!se!borran!mientras!editamos!un! menu! !!!!!!!!self.primerosModAdd!=![]! !!!!!!!!self.segundosModAdd!=![]! !!!!!!!!self.postresModAdd!=![]! !!!!!!!!self.primerosModDel!=![]! !!!!!!!!self.segundosModDel!=![]! !!!!!!!!self.postresModDel!=![]! !! !!!!!!!!self.editando!=!False! !! !!!!!!!!#!Dando!estilo!a!las!listas!de!items! !!!!!!!!self.list_ctrl_3.InsertColumn(0,u"ID")! !!!!!!!!self.list_ctrl_3.SetColumnWidth(0,0)!#!para!que!no!se!visualize! !!!!!!!!self.list_ctrl_3.InsertColumn(1,u"Nombre")! !!!!!!!!self.list_ctrl_3.InsertColumn(2,u"Precio")! !!!!!!!!self.list_ctrl_3.InsertColumn(3,u"Disponible")! !! !!!!!!!!self.list_ctrl_5ab.InsertColumn(0,u"ID")! !!!!!!!!self.list_ctrl_5ab.SetColumnWidth(0,0)!#!para!que!no!se!visualize! !!!!!!!!self.list_ctrl_5ab.InsertColumn(1,u"Nombre")! !!!!!!!!self.list_ctrl_5ab.SetColumnWidth(1,100)! !!!!!!!!self.list_ctrl_5ab.InsertColumn(2,u"Precio")! !!!!!!!!self.list_ctrl_5ab.SetColumnWidth(2,60)! !!!!!!!!self.list_ctrl_5ab.InsertColumn(3,u"Disponible")! !!!!!!!!self.list_ctrl_5ab.SetColumnWidth(3,45)! !! !!!!!!!!self.list_ctrl_5a.InsertColumn(0,u"ID")! !!!!!!!!self.list_ctrl_5a.SetColumnWidth(0,0)!#!para!que!no!se!visualize! !!!!!!!!self.list_ctrl_5a.InsertColumn(1,u"Nombre")! !!!!!!!!self.list_ctrl_5a.SetColumnWidth(1,100)! !!!!!!!!self.list_ctrl_5a.InsertColumn(2,u"Precio")! !!!!!!!!self.list_ctrl_5a.SetColumnWidth(2,60)! !!!!!!!!self.list_ctrl_5a.InsertColumn(3,u"Disponible")! !!!!!!!!self.list_ctrl_5a.SetColumnWidth(3,45)! !! !!!!!!!!self.list_ctrl_5abc.InsertColumn(0,u"ID")! !!!!!!!!self.list_ctrl_5abc.SetColumnWidth(0,0)!#!para!que!no!se!visualize! !!!!!!!!self.list_ctrl_5abc.InsertColumn(1,u"Nombre")! !!!!!!!!self.list_ctrl_5abc.SetColumnWidth(1,100)! !!!!!!!!self.list_ctrl_5abc.InsertColumn(2,u"Precio")! !!!!!!!!self.list_ctrl_5abc.SetColumnWidth(2,60)! !!!!!!!!self.list_ctrl_5abc.InsertColumn(3,u"Disponible")! !!!!!!!!self.list_ctrl_5abc.SetColumnWidth(3,45)! !! !!!!!!!!#!Rellenando!la!lista!de!items!disponibles!
!! 97! Diseño e implementación de un servidor web para mensajes publicitarios para redes ad hoc ! !!!!!!!!for!data!in!servicio.Items:! !!!!!!!!!!!!pos!=!self.list_ctrl_3.InsertStringItem(0,str(data['_data']['id']))! !!!!!!!!!!!!self.list_ctrl_3.SetStringItem(pos,1,str(data['_data']['nombre']))! !!!!!!!!!!!!self.list_ctrl_3.SetStringItem(pos,2,str(data['_data']['precio']))! !!!!!!!!!!!!self.list_ctrl_3.SetStringItem(pos,3,str(data['_data']['disponible']))! !!!!!!!!!!!!! !!!!!!!!self.__set_properties()! !!!!!!!!self.__do_layout()! !!!!!!!!! !!!!!!!!#!creamos!el!menu!del!click!derecho!para!poder!mostrarlo!cuando!queramos! !!!!!!!!self.createMenu()! !! !!!!!!!!#!bindeamos!los!eventos!a!los!diferentes!objetos!que!hemos!creado!mas!arriba! !!!!!!!!self.list_ctrl_3.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK,!self.showPopupMenu)! !!!!!!!!self.Bind(wx.EVT_BUTTON,!self.save_menu,!self.Guardar)! !!!!!!!!self.Bind(wx.EVT_TEXT,!self.solo_num,!self.text_ctrl_1)! !!!!!!!!self.Bind(wx.EVT_TEXT,!self.solo_num2,!self.text_ctrl_2)! !!!!!!!!self.Bind(wx.EVT_BUTTON,!self.load_img,!self.button_14)! !!!!!!!!self.Bind(wx.EVT_BUTTON,!self.add_prim,!self.button_5)! !!!!!!!!self.Bind(wx.EVT_BUTTON,!self.left_prim,!self.button_7)! !!!!!!!!self.Bind(wx.EVT_BUTTON,!self.crear_item,!self.button_6)! !!!!!!!!self.Bind(wx.EVT_BUTTON,!self.add_seg,!self.button_8)! !!!!!!!!self.Bind(wx.EVT_BUTTON,!self.left_seg,!self.button_9)! !!!!!!!!self.Bind(wx.EVT_BUTTON,!self.add_postre,!self.button_10)! !!!!!!!!self.Bind(wx.EVT_BUTTON,!self.left_postre,!self.button_11)! !!!!!!!!! !!!!!!!!#!si!estamos!editando!un!menu!rellenamos!los!datos! !!!!!!!!if!menu!=;1:! !!!!!!!!!!!!print!'Editando!menu!'+menu! !!!!!!!!!!!!self.menuMod!=!self.searchMenu(menu,servicio.Menus)! !!!!!!!!!!!!self.text_ctrl_3.AppendText(self.menuMod['nombre'])! !!!!!!!!!!!!self.text_ctrl_2.AppendText(str(self.menuMod['precio']))! !!!!!!!!!!!!self.text_ctrl_descripcion.AppendText(self.menuMod['descripcion'])! !! !!!!!!!!!!!!#!seleccionar!fechas! !!!!!!!!!!!!fechaini!=!datetime.datetime.strptime(self.menuMod['fecha_ini'],!"%Y;%m; %dT00:00:00")! !!!!!!!!!!!!fechafin!=!datetime.datetime.strptime(self.menuMod['fecha_fin'],!"%Y;%m; %dT00:00:00")! !! !!!!!!!!!!!!self.calendar_ctrl_3.PySetDate(fechaini)! !!!!!!!!!!!!#!duracion!en!dias! !!!!!!!!!!!!delta!=!fechafin!;!fechaini! !!!!!!!!!!!!self.text_ctrl_1.AppendText(str(delta.days))! !!!!!!!!!!!!if!self.menuMod['disponible']!==!False:! !!!!!!!!!!!!!!!!self.checkbox_1.SetValue(0)! !!!!!!!!!!!!self.img!=!self.menuMod['imagen']! !!!!!!!!!!!!#!rellenar!listas! !!!!!!!!!!!!for!data!in!self.menuMod['primeros']:! !!!!!!!!!!!!!!!!pos!=! self.list_ctrl_5a.InsertStringItem(0,str(self.searchNomItem(data['_data']['nombre'],servicio.I tems)))!#!id!diferente!explicar! !!!!!!!!!!!!!!!!self.list_ctrl_5a.SetStringItem(pos,1,str(data['_data']['nombre']))! !!!!!!!!!!!!!!!!self.list_ctrl_5a.SetStringItem(pos,2,str(data['_data']['precio']))!
! ! ! Anexo&.Descripción&del&código! ! ! 98! !!!!!!!!!!!!!!!!self.list_ctrl_5a.SetStringItem(pos,3,str(data['_data']['disponible']))! !!!!!!!!!!!!!!!! self.primeros.append(self.searchNomItem(data['_data']['nombre'],servicio.Items))!#!guardamos! las!id!de!los!items!modelos! !!!!!!!!!!!!for!data!in!self.menuMod['segundos']:! !!!!!!!!!!!!!!!!pos!=! self.list_ctrl_5ab.InsertStringItem(0,str(self.searchNomItem(data['_data']['nombre'],servicio. Items)))! !!!!!!!!!!!!!!!!self.list_ctrl_5ab.SetStringItem(pos,1,str(data['_data']['nombre']))! !!!!!!!!!!!!!!!!self.list_ctrl_5ab.SetStringItem(pos,2,str(data['_data']['precio']))! !!!!!!!!!!!!!!!!self.list_ctrl_5ab.SetStringItem(pos,3,str(data['_data']['disponible']))! !!!!!!!!!!!!!!!! self.segundos.append(self.searchNomItem(data['_data']['nombre'],servicio.Items))! !!!!!!!!!!!!for!data!in!self.menuMod['postres']:! !!!!!!!!!!!!!!!!pos!=! self.list_ctrl_5abc.InsertStringItem(0,str(self.searchNomItem(data['_data']['nombre'],servicio .Items)))! !!!!!!!!!!!!!!!!self.list_ctrl_5abc.SetStringItem(pos,1,str(data['_data']['nombre']))! !!!!!!!!!!!!!!!!self.list_ctrl_5abc.SetStringItem(pos,2,str(data['_data']['precio']))! !!!!!!!!!!!!!!!!self.list_ctrl_5abc.SetStringItem(pos,3,str(data['_data']['disponible']))! !!!!!!!!!!!!!!!! self.postres.append(self.searchNomItem(data['_data']['nombre'],servicio.Items))! !!!!!!!!!!!!self.editando!=!True!#!variable!bandera! !! !!!!!!!!#!centramos!la!ventana!en!la!pantalla! !!!!!!!!self.Center()! !!!!!!!!#!end!wxGlade! !! !!!!def!searchMenu(self,!iditem,!items):! !!!!!!!!for!element!in!items:! !!!!!!!!!!!!if!str(element['nombre'])!==!iditem:! !!!!!!!!!!!!!!!!return!element! !! !!!!def!__set_properties(self):! !!!!!!!!#!begin!wxGlade:!crear_menu.__set_properties! !!!!!!!!#!definimos!el!titulo!de!la!ventana!y!los!tamaños!de!los!elementos!de!la!interfaz! !!!!!!!!self.SetTitle(_("Menu"))! !!!!!!!!self.SetSize((800,!550))! !!!!!!!!self.checkbox_1.SetValue(1)! !!!!!!!!self.text_ctrl_descripcion.SetMinSize((200,!100))! !!!!!!!!self.list_ctrl_3.SetMinSize((250,!510))! !!!!!!!!self.calendar_ctrl_3.SetMinSize((240,!150))! !!!!!!!!#!primeros!segundos!y!postre! !!!!!!!!self.list_ctrl_5a.SetMinSize((164,!140))! !!!!!!!!self.list_ctrl_5ab.SetMinSize((164,!140))! !!!!!!!!self.list_ctrl_5abc.SetMinSize((164,!140))! !!!!!!!!#!end!wxGlade! !! !!!!def!__do_layout(self):! !!!!!!!!#!begin!wxGlade:!crear_menu.__do_layout! !!!!!!!!#!definimos!el!layout!en!el!que!se!colocaran!los!elementos!de!la!interfaz! !!!!!!!!sizer_4!=!wx.BoxSizer(wx.VERTICAL)! !!!!!!!!sizer_5!=!wx.BoxSizer(wx.HORIZONTAL)! !!!!!!!!sizer_activo!=!wx.BoxSizer(wx.HORIZONTAL)!
!! 99! Diseño e implementación de un servidor web para mensajes publicitarios para redes ad hoc ! !!!!!!!!grid_sizer_6!=!wx.FlexGridSizer(1,!2,!0,!0)! !!!!!!!!sizer_7!=!wx.BoxSizer(wx.VERTICAL)! !!!!!!!!sizer_10!=!wx.BoxSizer(wx.VERTICAL)! !!!!!!!!sizer_11!=!wx.BoxSizer(wx.VERTICAL)! !!!!!!!!sizer_8!=!wx.BoxSizer(wx.VERTICAL)! !!!!!!!!sizer_9!=!wx.BoxSizer(wx.VERTICAL)! !!!!!!!!sizer_6!=!wx.BoxSizer(wx.VERTICAL)! !!!!!!!!sizer_guardar!=!wx.BoxSizer(wx.VERTICAL)! !!!!!!!!self.sizer_descripcion_staticbox.Lower()! !!!!!!!!self.sizer_17_staticbox.Lower()! !!!!!!!!sizer_17!=!wx.StaticBoxSizer(self.sizer_17_staticbox,!wx.HORIZONTAL)! !!!!!!!!self.sizer_16_staticbox.Lower()! !!!!!!!!sizer_16!=!wx.StaticBoxSizer(self.sizer_16_staticbox,!wx.HORIZONTAL)! !!!!!!!!self.sizer_15_staticbox.Lower()! !!!!!!!!sizer_15!=!wx.StaticBoxSizer(self.sizer_15_staticbox,!wx.HORIZONTAL)! !!!!!!!!grid_sizer_7!=!wx.FlexGridSizer(7,!1,!0,!0)! !!!!!!!!self.sizer_14_staticbox.Lower()! !!!!!!!!sizer_14!=!wx.StaticBoxSizer(self.sizer_14_staticbox,!wx.HORIZONTAL)! !!!!!!!!self.sizer_13_staticbox.Lower()! !!!!!!!!sizer_13!=!wx.StaticBoxSizer(self.sizer_13_staticbox,!wx.HORIZONTAL)! !!!!!!!!self.sizer_12_staticbox.Lower()! !!!!!!!!sizer_12!=!wx.StaticBoxSizer(self.sizer_12_staticbox,!wx.HORIZONTAL)! !!!!!!!!self.sizer_34_staticbox.Lower()! !!!!!!!!sizer_34!=!wx.StaticBoxSizer(self.sizer_34_staticbox,!wx.HORIZONTAL)! !!!!!!!!grid_sizer_7.Add(self.calendar_ctrl_3,!0,!wx.EXPAND,!0)! !!!!!!!!sizer_34.Add(self.text_ctrl_3,!0,!0,!0)! !!!!!!!!grid_sizer_7.Add(sizer_34,!1,!wx.EXPAND,!0)! !!!!!!!!sizer_12.Add(self.text_ctrl_1,!0,!0,!0)! !!!!!!!!grid_sizer_7.Add(sizer_12,!1,!wx.EXPAND,!0)! !!!!!!!!sizer_13.Add(self.text_ctrl_2,!0,!0,!0)! !!!!!!!!grid_sizer_7.Add(sizer_13,!1,!wx.EXPAND,!0)! !!!!!!!!sizer_14.Add(self.checkbox_1,!0,!0,!0)! !!!!!!!!grid_sizer_activo!=!wx.FlexGridSizer(1,!3,!0,!0)! !!!!!!!!grid_sizer_7.Add(grid_sizer_activo,1,0,0)! !!!!!!!!grid_sizer_activo.Add(sizer_14,!1,!wx.EXPAND,!0)! !!!!!!!!grid_sizer_activo.Add((40,10),0,0,0)! !!!!!!!!grid_sizer_activo.Add(self.button_14,!1,!wx.EXPAND,!0)! !!!!!!!!sizer_descripcion!=!wx.StaticBoxSizer(self.sizer_descripcion_staticbox,!wx.HORIZONTAL)! !!!!!!!!sizer_descripcion.Add(self.text_ctrl_descripcion,0,0,0)! !!!!!!!!grid_sizer_7.Add(sizer_descripcion,1,wx.EXPAND,0)! !!!!!!!!sizer_guardar.Add(self.Guardar,!0,!wx.ALIGN_CENTER_HORIZONTAL!|! wx.ALIGN_CENTER_VERTICAL,!0)!! !!!!!!!!grid_sizer_7.Add(sizer_guardar,0,wx.ALIGN_CENTER_HORIZONTAL!|! wx.ALIGN_CENTER_VERTICAL,0)! !!!!!!!!sizer_5.Add(grid_sizer_7,!1,!wx.EXPAND,!0)! !!!!!!!!sizer_15.Add(self.list_ctrl_5a,!1,!wx.EXPAND,!0)! !!!!!!!!sizer_6.Add(sizer_15,!1,!wx.EXPAND!|!wx.ALIGN_RIGHT!,!0)! !!!!!!!!sizer_16.Add(self.list_ctrl_5ab,!1,!wx.EXPAND!,!0)! !!!!!!!!sizer_6.Add(sizer_16,!1,!wx.EXPAND!|!wx.ALIGN_RIGHT,!0)! !!!!!!!!sizer_17.Add(self.list_ctrl_5abc,!1,!wx.EXPAND,!0)! !!!!!!!!sizer_6.Add(sizer_17,!1,!wx.EXPAND!|!wx.ALIGN_RIGHT,!0)! !!!!!!!!sizer_5.Add(sizer_6,!1,!wx.EXPAND,!0)! !!!!!!!!sizer_7.Add((85,!20),!0,!0,!0)! !!!!!!!!sizer_7.Add(self.button_6,!0,!0,!0)!
! ! ! Anexo&.Descripción&del&código! ! ! 100! !!!!!!!!sizer_7.Add((85,!20),!0,!0,!0)! !!!!!!!!sizer_7.Add(self.button_5,!0,!0,!0)! !!!!!!!!sizer_7.Add(self.button_7,!0,!0,!0)! !!!!!!!!sizer_7.Add((85,!150),!0,!0,!0)! !!!!!!!!sizer_9.Add(self.button_8,!0,!0,!0)! !!!!!!!!sizer_9.Add(self.button_9,!0,!0,!0)! !!!!!!!!sizer_8.Add(sizer_9,!1,!wx.EXPAND,!0)! !!!!!!!!sizer_8.Add((86,!120),!0,!0,!0)! !!!!!!!!sizer_7.Add(sizer_8,!1,!wx.EXPAND,!0)! !!!!!!!!sizer_11.Add(self.button_10,!0,!0,!0)! !!!!!!!!sizer_11.Add(self.button_11,!0,!0,!0)! !!!!!!!!sizer_10.Add(sizer_11,!1,!wx.EXPAND,!0)! !!!!!!!!sizer_7.Add(sizer_10,!1,!wx.EXPAND,!0)! !!!!!!!!grid_sizer_6.Add(sizer_7,!1,!wx.EXPAND,!0)! !!!!!!!!grid_sizer_6.Add(self.list_ctrl_3,!1,!0,!0)! !!!!!!!!sizer_5.Add(grid_sizer_6,!1,!wx.EXPAND,!0)! !!!!!!!!sizer_4.Add(sizer_5,!1,!wx.EXPAND,!0)! !!!!!!!!self.SetSizer(sizer_4)! !!!!!!!!self.Layout()! !!!!!!!!#!end!wxGlade! !! !!!!#!funcion!para!crear!el!context!menu!del!click!izquierdo! !!!!def!createMenu(self):! !!!!!!!!self.menu!=!wx.Menu()! !!!!!!!!item1!=!self.menu.Append(;1,u'Editar')!!!!!!!!!!!#!añadimos!los!items! !!!!!!!!item2!=!self.menu.Append(;1,u'Borrar')! !!!!!!!!self.Bind(wx.EVT_MENU,!self.editarItem,!item1)!!#!bindeamos!sus!acciones! !!!!!!!!self.Bind(wx.EVT_MENU,!self.borrarItem,!item2)! !! !!!!def!borrarItem(self,event):! !!!!!!!!print!'Borrando!item'! !!!!!!!!item!=!self.list_ctrl_3.GetItemText(self.list_ctrl_3.GetFocusedItem())! !!!!!!!!if!servicio.delItem(item):! !!!!!!!!!!!!msgbox!=!wx.MessageBox(u'!Item!borrado!',!u'Información',!wx.ICON_INFORMATION!|! wx.STAY_ON_TOP)! !!!!!!!!!!!!self.list_ctrl_3.DeleteAllItems()!#!limpiamos!la!lista! !!!!!!!!!!!!self.comprobarItemsBorrado(item)!! !!!!!!!!!!!!servicio.updateItems()!!!!!!!!!!!!#!actualizamos!los!items!del!servidor! !!!!!!!!!!!!for!data!in!servicio.Items:!!!!!!!#!y!actualizamos!la!lista! !!!!!!!!!!!!!!!!pos!=!self.list_ctrl_3.InsertStringItem(0,str(data['_data']['id']))! !!!!!!!!!!!!!!!!self.list_ctrl_3.SetStringItem(pos,1,str(data['_data']['nombre']))! !!!!!!!!!!!!!!!!self.list_ctrl_3.SetStringItem(pos,2,str(data['_data']['precio']))! !!!!!!!!!!!!!!!!self.list_ctrl_3.SetStringItem(pos,3,str(data['_data']['disponible']))! !!!!!!!!else:! !!!!!!!!!!!!print!'ERROR!al!borrar!el!item'! !!!!!!!!!!!!msgbox!=!wx.MessageBox(u'¡No!se!pudo!borrar!el!item!',!u'Alerta',! wx.ICON_EXCLAMATION!|!wx.STAY_ON_TOP)! !!!!!!!!event.Skip()! !! !!!!#!muestra!el!la!ventana!de!editar!item!con!los!datos!del!item!que!estamos!editando! !!!!#!tambien!se!bindea!el!evento!para!que!se!actualicen!los!items!cuando!se!cierre!la!ventana! de!editar!item! !!!!def!editarItem(self,event):! !!!!!!!!print!'Editando!item'!
!! 101! Diseño e implementación de un servidor web para mensajes publicitarios para redes ad hoc ! !!!!!!!!item!=!self.list_ctrl_3.GetItemText(self.list_ctrl_3.GetFocusedItem())! !!!!!!!!crearItem!=!crear_item(self,item=item)! !!!!!!!!crearItem.Show()! !!!!!!!!self.Hide()! !!!!!!!!crearItem.Bind(wx.EVT_CLOSE,!self.on_close_crear_item)! !!!!!!!!self.crearItem!=!crearItem!#!comentar!esto!creo!que!sobra! !!!!!!!!event.Skip()! !! !!!!#!muestra!el!menu!del!click!derecho! !!!!def!showPopupMenu(self,event):! !!!!!!!!print!'Boton!derecho'! !!!!!!!!position!=!self.ScreenToClient(wx.GetMousePosition())! !!!!!!!!item!=!self.list_ctrl_3.HitTest(event.GetPosition())! !!!!!!!!if!item[0]!=;1:!#!para!que!solo!aparezca!el!menu!cuando!pincha!en!un!item! !!!!!!!!!!!!self.PopupMenu(self.menu,position)! !!!!!!!!event.Skip()! !! !!!!#!cuando!se!borra!un!item!actualiza!las!listas!de!primeros,!segundos!y!postres!quitando!el! item!borrado!de!ellas! !!!!def!comprobarItemsBorrado(self,itemBorrado):! !!!!!!!!count!=!self.list_ctrl_5a.GetItemCount()! !!!!!!!!i=0! !!!!!!!!for!row!in!range(count):! !!!!!!!!!!!!item!=!self.list_ctrl_5a.GetItem(itemId=row,!col=0)! !!!!!!!!!!!!if!item.GetText()!==!itemBorrado:! !!!!!!!!!!!!!!!!self.list_ctrl_5a.DeleteItem(i)! !!!!!!!!!!!!!!!!self.primeros.pop(self.primeros.index(int(itemBorrado)))! !!!!!!!!!!!!!!!!break! !!!!!!!!!!!!i+=1! !!!!!!!!count!=!self.list_ctrl_5ab.GetItemCount()! !!!!!!!!i=0! !!!!!!!!for!row!in!range(count):! !!!!!!!!!!!!item!=!self.list_ctrl_5ab.GetItem(itemId=row,!col=0)! !!!!!!!!!!!!if!item.GetText()!==!itemBorrado:! !!!!!!!!!!!!!!!!self.list_ctrl_5ab.DeleteItem(i)! !!!!!!!!!!!!!!!!self.segundos.pop(self.segundos.index(int(itemBorrado)))! !!!!!!!!!!!!!!!!break! !!!!!!!!!!!!i+=1! !!!!!!!!count!=!self.list_ctrl_5abc.GetItemCount()! !!!!!!!!i=0! !!!!!!!!for!row!in!range(count):! !!!!!!!!!!!!item!=!self.list_ctrl_5abc.GetItem(itemId=row,!col=0)! !!!!!!!!!!!!if!item.GetText()!==!itemBorrado:! !!!!!!!!!!!!!!!!self.list_ctrl_5abc.DeleteItem(i)! !!!!!!!!!!!!!!!!self.postres.pop(self.postres.index(int(itemBorrado)))! !!!!!!!!!!!!!!!!break! !!!!!!!!!!!!i+=1! !! !!!!#!solo!deja!escribir!numeros! !!!!def!solo_num(self,!event):!!#!wxGlade:!crear_menu.<event_handler>! !!!!!!!!print!"solo_num"! !!!!!!!!raw_value!=!self.text_ctrl_1.GetValue().strip()! !!!!!!!!#!numeric!check! !!!!!!!!if!all(x!in!'0123456789'!for!x!in!raw_value):!
! ! ! Anexo&.Descripción&del&código! ! ! 102! !!!!!!!!!!!!pass! !!!!!!!!else:! !!!!!!!!!!!!self.text_ctrl_1.ChangeValue('')! !!!!!!!!event.Skip()! !! !!!!#!solo!deja!escribir!numeros!y!puntos! !!!!def!solo_num2(self,!event):!!#!wxGlade:!crear_menu.<event_handler>! !!!!!!!!print!"solo_num2"! !!!!!!!!raw_value!=!self.text_ctrl_2.GetValue().strip()! !!!!!!!!#!numeric!check! !!!!!!!!if!all(x!in!'0123456789.'!for!x!in!raw_value):! !!!!!!!!!!!!pass! !!!!!!!!else:! !!!!!!!!!!!!self.text_ctrl_2.ChangeValue('')! !!!!!!!!event.Skip()! !! !!!!#!Crea!el!menu!con!los!datos!introducidos! !!!!def!save_menu(self,!event):!!#!wxGlade:!crear_menu.<event_handler>! !!!!!!!!print!"save_menu"! !!!!!!!!#!comprobar!que!no!hay!campos!vacios! !!!!!!!!if!self.text_ctrl_2.GetValue()!=''!and!self.text_ctrl_1.GetValue()!=''!and! self.text_ctrl_3.GetValue()!=''!and!self.text_ctrl_descripcion.GetValue()!='':! !!!!!!!!!!!!#!generando!fechas!de!inicio!y!fin!del!menu!el!objeto!que!encapsula!el!sevicio!en! el!interfaz! !!!!!!!!!!!!#!recibe!las!fechas!en!formato!%Y;%m;%d!en!una!string! !!!!!!!!!!!!fechaini!=!self.calendar_ctrl_3.GetDate().FormatISODate()! !!!!!!!!!!!!fechaini2!=!datetime.datetime.strptime(fechaini,!"%Y;%m;%d")! !! !!!!!!!!!!!!fechafin!=!fechaini2!+!datetime.timedelta(!days=int(self.text_ctrl_1.GetValue())!)! !!!!!!!!!!!!fechafin!=!fechafin.date().isoformat()! !! !!!!!!!!!!!!if!self.editando!==!False:! !!!!!!!!!!!!!!!!#!crear!menu! !!!!!!!!!!!!!!!!if!servicio.createMenu(self.checkbox_1.GetValue(),! self.text_ctrl_2.GetValue(),!self.text_ctrl_3.GetValue(),! self.text_ctrl_descripcion.GetValue(),!fechaini,!fechafin,!self.img):! !!!!!!!!!!!!!!!!!!!!#!asignando!items! !!!!!!!!!!!!!!!!!!!!if!servicio.addItemMenuP(self.primeros,!self.text_ctrl_3.GetValue())!and! servicio.addItemMenuS(self.segundos,!self.text_ctrl_3.GetValue())!and! servicio.addItemMenuD(self.postres,!self.text_ctrl_3.GetValue()):! !!!!!!!!!!!!!!!!!!!!!!!!print!'Items!asignados'! !!!!!!!!!!!!!!!!!!!!!!!!msgbox!=!wx.MessageBox(u'!Menu!creado!',!u'Información',! wx.ICON_INFORMATION!|!wx.STAY_ON_TOP)! !!!!!!!!!!!!!!!!!!!!!!!!self.Close(True)! !!!!!!!!!!!!!!!!!!!!else:! !!!!!!!!!!!!!!!!!!!!!!!!servicio.delMenu(self.text_ctrl_3.GetValue())! !!!!!!!!!!!!!!!!!!!!!!!!msgbox!=!wx.MessageBox(u'¡No!se!pudo!crear!menu!',!u'Alerta',! wx.ICON_EXCLAMATION!|!wx.STAY_ON_TOP)! !!!!!!!!!!!!!!!!else:! !!!!!!!!!!!!!!!!!!!!msgbox!=!wx.MessageBox(u'¡No!se!pudo!crear!el!menu,!cambie!el!nombre!',! u'Alerta',!wx.ICON_EXCLAMATION!|!wx.STAY_ON_TOP)! !!!!!!!!!!!!else:! !!!!!!!!!!!!!!!!#!editando!menu! !!!!!!!!!!!!!!!!if!servicio.updateMenu(self.checkbox_1.GetValue(),!
!! 103! Diseño e implementación de un servidor web para mensajes publicitarios para redes ad hoc ! self.text_ctrl_2.GetValue(),!self.text_ctrl_3.GetValue(),! self.text_ctrl_descripcion.GetValue(),!fechaini,!fechafin,!self.img):! !!!!!!!!!!!!!!!!!!!!#!asignando!items! !!!!!!!!!!!!!!!!!!!!if!servicio.addItemMenuP(self.primerosModAdd,!self.text_ctrl_3.GetValue())! and!servicio.addItemMenuS(self.segundosModAdd,!self.text_ctrl_3.GetValue())!and! servicio.addItemMenuD(self.postresModAdd,!self.text_ctrl_3.GetValue())!and! servicio.delItemMenuP(self.primerosModDel,!self.text_ctrl_3.GetValue())!and! servicio.delItemMenuS(self.segundosModDel,!self.text_ctrl_3.GetValue())!and! servicio.delItemMenuD(self.postresModDel,!self.text_ctrl_3.GetValue()):! !!!!!!!!!!!!!!!!!!!!!!!!print!'Items!asignados'! !!!!!!!!!!!!!!!!!!!!!!!!msgbox!=!wx.MessageBox(u'!Menu!editado!',!u'Información',! wx.ICON_INFORMATION!|!wx.STAY_ON_TOP)! !!!!!!!!!!!!!!!!!!!!!!!!self.Close(True)! !!!!!!!!!!!!!!!!!!!!else:! !!!!!!!!!!!!!!!!!!!!!!!!servicio.delMenu(self.text_ctrl_3.GetValue())! !!!!!!!!!!!!!!!!!!!!!!!!msgbox!=!wx.MessageBox(u'¡No!se!pudo!editar!los!items!del!menu!',! u'Alerta',!wx.ICON_EXCLAMATION!|!wx.STAY_ON_TOP)! !!!!!!!!!!!!!!!!else:! !!!!!!!!!!!!!!!!!!!!msgbox!=!wx.MessageBox(u'¡No!se!pudo!editar!el!menu!',!u'Alerta',! wx.ICON_EXCLAMATION!|!wx.STAY_ON_TOP)! !!!!!!!!else:! !!!!!!!!!!!!msgbox!=!wx.MessageBox(u'¡Rellena!los!campos!',!u'Alerta',!wx.ICON_EXCLAMATION!|! wx.STAY_ON_TOP)! !!!!!!!!event.Skip()! !! !!!!#!muestra!selector!de!archivos!nativo!para!seleccionar!la!imagen!que!queremos!y!genera!el! base64!de!la!misma! !!!!def!load_img(self,!event):!!#!wxGlade:!crear_menu.<event_handler>! !!!!!!!!print!"load_img"! !!!!!!!!img!=!self.img! !!!!!!!!openFileDialog!=!wx.FileDialog(self,!u"Selecionar!imagen!del!menu",!"",!"",!"pictures! (*.jpeg,*.jpg,*.png)|*.jpeg;*.jpg;*.png",!wx.FD_OPEN!|!wx.FD_FILE_MUST_EXIST)! !! !!!!!!!!if!openFileDialog.ShowModal()!==!wx.ID_CANCEL:! !!!!!!!!!!!!return!!!!!#!the!user!changed!idea...! !! !!!!!!!!print!openFileDialog.GetPath()! !!!!!!!!#!pasar!a!base64!y!guardar!en!variable! !!!!!!!!with!open(openFileDialog.GetPath(),!"rb")!as!imageFile:! !!!!!!!!!!!!img!=!base64.b64encode(imageFile.read())! !!!!!!!!if!img!=self.img:! !!!!!!!!!!!!msgbox!=!wx.MessageBox(u'!Imágen!guardada!',!u'Información',!wx.ICON_INFORMATION!|! wx.STAY_ON_TOP)! !!!!!!!!!!!!self.img!=!img! !!!!!!!!else:! !!!!!!!!!!!!msgbox!=!wx.MessageBox(u'¡La!imágen!no!se!guardo,!o!elegiste!la!misma!que!ya! estaba!guardada!',!u'Alerta',!wx.ICON_EXCLAMATION!|!wx.STAY_ON_TOP)! !!!!!!!!event.Skip()! !! !!!!#!Añade!un!item!a!la!lista!de!los!primeros! !!!!def!add_prim(self,!event):!!#!wxGlade:!crear_menu.<event_handler>! !!!!!!!!print!"add_prim"! !!!!!!!!#!comprobar!si!el!item!existe!en!la!lista!para!no!añadirlo!dos!veces! !!!!!!!!if!self.list_ctrl_3.GetFocusedItem()!=;1:!
! ! ! Anexo&.Descripción&del&código! ! ! 104! !!!!!!!!!!!!item!=! self.searchItem(self.list_ctrl_3.GetItemText(self.list_ctrl_3.GetFocusedItem()),servicio.Items )! !!!!!!!!!!!!try:! !!!!!!!!!!!!!!!!self.primeros.index(item['_data']['id'])! !!!!!!!!!!!!!!!!#!el!item!existe! !!!!!!!!!!!!except!Exception,!e:! !!!!!!!!!!!!!!!!#!el!item!no!esta!en!la!lista!primeros! !!!!!!!!!!!!!!!!if!self.list_ctrl_3.GetFirstSelected()!=;1:! !!!!!!!!!!!!!!!!!!!!pos!=!self.list_ctrl_5a.InsertStringItem(0,str(item['_data']['id']))! !!!!!!!!!!!!!!!!!!!!self.list_ctrl_5a.SetStringItem(pos,1,str(item['_data']['nombre']))! !!!!!!!!!!!!!!!!!!!!self.list_ctrl_5a.SetStringItem(pos,2,str(item['_data']['precio']))! !!!!!!!!!!!!!!!!!!!!self.list_ctrl_5a.SetStringItem(pos,3,str(item['_data']['disponible']))! !!!!!!!!!!!!!!!!!!!!self.primeros.append(item['_data']['id'])! !!!!!!!!!!!!!!!!!!!!if!self.editando!==!True:! !!!!!!!!!!!!!!!!!!!!!!!!try:! !!!!!!!!!!!!!!!!!!!!!!!!!!!! self.primerosModDel.pop(self.primerosModDel.index(item['_data']['nombre']))! !!!!!!!!!!!!!!!!!!!!!!!!!!!!#!si!se!habia!añadido!para!borrarse!lo!quitamos! !!!!!!!!!!!!!!!!!!!!!!!!except!Exception,!e:! !!!!!!!!!!!!!!!!!!!!!!!!!!!!#!en!caso!contrario!lo!añadimos!para!que!se!añada! !!!!!!!!!!!!!!!!!!!!!!!!!!!!self.primerosModAdd.append(item['_data']['id'])! !!!!!!!!event.Skip()! !! !!!!#!Elimina!el!item!seleccionado!o!el!primero!en!caso!de!no!estar!ninguno!seleccionado!de!la! lista!de!primeros! !!!!def!left_prim(self,!event):!!#!wxGlade:!crear_menu.<event_handler>! !!!!!!!!print!"left_prim"! !!!!!!!!if!self.list_ctrl_5a.GetFirstSelected()!=;1:! !!!!!!!!!!!!item!=!self.searchItem(!self.list_ctrl_5a.GetItemText(! self.list_ctrl_5a.GetFirstSelected()!),!servicio.Items!)! !!!!!!!!!!!!self.list_ctrl_5a.DeleteItem(self.list_ctrl_5a.GetFirstSelected())! !!!!!!!!!!!!self.primeros.pop(self.primeros.index(item['_data']['id']))! !!!!!!!!!!!!if!self.editando!==!True:! !!!!!!!!!!!!!!!!try:! !!!!!!!!!!!!!!!!!!!!self.primerosModAdd.pop(self.primerosModAdd.index(item['_data']['id']))! !!!!!!!!!!!!!!!!!!!!#!antes!de!añadirlo!a!la!lista!de!borrados!comprobamos!si!se!a!añadido! como!nuevo!item!en!al!modificacion! !!!!!!!!!!!!!!!!except!Exception,!e:! !!!!!!!!!!!!!!!!!!!!#!si!no!lo!esta!quiere!decir!que!se!esta!borrando!un!item!que!estaba!desde! el!principio!en!el!manu! !!!!!!!!!!!!!!!!!!!!self.primerosModDel.append(item['_data']['nombre'])! !!!!!!!!else:! !!!!!!!!!!!!try:! !!!!!!!!!!!!!!!!self.primeros.pop(self.primeros.index(int(self.list_ctrl_5a.GetItem(itemId=0,! col=0).GetText())))! !!!!!!!!!!!!!!!!if!self.editando!==!True:! !!!!!!!!!!!!!!!!!!!!try:! !!!!!!!!!!!!!!!!!!!!!!!! self.primerosModAdd.pop(self.primerosModAdd.index(str(self.list_ctrl_5a.GetItem(itemId=0,! col=1).GetText())))! !!!!!!!!!!!!!!!!!!!!except!Exception,!e:! !!!!!!!!!!!!!!!!!!!!!!!!self.primerosModDel.append(str(self.list_ctrl_5a.GetItem(itemId=0,! col=1).GetText()))!
!! 105! Diseño e implementación de un servidor web para mensajes publicitarios para redes ad hoc ! !!!!!!!!!!!!!!!!self.list_ctrl_5a.DeleteItem(0)! !!!!!!!!!!!!except!Exception,!e:! !!!!!!!!!!!!!!!!pass! !!!!!!!!event.Skip()! !! !!!!#!Busta!iditem!en!la!lista!de!items! !!!!def!searchItem(self,iditem,!items):! !!!!!!!!for!element!in!items:! !!!!!!!!!!!!if!str(element['_data']['id'])!==!str(iditem):! !!!!!!!!!!!!!!!!return!element! !! !!!!#!Abre!la!ventana!de!crear!item!y!bindea!un!evento!para!que!al!cerrarse!se!actualice!la! lista!de!items! !!!!def!crear_item(self,!event):!!#!wxGlade:!crear_menu.<event_handler>! !!!!!!!!print!"crear_item"! !!!!!!!!crearItem!=!crear_item(self)! !!!!!!!!crearItem.Show()! !!!!!!!!self.Hide()!!!!!!!!!!!!!!!!!#!oculta!la!ventana!de!crear!menu! !!!!!!!!crearItem.Bind(wx.EVT_CLOSE,!self.on_close_crear_item)! !!!!!!!!self.crearItem!=!crearItem!#!comentar!esto!creo!que!sobra! !!!!!!!!event.Skip()! !! !!!!#!Actualiza!la!lista!de!items! !!!!def!on_close_crear_item(self,!event):! !!!!!!!!self.list_ctrl_3.DeleteAllItems()!#!limpiamos!la!lista! !!!!!!!!servicio.updateItems()!!!!!!!!!!!!#!actualizamos!los!items!del!servidor! !!!!!!!!for!data!in!servicio.Items:!!!!!!!#!y!actualizamos!la!lista! !!!!!!!!!!!!pos!=!self.list_ctrl_3.InsertStringItem(0,str(data['_data']['id']))! !!!!!!!!!!!!self.list_ctrl_3.SetStringItem(pos,1,str(data['_data']['nombre']))! !!!!!!!!!!!!self.list_ctrl_3.SetStringItem(pos,2,str(data['_data']['precio']))! !!!!!!!!!!!!self.list_ctrl_3.SetStringItem(pos,3,str(data['_data']['disponible']))! !!!!!!!!if!self.editando:! !!!!!!!!!!!!#!actualizamos!las!listas!de!items! !!!!!!!!!!!!self.menuMod!=!servicio.getMenu(self.menuMod['nombre'])! !!!!!!!!!!!!self.list_ctrl_5a.DeleteAllItems()! !!!!!!!!!!!!for!data!in!self.menuMod['primeros']:! !!!!!!!!!!!!!!!!pos!=! self.list_ctrl_5a.InsertStringItem(0,str(self.searchNomItem(data['_data']['nombre'],servicio.I tems)))!#!id!diferente!explicar! !!!!!!!!!!!!!!!!self.list_ctrl_5a.SetStringItem(pos,1,str(data['_data']['nombre']))! !!!!!!!!!!!!!!!!self.list_ctrl_5a.SetStringItem(pos,2,str(data['_data']['precio']))! !!!!!!!!!!!!!!!!self.list_ctrl_5a.SetStringItem(pos,3,str(data['_data']['disponible']))! !!!!!!!!!!!!self.list_ctrl_5ab.DeleteAllItems()! !!!!!!!!!!!!for!data!in!self.menuMod['segundos']:! !!!!!!!!!!!!!!!!pos!=! self.list_ctrl_5ab.InsertStringItem(0,str(self.searchNomItem(data['_data']['nombre'],servicio. Items)))! !!!!!!!!!!!!!!!!self.list_ctrl_5ab.SetStringItem(pos,1,str(data['_data']['nombre']))! !!!!!!!!!!!!!!!!self.list_ctrl_5ab.SetStringItem(pos,2,str(data['_data']['precio']))! !!!!!!!!!!!!!!!!self.list_ctrl_5ab.SetStringItem(pos,3,str(data['_data']['disponible']))! !!!!!!!!!!!!self.list_ctrl_5abc.DeleteAllItems()! !!!!!!!!!!!!for!data!in!self.menuMod['postres']:! !!!!!!!!!!!!!!!!pos!=! self.list_ctrl_5abc.InsertStringItem(0,str(self.searchNomItem(data['_data']['nombre'],servicio
! ! ! Anexo&.Descripción&del&código! ! ! 112! !!!!def!__init__(self,!parent,!oferta=;1,!*args,!**kwds):! !!!!!!!!#!begin!wxGlade:!crear_oferta.__init__! !!!!!!!!kwds["style"]!=!wx.CLOSE_BOX|wx.CAPTION|wx.MINIMIZE_BOX|wx.CLIP_CHILDREN! !!!!!!!!wx.Frame.__init__(self,!parent,!*args,!**kwds)! !!!!!!!!self.calendar_ctrl_4!=!wx.calendar.CalendarCtrl(self,!wx.ID_ANY,! style=wx.calendar.CAL_MONDAY_FIRST)! !!!!!!!!#self.text_ctrl_10!=!wx.TextCtrl(self,!wx.ID_ANY,!"")! !!!!!!!!self.sizer_35_staticbox!=!wx.StaticBox(self,!wx.ID_ANY,!_(u"Nombre"))! !!!!!!!!self.text_ctrl_5!=!wx.TextCtrl(self,!wx.ID_ANY,!"")! !!!!!!!!self.sizer_26_staticbox!=!wx.StaticBox(self,!wx.ID_ANY,!_(u"Duración!en!dias"))! !!!!!!!!self.text_ctrl_6!=!wx.TextCtrl(self,!wx.ID_ANY,!"")! !!!!!!!!self.sizer_27_staticbox!=!wx.StaticBox(self,!wx.ID_ANY,!_(u"Precio"))! !!!!!!!!self.checkbox_3!=!wx.CheckBox(self,!wx.ID_ANY,!"")! !!!!!!!!self.sizer_28_staticbox!=!wx.StaticBox(self,!wx.ID_ANY,!_(u"Activo"))! !!!!!!!!self.button_15!=!wx.Button(self,!wx.ID_ANY,!_(u"img"))! !!!!!!!!self.button_16!=!wx.Button(self,!wx.ID_ANY,!_(u"Guardar"))! !!!!!!!!self.list_ctrl_5!=!wx.ListCtrl(self,!wx.ID_ANY,!style=wx.LC_REPORT!|!wx.SUNKEN_BORDER)! !!!!!!!!self.sizer_29_staticbox!=!wx.StaticBox(self,!wx.ID_ANY,!_(u"Items"))! !!!!!!!!self.button_17!=!wx.Button(self,!wx.ID_ANY,!_(u"<<"))! !!!!!!!!self.button_18!=!wx.Button(self,!wx.ID_ANY,!_(u">>"))! !!!!!!!!self.button_crear_item!=!wx.Button(self,!wx.ID_ANY,!_(u"Crear!item"))! !!!!!!!!self.list_ctrl_4!=!wx.ListCtrl(self,!wx.ID_ANY,!style=wx.LC_REPORT!|!wx.SUNKEN_BORDER)! !!!!!!!!self.text_ctrl_descripcion!=!wx.TextCtrl(self,!wx.ID_ANY,!"",!style=wx.TE_MULTILINE)! !!!!!!!!self.sizer_descripcion_staticbox!=!wx.StaticBox(self,!wx.ID_ANY,!_(u"Descripción"))! !!!!!!!!if!oferta!=;1:! !!!!!!!!!!!!self.text_ctrl_10!=!wx.TextCtrl(self,!wx.ID_ANY,!"",! style=wx.TE_READONLY,size=(200,;1))! !!!!!!!!else:! !!!!!!!!!!!!self.text_ctrl_10!=!wx.TextCtrl(self,!wx.ID_ANY,!"",!size=(200,;1))! !! !!!!!!!!self.createMenu()! !! !!!!!!!!self.list_ctrl_4.InsertColumn(0,u"ID")! !!!!!!!!self.list_ctrl_4.SetColumnWidth(0,0)! !!!!!!!!self.list_ctrl_4.InsertColumn(1,u"Nombre")! !!!!!!!!self.list_ctrl_4.SetColumnWidth(1,100)! !!!!!!!!self.list_ctrl_4.InsertColumn(2,u"Precio")! !!!!!!!!self.list_ctrl_4.SetColumnWidth(2,60)! !!!!!!!!self.list_ctrl_4.InsertColumn(3,u"Disponible")! !!!!!!!!self.list_ctrl_4.SetColumnWidth(3,45)! !! !!!!!!!!self.list_ctrl_5.InsertColumn(0,u"ID")! !!!!!!!!self.list_ctrl_5.SetColumnWidth(0,0)! !!!!!!!!self.list_ctrl_5.InsertColumn(1,u"Nombre")! !!!!!!!!self.list_ctrl_5.SetColumnWidth(1,100)! !!!!!!!!self.list_ctrl_5.InsertColumn(2,u"Precio")! !!!!!!!!self.list_ctrl_5.SetColumnWidth(2,60)! !!!!!!!!self.list_ctrl_5.InsertColumn(3,u"Disponible")! !!!!!!!!self.list_ctrl_5.SetColumnWidth(3,45)! !! !!!!!!!!for!data!in!servicio.Items:! !!!!!!!!!!!!pos!=!self.list_ctrl_4.InsertStringItem(0,str(data['_data']['id']))! !!!!!!!!!!!!self.list_ctrl_4.SetStringItem(pos,1,str(data['_data']['nombre']))! !!!!!!!!!!!!self.list_ctrl_4.SetStringItem(pos,2,str(data['_data']['precio']))!
!! 113! Diseño e implementación de un servidor web para mensajes publicitarios para redes ad hoc ! !!!!!!!!!!!!self.list_ctrl_4.SetStringItem(pos,3,str(data['_data']['disponible']))! !! !!!!!!!!#!variables!para!guardar!los!items!asignados!y!la!codificacion!en!base64!de!la!imagen! !!!!!!!!#!en!ellos!se!listan!las!id!de!la!base!de!datos! !!!!!!!!self.items!=![]! !!!!!!!!self.img!=!0! !!!!!!!!#!variables!temporales!para!los!items!que!se!asignan!y!se!borran!mientras!editamos!un! menu! !!!!!!!!self.itemsModAdd!=![]! !!!!!!!!self.itemsModDel!=![]! !! !!!!!!!!self.editando!=!False! !! !!!!!!!!self.__set_properties()! !!!!!!!!self.__do_layout()! !! !!!!!!!!self.Bind(wx.EVT_TEXT,!self.solo_num,!self.text_ctrl_5)! !!!!!!!!self.Bind(wx.EVT_TEXT,!self.solo_num2,!self.text_ctrl_6)! !!!!!!!!self.Bind(wx.EVT_CHECKBOX,!self.activo,!self.checkbox_3)! !!!!!!!!self.Bind(wx.EVT_BUTTON,!self.load_img_oferta,!self.button_15)! !!!!!!!!self.Bind(wx.EVT_BUTTON,!self.guardar_oferta,!self.button_16)! !!!!!!!!self.Bind(wx.EVT_LIST_ITEM_SELECTED,!self.item_selec2,!self.list_ctrl_5)! !!!!!!!!self.Bind(wx.EVT_BUTTON,!self.pasar_izq,!self.button_17)! !!!!!!!!self.Bind(wx.EVT_BUTTON,!self.pasar_der,!self.button_18)! !!!!!!!!self.Bind(wx.EVT_LIST_ITEM_SELECTED,!self.item_select,!self.list_ctrl_4)! !!!!!!!!self.Bind(wx.EVT_BUTTON,!self.crear_item,!self.button_crear_item)! !!!!!!!!self.list_ctrl_4.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK,!self.showPopupMenu)! !! !!!!!!!!#!si!estamos!editando!una!oferta!rellenamos!los!datos! !!!!!!!!if!oferta!=;1:! !!!!!!!!!!!!print!'Editando!oferta!'+oferta! !!!!!!!!!!!!self.ofertaMod!=!self.searchOferta(oferta,servicio.Ofertas)! !!!!!!!!!!!!self.text_ctrl_10.AppendText(self.ofertaMod['nombre'])! !!!!!!!!!!!!self.text_ctrl_6.AppendText(str(self.ofertaMod['precio']))! !!!!!!!!!!!!self.text_ctrl_descripcion.AppendText(self.ofertaMod['descripcion'])! !! !!!!!!!!!!!!#!seleccionar!fechas! !!!!!!!!!!!!fechaini!=!datetime.datetime.strptime(self.ofertaMod['fecha_ini'],!"%Y;%m; %dT00:00:00")! !!!!!!!!!!!!fechafin!=!datetime.datetime.strptime(self.ofertaMod['fecha_fin'],!"%Y;%m; %dT00:00:00")! !! !!!!!!!!!!!!self.calendar_ctrl_4.PySetDate(fechaini)! !!!!!!!!!!!!#!duracion!en!dias! !!!!!!!!!!!!delta!=!fechafin!;!fechaini! !!!!!!!!!!!!self.text_ctrl_5.AppendText(str(delta.days))! !!!!!!!!!!!!if!self.ofertaMod['disponible']!==!False:! !!!!!!!!!!!!!!!!self.checkbox_3.SetValue(0)! !!!!!!!!!!!!self.img!=!self.ofertaMod['imagen']! !!!!!!!!!!!!#!rellenar!listas! !!!!!!!!!!!!for!data!in!self.ofertaMod['items']:! !!!!!!!!!!!!!!!!pos!=! self.list_ctrl_5.InsertStringItem(0,str(self.searchNomItem(data['_data']['nombre'],servicio.It ems)))!#!id!diferente!explicar!
! ! ! Anexo&.Descripción&del&código! ! ! 114! !!!!!!!!!!!!!!!!self.list_ctrl_5.SetStringItem(pos,1,str(data['_data']['nombre']))! !!!!!!!!!!!!!!!!self.list_ctrl_5.SetStringItem(pos,2,str(data['_data']['precio']))! !!!!!!!!!!!!!!!!self.list_ctrl_5.SetStringItem(pos,3,str(data['_data']['disponible']))! !!!!!!!!!!!!!!!!self.items.append(self.searchNomItem(data['_data']['nombre'],servicio.Items))! #!guardamos!las!id!de!los!items!modelos! !!!!!!!!!!!!self.editando!=!True!#!variable!bandera! !! !!!!!!!!#!centramos!la!ventana!en!la!pantalla! !!!!!!!!self.Center()! !!!!!!!!#!end!wxGlade! !! !!!!def!__set_properties(self):! !!!!!!!!#!begin!wxGlade:!crear_oferta.__set_properties! !!!!!!!!self.SetTitle(_("Oferta"))! !!!!!!!!self.SetSize((800,!550))! !!!!!!!!self.checkbox_3.SetValue(1)! !!!!!!!!self.list_ctrl_5.SetMinSize((150,!500))! !!!!!!!!self.list_ctrl_4.SetMinSize((250,!500))! !!!!!!!!self.calendar_ctrl_4.SetMinSize((240,!150))! !!!!!!!!self.text_ctrl_descripcion.SetMinSize((200,!100))! !!!!!!!!#!end!wxGlade! !! !!!!def!__do_layout(self):! !!!!!!!!#!begin!wxGlade:!crear_oferta.__do_layout! !!!!!!!!sizer_24!=!wx.BoxSizer(wx.VERTICAL)! !!!!!!!!sizer_25!=!wx.BoxSizer(wx.HORIZONTAL)! !!!!!!!!grid_sizer_10!=!wx.FlexGridSizer(1,!2,!0,!0)! !!!!!!!!sizer_30!=!wx.BoxSizer(wx.VERTICAL)! !!!!!!!!sizer_31!=!wx.BoxSizer(wx.VERTICAL)! !!!!!!!!self.sizer_29_staticbox.Lower()! !!!!!!!!self.sizer_descripcion_staticbox.Lower()! !!!!!!!!sizer_29!=!wx.StaticBoxSizer(self.sizer_29_staticbox,!wx.HORIZONTAL)! !!!!!!!!grid_sizer_9!=!wx.FlexGridSizer(7,!1,!0,!0)! !!!!!!!!self.sizer_28_staticbox.Lower()! !!!!!!!!sizer_28!=!wx.StaticBoxSizer(self.sizer_28_staticbox,!wx.HORIZONTAL)! !!!!!!!!self.sizer_27_staticbox.Lower()! !!!!!!!!sizer_27!=!wx.StaticBoxSizer(self.sizer_27_staticbox,!wx.HORIZONTAL)! !!!!!!!!self.sizer_26_staticbox.Lower()! !!!!!!!!sizer_26!=!wx.StaticBoxSizer(self.sizer_26_staticbox,!wx.HORIZONTAL)! !!!!!!!!self.sizer_35_staticbox.Lower()! !!!!!!!!sizer_35!=!wx.StaticBoxSizer(self.sizer_35_staticbox,!wx.HORIZONTAL)! !!!!!!!!grid_sizer_9.Add(self.calendar_ctrl_4,!0,!wx.EXPAND,!0)! !!!!!!!!sizer_35.Add(self.text_ctrl_10,!0,!0,!0)! !!!!!!!!grid_sizer_9.Add(sizer_35,!1,!wx.EXPAND,!0)! !!!!!!!!sizer_26.Add(self.text_ctrl_5,!0,!0,!0)! !!!!!!!!grid_sizer_9.Add(sizer_26,!1,!wx.EXPAND,!0)! !!!!!!!!sizer_27.Add(self.text_ctrl_6,!0,!0,!0)! !!!!!!!!grid_sizer_9.Add(sizer_27,!1,!wx.EXPAND,!0)! !!!!!!!!sizer_28.Add(self.checkbox_3,!0,!0,!0)! !!!!!!!!grid_sizer_activo!=!wx.FlexGridSizer(1,!3,!0,!0)! !!!!!!!!grid_sizer_activo.Add(sizer_28,!1,!wx.EXPAND,!0)! !!!!!!!!grid_sizer_activo.Add((40,10),0,0,0)! !!!!!!!!grid_sizer_activo.Add(self.button_15,!1,!wx.EXPAND,!0)! !!!!!!!!sizer_descripcion!=!wx.StaticBoxSizer(self.sizer_descripcion_staticbox,!wx.HORIZONTAL)!
!! 115! Diseño e implementación de un servidor web para mensajes publicitarios para redes ad hoc ! !!!!!!!!sizer_descripcion.Add(self.text_ctrl_descripcion,0,0,0)! !!!!!!!!grid_sizer_9.Add(grid_sizer_activo,1,wx.EXPAND,0)! !!!!!!!!grid_sizer_9.Add(sizer_descripcion,1,wx.EXPAND,0)! !!!!!!!!grid_sizer_9.Add(self.button_16,!0,!wx.ALIGN_CENTER_HORIZONTAL!|! wx.ALIGN_CENTER_VERTICAL,!0)! !!!!!!!!sizer_25.Add(grid_sizer_9,!1,!wx.EXPAND,!0)! !!!!!!!!sizer_29.Add(self.list_ctrl_5,!1,!wx.EXPAND,!0)! !!!!!!!!sizer_25.Add(sizer_29,!1,!wx.EXPAND,!0)! !!!!!!!!sizer_30.Add((80,!40),!0,!0,!0)! !!!!!!!!sizer_30.Add(self.button_crear_item!,0,0,0)! !!!!!!!!sizer_30.Add((80,!160),!0,!0,!0)! !!!!!!!!sizer_31.Add(self.button_17,!0,!0,!0)! !!!!!!!!sizer_31.Add(self.button_18,!0,!0,!0)! !!!!!!!!sizer_30.Add(sizer_31,!1,!wx.EXPAND,!0)! !!!!!!!!grid_sizer_10.Add(sizer_30,!1,!wx.EXPAND,!0)! !!!!!!!!grid_sizer_10.Add(self.list_ctrl_4,!1,!wx.EXPAND,!0)! !!!!!!!!sizer_25.Add(grid_sizer_10,!1,!wx.EXPAND,!0)! !!!!!!!!sizer_24.Add(sizer_25,!1,!wx.EXPAND,!0)! !!!!!!!!self.SetSizer(sizer_24)! !!!!!!!!self.Layout()! !!!!!!!!#!end!wxGlade! !! !!!!#!Busta!iditem!en!la!lista!de!ofertas! !!!!def!searchOferta(self,!iditem,!items):! !!!!!!!!for!element!in!items:! !!!!!!!!!!!!if!str(element['nombre'])!==!iditem:! !!!!!!!!!!!!!!!!return!element! !! !!!!#!Busta!iditem!en!la!lista!de!items! !!!!def!searchItem(self,iditem,!items):! !!!!!!!!for!element!in!items:! !!!!!!!!!!!!if!str(element['_data']['id'])!==!iditem:! !!!!!!!!!!!!!!!!return!element! !! !!!!#!Abre!la!ventana!de!crear!item!y!bindea!un!evento!para!que!al!cerrarse!se!actualice!la! lista!de!items! !!!!def!crear_item(self,!event):!!#!wxGlade:!crear_menu.<event_handler>! !!!!!!!!print!"crear_item"! !!!!!!!!crearItem!=!crear_item(self)! !!!!!!!!crearItem.Show()! !!!!!!!!self.Hide()!!!!!!!!!!!!!!!!!#!oculta!la!ventana!de!crear!menu! !!!!!!!!crearItem.Bind(wx.EVT_CLOSE,!self.on_close_crear_item)! !!!!!!!!self.crearItem!=!crearItem!#!comentar!esto!creo!que!sobra! !!!!!!!!event.Skip()! !! !!!!#!Actualiza!la!lista!de!items! !!!!def!on_close_crear_item(self,!event):! !!!!!!!!self.list_ctrl_4.DeleteAllItems()!#!limpiamos!la!lista! !!!!!!!!servicio.updateItems()!!!!!!!!!!!!#!actualizamos!los!items!del!servidor! !!!!!!!!for!data!in!servicio.Items:!!!!!!!#!y!actualizamos!la!lista! !!!!!!!!!!!!pos!=!self.list_ctrl_4.InsertStringItem(0,str(data['_data']['id']))! !!!!!!!!!!!!self.list_ctrl_4.SetStringItem(pos,1,str(data['_data']['nombre']))! !!!!!!!!!!!!self.list_ctrl_4.SetStringItem(pos,2,str(data['_data']['precio']))! !!!!!!!!!!!!self.list_ctrl_4.SetStringItem(pos,3,str(data['_data']['disponible']))!
! ! ! Anexo&.Descripción&del&código! ! ! 116! !!!!!!!!if!self.editando:! !!!!!!!!!!!!#!actualizamos!la!lista!de!items!de!la!oferta! !!!!!!!!!!!!self.ofertaMod!=!servicio.getOferta(self.ofertaMod['nombre'])! !!!!!!!!!!!!self.list_ctrl_5.DeleteAllItems()! !!!!!!!!!!!!for!data!in!self.ofertaMod['items']:! !!!!!!!!!!!!!!!!pos!=! self.list_ctrl_5.InsertStringItem(0,str(self.searchNomItem(data['_data']['nombre'],servicio.It ems)))!#!id!diferente!explicar! !!!!!!!!!!!!!!!!self.list_ctrl_5.SetStringItem(pos,1,str(data['_data']['nombre']))! !!!!!!!!!!!!!!!!self.list_ctrl_5.SetStringItem(pos,2,str(data['_data']['precio']))! !!!!!!!!!!!!!!!!self.list_ctrl_5.SetStringItem(pos,3,str(data['_data']['disponible']))! !!!!!!!!else:! !!!!!!!!!!!!#!usar!las!variables!de!primeros!segundos!y!postres!para!rellenar!las!listas! actualizadas! !!!!!!!!!!!!self.list_ctrl_5.DeleteAllItems()! !!!!!!!!!!!!for!x!in!self.items:! !!!!!!!!!!!!!!!!data!=!self.searchItem(x,servicio.Items)! !!!!!!!!!!!!!!!!pos!=! self.list_ctrl_5.InsertStringItem(0,str(self.searchNomItem(data['_data']['nombre'],servicio.It ems)))!#!id!diferente!explicar! !!!!!!!!!!!!!!!!self.list_ctrl_5.SetStringItem(pos,1,str(data['_data']['nombre']))! !!!!!!!!!!!!!!!!self.list_ctrl_5.SetStringItem(pos,2,str(data['_data']['precio']))! !!!!!!!!!!!!!!!!self.list_ctrl_5.SetStringItem(pos,3,str(data['_data']['disponible']))! !!!!!!!!closed_window!=!event.EventObject! !!!!!!!!if!closed_window!==!self.crearItem:! !!!!!!!!!!!!self.crearItem!=!None! !!!!!!!!!!!!self.Show()! !!!!!!!!elif!closed_window!==!self:! !!!!!!!!!!!!print!'Carry!out!your!code!for!when!Main!window!closes'! !!!!!!!!event.Skip()! !! !!!!#!funcion!para!crear!el!context!menu!del!click!izquierdo! !!!!def!createMenu(self):! !!!!!!!!self.menu!=!wx.Menu()! !!!!!!!!item1!=!self.menu.Append(;1,'Editar')!!!!!!!!!!!#!añadimos!los!items! !!!!!!!!item2!=!self.menu.Append(;1,'Borrar')! !!!!!!!!self.Bind(wx.EVT_MENU,!self.editarItem,!item1)!!#!bindeamos!sus!acciones! !!!!!!!!self.Bind(wx.EVT_MENU,!self.borrarItem,!item2)! !! !!!!def!borrarItem(self,event):! !!!!!!!!print!'Borrando!item'! !!!!!!!!item!=!self.list_ctrl_4.GetItemText(self.list_ctrl_4.GetFocusedItem())! !!!!!!!!if!servicio.delItem(item):! !!!!!!!!!!!!msgbox!=!wx.MessageBox('!Item!borrado!',!'Información',!wx.ICON_INFORMATION!|! wx.STAY_ON_TOP)! !!!!!!!!!!!!self.list_ctrl_4.DeleteAllItems()!#!limpiamos!la!lista! !!!!!!!!!!!!self.comprobarItemsBorrado(item)!! !!!!!!!!!!!!servicio.updateItems()!!!!!!!!!!!!#!actualizamos!los!items!del!servidor! !!!!!!!!!!!!for!data!in!servicio.Items:!!!!!!!#!y!actualizamos!la!lista! !!!!!!!!!!!!!!!!pos!=!self.list_ctrl_4.InsertStringItem(0,str(data['_data']['id']))! !!!!!!!!!!!!!!!!self.list_ctrl_4.SetStringItem(pos,1,str(data['_data']['nombre']))! !!!!!!!!!!!!!!!!self.list_ctrl_4.SetStringItem(pos,2,str(data['_data']['precio']))! !!!!!!!!!!!!!!!!self.list_ctrl_4.SetStringItem(pos,3,str(data['_data']['disponible']))! !!!!!!!!else:!
!! 117! Diseño e implementación de un servidor web para mensajes publicitarios para redes ad hoc ! !!!!!!!!!!!!print!'ERROR!al!borrar!el!item'! !!!!!!!!!!!!msgbox!=!wx.MessageBox(u'¡No!se!pudo!borrar!el!item!',!u'Alerta',! wx.ICON_EXCLAMATION!|!wx.STAY_ON_TOP)! !!!!!!!!event.Skip()! !! !!!!#!muestra!el!la!ventana!de!editar!item!con!los!datos!del!item!que!estamos!editando! !!!!#!tambien!se!bindea!el!evento!para!que!se!actualicen!los!items!cuando!se!cierre!la!ventana! de!editar!item! !!!!def!editarItem(self,event):! !!!!!!!!print!'Editando!item'! !!!!!!!!item!=!self.list_ctrl_4.GetItemText(self.list_ctrl_4.GetFocusedItem())! !!!!!!!!crearItem!=!crear_item(self,item=item)! !!!!!!!!crearItem.Show()! !!!!!!!!self.Hide()! !!!!!!!!crearItem.Bind(wx.EVT_CLOSE,!self.on_close_crear_item)! !!!!!!!!self.crearItem!=!crearItem!#!comentar!esto!creo!que!sobra! !!!!!!!!event.Skip()! !! !!!!#!muestra!el!menu!del!click!derecho! !!!!def!showPopupMenu(self,event):! !!!!!!!!print!'Boton!derecho'! !!!!!!!!position!=!self.ScreenToClient(wx.GetMousePosition())! !!!!!!!!item!=!self.list_ctrl_4.HitTest(event.GetPosition())! !!!!!!!!if!item[0]!=;1:!#!para!que!solo!aparezca!el!menu!cuando!pincha!en!un!item! !!!!!!!!!!!!self.PopupMenu(self.menu,position)! !!!!!!!!event.Skip()! !! !!!!#!cuando!se!borra!un!item!actualiza!la!lista!de!items!el!item!borrado!de!ellas! !!!!def!comprobarItemsBorrado(self,itemBorrado):! !!!!!!!!count!=!self.list_ctrl_5.GetItemCount()! !!!!!!!!i=0! !!!!!!!!for!row!in!range(count):! !!!!!!!!!!!!item!=!self.list_ctrl_5.GetItem(itemId=row,!col=0)! !!!!!!!!!!!!if!item.GetText()!==!itemBorrado:! !!!!!!!!!!!!!!!!self.list_ctrl_5.DeleteItem(i)! !!!!!!!!!!!!!!!!self.primeros.pop(self.primeros.index(int(itemBorrado)))! !!!!!!!!!!!!!!!!break! !!!!!!!!!!!!i+=1! !! !!!!#!solo!deja!escribir!numeros! !!!!def!solo_num(self,!event):!!#!wxGlade:!crear_menu.<event_handler>! !!!!!!!!print!"solo_num"! !!!!!!!!raw_value!=!self.text_ctrl_5.GetValue().strip()! !!!!!!!!#!numeric!check! !!!!!!!!if!all(x!in!'0123456789'!for!x!in!raw_value):! !!!!!!!!!!!!pass! !!!!!!!!else:! !!!!!!!!!!!!self.text_ctrl_5.ChangeValue('')! !!!!!!!!event.Skip()! !! !!!!#!solo!deja!escribir!numeros!y!puntos! !!!!def!solo_num2(self,!event):!!#!wxGlade:!crear_menu.<event_handler>! !!!!!!!!print!"solo_num2"! !!!!!!!!raw_value!=!self.text_ctrl_6.GetValue().strip()!
! ! ! Anexo&.Descripción&del&código! ! ! 118! !!!!!!!!#!numeric!check! !!!!!!!!if!all(x!in!'0123456789.'!for!x!in!raw_value):! !!!!!!!!!!!!pass! !!!!!!!!else:! !!!!!!!!!!!!self.text_ctrl_6.ChangeValue('')! !!!!!!!!event.Skip()! !! !!!!def!activo(self,!event):!!#!wxGlade:!crear_oferta.<event_handler>! !!!!!!!!print!"Event!handler!'activo'!not!implemented!"! !!!!!!!!event.Skip()! !! !!!!def!load_img_oferta(self,!event):!!#!wxGlade:!crear_oferta.<event_handler>! !!!!!!!!print!"load_img"! !!!!!!!!img!=!self.img! !!!!!!!!openFileDialog!=!wx.FileDialog(self,!u"Selecionar!imagen!del!menu",!"",!"",!"pictures! (*.jpeg,*.jpg,*.png)|*.jpeg;*.jpg;*.png",!wx.FD_OPEN!|!wx.FD_FILE_MUST_EXIST)! !! !!!!!!!!if!openFileDialog.ShowModal()!==!wx.ID_CANCEL:! !!!!!!!!!!!!return!!!!!#!the!user!changed!idea...! !! !!!!!!!!print!openFileDialog.GetPath()! !!!!!!!!#!pasar!a!base64!y!guardar!en!variable! !!!!!!!!with!open(openFileDialog.GetPath(),!"rb")!as!imageFile:! !!!!!!!!!!!!img!=!base64.b64encode(imageFile.read())! !!!!!!!!if!img!=self.img:! !!!!!!!!!!!!msgbox!=!wx.MessageBox(u'!Imágen!guardada!',!u'Información',!wx.ICON_INFORMATION!|! wx.STAY_ON_TOP)! !!!!!!!!!!!!self.img!=!img! !!!!!!!!else:! !!!!!!!!!!!!msgbox!=!wx.MessageBox(u'¡La!imágen!no!se!guardo,!o!elegiste!la!misma!que!ya! estaba!guardada!',!u'Alerta',!wx.ICON_EXCLAMATION!|!wx.STAY_ON_TOP)! !!!!!!!!event.Skip()! !! !!!!def!guardar_oferta(self,!event):!!#!wxGlade:!crear_oferta.<event_handler>! !!!!!!!!print!"guardar_oferta"! !!!!!!!!#!comprobar!que!no!hay!campos!vacios! !!!!!!!!if!self.text_ctrl_10.GetValue()!=''!and!self.text_ctrl_5.GetValue()!=''!and! self.text_ctrl_6.GetValue()!=''!and!self.text_ctrl_descripcion.GetValue()!='':! !!!!!!!!!!!!#!generando!fechas!de!inicio!y!fin!del!menu!el!objeto!que!encapsula!el!sevicio!en! el!interfaz! !!!!!!!!!!!!#!recibe!las!fechas!en!formato!%Y;%m;%d!en!una!string! !!!!!!!!!!!!fechaini!=!self.calendar_ctrl_4.GetDate().FormatISODate()! !!!!!!!!!!!!fechaini2!=!datetime.datetime.strptime(fechaini,!"%Y;%m;%d")! !! !!!!!!!!!!!!fechafin!=!fechaini2!+!datetime.timedelta(!days=int(self.text_ctrl_5.GetValue())!)! !!!!!!!!!!!!fechafin!=!fechafin.date().isoformat()! !! !!!!!!!!!!!!if!self.editando!==!False:! !!!!!!!!!!!!!!!!#!crear!menu! !!!!!!!!!!!!!!!!if!servicio.createOferta(self.checkbox_3.GetValue(),! self.text_ctrl_6.GetValue(),!self.text_ctrl_10.GetValue(),! self.text_ctrl_descripcion.GetValue(),!fechaini,!fechafin,!self.img):! !!!!!!!!!!!!!!!!!!!!#!asignando!items! !!!!!!!!!!!!!!!!!!!!if!servicio.addItemOferta(self.items,!self.text_ctrl_10.GetValue()):!
!! 119! Diseño e implementación de un servidor web para mensajes publicitarios para redes ad hoc ! !!!!!!!!!!!!!!!!!!!!!!!!print!'Items!asignados'! !!!!!!!!!!!!!!!!!!!!!!!!msgbox!=!wx.MessageBox(u'!Oferta!creada!',!u'Información',! wx.ICON_INFORMATION!|!wx.STAY_ON_TOP)! !!!!!!!!!!!!!!!!!!!!!!!!self.Close(True)! !!!!!!!!!!!!!!!!!!!!else:! !!!!!!!!!!!!!!!!!!!!!!!!servicio.delOferta(self.text_ctrl_10.GetValue())! !!!!!!!!!!!!!!!!!!!!!!!!msgbox!=!wx.MessageBox(u'¡No!se!pudo!crear!la!oferta!',!u'Alerta',! wx.ICON_EXCLAMATION!|!wx.STAY_ON_TOP)! !!!!!!!!!!!!!!!!else:! !!!!!!!!!!!!!!!!!!!!msgbox!=!wx.MessageBox(u'¡No!se!pudo!crear!la!oferta,!cambie!el!nombre!',! u'Alerta',!wx.ICON_EXCLAMATION!|!wx.STAY_ON_TOP)! !!!!!!!!!!!!else:! !!!!!!!!!!!!!!!!#!editando!menu! !!!!!!!!!!!!!!!!if!servicio.updateOferta(self.checkbox_3.GetValue(),! self.text_ctrl_6.GetValue(),!self.text_ctrl_10.GetValue(),! self.text_ctrl_descripcion.GetValue(),!fechaini,!fechafin,!self.img):! !!!!!!!!!!!!!!!!!!!!#!asignando!items! !!!!!!!!!!!!!!!!!!!!if!servicio.addItemOferta(self.itemsModAdd,!self.text_ctrl_10.GetValue())! and!servicio.delItemOferta(self.itemsModDel,!self.text_ctrl_10.GetValue()):! !!!!!!!!!!!!!!!!!!!!!!!!print!'Items!asignados'! !!!!!!!!!!!!!!!!!!!!!!!!msgbox!=!wx.MessageBox(u'!Oferta!editado!',!u'Información',! wx.ICON_INFORMATION!|!wx.STAY_ON_TOP)! !!!!!!!!!!!!!!!!!!!!!!!!self.Close(True)! !!!!!!!!!!!!!!!!!!!!else:! !!!!!!!!!!!!!!!!!!!!!!!!servicio.delOferta(self.text_ctrl_10.GetValue())! !!!!!!!!!!!!!!!!!!!!!!!!msgbox!=!wx.MessageBox(u'¡No!se!pudo!editar!los!items!de!la!oferta!',! u'Alerta',!wx.ICON_EXCLAMATION!|!wx.STAY_ON_TOP)! !!!!!!!!!!!!!!!!else:! !!!!!!!!!!!!!!!!!!!!msgbox!=!wx.MessageBox(u'¡No!se!pudo!editar!la!oferta!',!u'Alerta',! wx.ICON_EXCLAMATION!|!wx.STAY_ON_TOP)! !!!!!!!!else:! !!!!!!!!!!!!msgbox!=!wx.MessageBox(u'¡Rellena!los!campos!',!u'Alerta',!wx.ICON_EXCLAMATION!|! wx.STAY_ON_TOP)! !!!!!!!!event.Skip()! !! !!!!def!item_selec2(self,!event):!!#!wxGlade:!crear_oferta.<event_handler>! !!!!!!!!print!"Event!handler!'item_selec2'!not!implemented!"! !!!!!!!!event.Skip()! !! !!!!def!pasar_izq(self,!event):!!#!wxGlade:!crear_oferta.<event_handler>! !!!!!!!!print!"pasar_izq"! !!!!!!!!#!comprobar!si!el!item!existe!en!la!lista!para!no!añadirlo!dos!veces! !!!!!!!!if!self.list_ctrl_4.GetFocusedItem()!=;1:! !!!!!!!!!!!!item!=! self.searchItem(self.list_ctrl_4.GetItemText(self.list_ctrl_4.GetFocusedItem()),servicio.Items )! !!!!!!!!!!!!try:! !!!!!!!!!!!!!!!!self.items.index(item['_data']['id'])! !!!!!!!!!!!!!!!!#!el!item!existe! !!!!!!!!!!!!except!Exception,!e:! !!!!!!!!!!!!!!!!#!el!item!no!esta!en!la!lista!primeros! !!!!!!!!!!!!!!!!if!self.list_ctrl_4.GetFirstSelected()!=;1:! !!!!!!!!!!!!!!!!!!!!pos!=!self.list_ctrl_5.InsertStringItem(0,str(item['_data']['id']))! !!!!!!!!!!!!!!!!!!!!self.list_ctrl_5.SetStringItem(pos,1,str(item['_data']['nombre']))!
! ! ! Anexo&.Descripción&del&código! ! ! 120! !!!!!!!!!!!!!!!!!!!!self.list_ctrl_5.SetStringItem(pos,2,str(item['_data']['precio']))! !!!!!!!!!!!!!!!!!!!!self.list_ctrl_5.SetStringItem(pos,3,str(item['_data']['disponible']))! !!!!!!!!!!!!!!!!!!!!self.items.append(item['_data']['id'])! !!!!!!!!!!!!!!!!!!!!if!self.editando!==!True:! !!!!!!!!!!!!!!!!!!!!!!!!try:! !!!!!!!!!!!!!!!!!!!!!!!!!!!! self.itemsModDel.pop(self.itemsModDel.index(item['_data']['nombre']))! !!!!!!!!!!!!!!!!!!!!!!!!!!!!#!si!se!habia!añadido!para!borrarse!lo!quitamos! !!!!!!!!!!!!!!!!!!!!!!!!except!Exception,!e:! !!!!!!!!!!!!!!!!!!!!!!!!!!!!#!en!caso!contrario!lo!añadimos!para!que!se!añada! !!!!!!!!!!!!!!!!!!!!!!!!!!!!self.itemsModAdd.append(item['_data']['id'])! !!!!!!!!event.Skip()! !! !!!!def!pasar_der(self,!event):!!#!wxGlade:!crear_oferta.<event_handler>! !!!!!!!!print!"pasar_der"! !!!!!!!!if!self.list_ctrl_5.GetFirstSelected()!=;1:! !!!!!!!!!!!!item!=!self.searchItem(!self.list_ctrl_5.GetItemText(! self.list_ctrl_5.GetFirstSelected()!),!servicio.Items!)! !!!!!!!!!!!!self.list_ctrl_5.DeleteItem(self.list_ctrl_5.GetFirstSelected())! !!!!!!!!!!!!self.items.pop(self.items.index(item['_data']['id']))! !!!!!!!!!!!!if!self.editando!==!True:! !!!!!!!!!!!!!!!!try:! !!!!!!!!!!!!!!!!!!!!self.itemsModAdd.pop(self.itemsModAdd.index(item['_data']['id']))! !!!!!!!!!!!!!!!!!!!!#!antes!de!añadirlo!a!la!lista!de!borrados!comprobamos!si!se!a!añadido! como!nuevo!item!en!al!modificacion! !!!!!!!!!!!!!!!!except!Exception,!e:! !!!!!!!!!!!!!!!!!!!!#!si!no!lo!esta!quiere!decir!que!se!esta!borrando!un!item!que!estaba!desde! el!principio!en!el!manu! !!!!!!!!!!!!!!!!!!!!self.itemsModDel.append(item['_data']['nombre'])! !!!!!!!!else:! !!!!!!!!!!!!try:! !!!!!!!!!!!!!!!!self.items.pop(self.items.index(int(self.list_ctrl_5.GetItem(itemId=0,! col=0).GetText())))! !!!!!!!!!!!!!!!!if!self.editando!==!True:! !!!!!!!!!!!!!!!!!!!!try:! !!!!!!!!!!!!!!!!!!!!!!!! self.itemsModAdd.pop(self.itemsModAdd.index(str(self.list_ctrl_5.GetItem(itemId=0,! col=1).GetText())))! !!!!!!!!!!!!!!!!!!!!except!Exception,!e:! !!!!!!!!!!!!!!!!!!!!!!!!self.itemsModDel.append(str(self.list_ctrl_5.GetItem(itemId=0,! col=1).GetText()))! !!!!!!!!!!!!!!!!self.list_ctrl_5.DeleteItem(0)! !!!!!!!!!!!!except!Exception,!e:! !!!!!!!!!!!!!!!!pass! !!!!!!!!event.Skip()! !! !! !!!!def!item_select(self,!event):!!#!wxGlade:!crear_oferta.<event_handler>! !!!!!!!!print!"Event!handler!'item_select'!not!implemented!"! !!!!!!!!event.Skip()! !! #!end!of!class!crear_oferta! !! class!InterfazServidor(wx.App):!
!! 121! Diseño e implementación de un servidor web para mensajes publicitarios para redes ad hoc ! !!!!def!OnInit(self):! !!!!!!!!wx.InitAllImageHandlers()! !!!!!!!!main!=!MyFrame(None,!wx.ID_ANY,!"")! !!!!!!!!self.SetTopWindow(main)! !!!!!!!!main.Show()! !!!!!!!!return!1! !! #!end!of!class!InterfazServidor! !! if!__name__!==!"__main__":! !!!!try:! !!!!!!!!servicio!=!ServicioPyro()! !!!!!!!!servicio.isOnline()! !!!!except!Exception,!e:! !!!!!!!!print!'No!se!encontro!servidor!en!localhost'! !! !!!!gettext.install("Servidor")!#!replace!with!the!appropriate!catalog!name! !! !!!!Servidor!=!InterfazServidor(0)! !!!!Servidor.MainLoop()! ! ServicioPyro.py" #!;*;!coding:!UTF;8!;*;! #!clase!proveedora!de!contenidos!a!la!interfaz! !! import!Pyro4! !! import!time! from!datetime!import!date! import!datetime!as!datetime! !! import!base64! import!serpent! from!setting!import!*! !! class!ServicioPyro(object):! !"""docstring!for!ServicioPyro"""! !def!__init__(self,!direccion=DIRECCION_PYRO,!objeto=OBJETO_PYRO,!key=KEY):! ! ! super(ServicioPyro,!self).__init__()! ! ! #variables! ! ! self.Menus!=![]! ! ! self.Ofertas!=![]! ! ! self.Items!=![]! ! ! self.Online!=!False! ! ! self.servicio!=!0! ! ! self.dir!=!direccion! ! ! self.key!=!key! ! ! self.hoy!=!date.today()! ! ! #fin!de!variables! ! ! try:! ! ! ! print!'Conectando!...'! ! ! ! Pyro4.config.HMAC_KEY=key! ! ! ! self.servicio!=!Pyro4.Proxy('PYRONAME:'+objeto+direccion)!