Equation Chapter 1 Section 1 Trabajo Fin de Grado Grado en Ingeniería de las Tecnologías de Telecomunicación Herramienta de gestión y análisis para redes WiFi en entornos Big Data Autor: Julio José García Pérez Tutor: Pablo Nebrera Herrera Departamento de Ingeniería Telemática Escuela Técnica Superior de Ingeniería Universidad de Sevilla Sevilla, 2015
2
Trabajo Fin de Grado Grado en Ingeniería de las Tecnologías de Telecomunicación Herramienta de gestión y análisis para redes WiFi en entornos Big Data Autor: Julio José García Pérez Tutor: Pablo Nebrera Herrera Profesor asociado Departamento de Ingeniería Telemática Escuela Técnica Superior de Ingeniería Universidad de Sevilla Sevilla, 2015
4
Trabajo Fin de Grado: Herramienta de gestión y análisis para redes WiFi en entornos Big Data Autor: Julio José García Pérez Tutor: Pablo Nebrera Herrera 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
6 El Secretario del Tribunal
A mi hermano
8
Agradecimientos. Este proyecto pone fin a mis estudios universitarios en Ingeniería de Telecomunicaciones, una etapa de mi vida inolvidable en la que he vivido momentos muy duros pero que hoy me permite sentir una enorme satisfacción personal. Quisiera agradecer en primer lugar a mis padres por brindarme la oportunidad de estudiar en la Escuela Superior de Ingeniería de Sevilla, así como por su apoyo incondicional. Agradecer también a todo el equipo de redBorder. En especial a José Antonio, Ángel, Carlos y Clara, por su ayuda y sobre todo por su paciencia estos últimos meses. A mi tutor, Pablo Nebrera, por permitirme realizar un interesante proyecto muy ligado al mundo profesional. Por último, quisiera dar las gracias a mis compañeros de clase, sin ellos difícilmente hubiera llegado hasta aquí. Julio José García Pérez Sevilla, 2015
16
1.INTRODUCCIÓN 1.1..Motivación oy en día, resulta extraño conocer a alguien que no tenga en su bolsillo un dispositivo con el que acceder a internet. En España, el acceso a través de la red móvil es cada vez más común. Sin embargo, la existente limitación en el consumo de datos por parte de las principales operadoras provoca que el usuario se sienta más cómodo navegando en una red WiFi. Así, resulta sencillo encontrar acceso a internet inalámbrico a coste cero en hoteles, centros comerciales, congresos, edificios públicos, etc. De este modo, si tenemos en cuenta la evidente necesidad por compartir y comunicar de la que sufre la gran masa social, podemos entender el interés por controlar y gestionar una red WiFi, tanto desde el punto de vista técnico como analítico. 1.2..Objetivos El proyecto cuenta con dos bloques, uno dedicado a la gestión de red y otro a la analítica de los datos obtenidos. Se desea desarrollar una herramienta en forma de aplicación Web fácilmente manejable y que implemente las últimas tecnología de Software Libre existentes para desarrollo Web en equipos de escritorio y dispositivos móviles. El objetivo último es implementar la herramienta en la aplicación de redBorder aprovechando su consistente sistema Big Data. 1.3..Metodología En primer lugar, haremos un estudio del protocolo SNMP en su versión 2C para Wireless Lan Controllers de Cisco, centrándonos en su escalabilidad para poder escanear el gran número de puntos de acceso WiFi con los que trabaja redBorder. Se buscará desarrollar un software de monitorización en lenguaje Ruby para finalmente incluirlo como un demonio que actualice la Base de Datos de una aplicación Ruby on Rails. Una vez obtenida la información, pasaremos a representarla sobre una aplicación Web Ruby on Rails que permita gestionar una gran cantidad de Puntos de Acceso, realizar filtros, localizarlos sobre un mapa o plano, etc. Por último, usaremos el sistema de Big Data de redBorder para llevar a la interfaz web gráficos y mapas de calor que permitan conocer y estudiar el movimiento de masas de dispositivos conectados en un intervalo de tiempo. H Si el usuario no puede encontrarlo, es como si no existiera - HFi Button -
18 2.TECNOLOGÍAS 2.1..Ruby on Rails Ruby on Rails [1], más conocido como Rails, es un entorno de desarrollo web de código abierto programado en lenguaje Ruby. Sigue el Modelo Vista Controlador (MVC), ofreciendo consistentes mecanismos para manejo de Base de Datos, Servicios Web y Páginas Web. Rails refuerza y facilita el uso de estándares como JSON y XML para transferencia de datos, así como HTML, CSS y Javascript para interfaz de usuario. Además, cuenta con un conjunto de librerías, automatismos y convenciones destinados a resolver los problemas más comunes a la hora de desarrollar una aplicación web, para que el programador pueda centrarse en los aspectos únicos y diferenciales de su proyecto en lugar de los problemas recurrentes. El framework se utiliza para construir aplicaciones modernas de internet de la talla de Twitter, Scrbd, Basecamp, Github, etc. Figura 1- Estructura Aplicación Ruby on Rails It has become appallingly obvious that our technology has exceded our humanity. - Albert Einstein -
2.1.1.Historia Rails fue creado en 2003 por David Heinemeier Hansson como parte de su proyecto en Basecamp. Desde entonces, ha sido extendido por el Rails Score Team, con más de 3700 colaboradores y soportado por una extensa y activa comunidad. 2.1.2.Modelo Vista Controlador Como muchos entornos de desarrollo Web, Rails funciona bajo el paradigma MVC, separando claramente las diferentes partes que forman la aplicación. En una aplicación Ruby on Rails, un modelo se traduce en una tabla en Base de Datos y su correspondiente clase Ruby. Como ejemplo más común, la clase Usuario será definida en el fichero usuario.rb y enlazada con la tabla usuarios en Base de Datos. Los controladores son componentes locales en Rails que responden a peticiones externas del servidor Web a la aplicación. Contiene el código Ruby que debe procesar la petición y determina qué vista se debe renderizar, compartiendo variables que permitan a las vistas acceder a información de los modelos. Rails facilita a los desarrolladores usar rutas REST (Transferencia de Estado Representacional) y acciones que gestionen las operaciones básicas en base de datos CRUD (Crear, Obtener, Actualizar y Eliminar). Los mapeos entre qué controladores responden a peticiones a rutas determinadas se configuran en los ficheros routes.rb. 2.2..Simple Network Management Protocol (SNMP) Simple Network Management Protocol (SNMP) [2] es un protocolo para gestión y monitorización de equipos conectados a una red IP. Estos dispositivos pueden ser routers, puentes, servidores, impresoras, etc. 2.2.1. Conceptos básicos SNMP está compuesto por uno o más equipos llamados gestores, cuya tarea es monitorizar y/o gestionar un conjunto de dispositivos conectados a la red, que incorporan software conocidos como agentes. Los agentes SNMP exportan o importan datos y configuraciones del propio dispositivo como variables. Estas variables están organizadas en MIBs. 2.2.2..Management Information Base (MIB) Una MIB es una colección de información organizada jerárquicamente. En SNMP, las MIBs contienen definiciones de los objetos que se desean gestionar. Cada objeto se identifica con una OID a la que se puede acceder vía SNMP. Los objetos en las MIBs se describen utilizando la notación definida por SMI, un extracto de ASN.1. 2.2.3..Detalles del protocolo SNMP funciona en la capa de aplicación del modelo OSI. Los agentes y gestores se comunican usando el protocolo 161 UDP. También existen notificaciones (Traps e InformRequests) que el gestor recibe en el puerto 162 UDP. La PDU de SNMP se construye de la siguiente forma: Cabecera IP Cabecera UDP Versión Comunidad Tipo PDU ID de la petición Estado de error Índice de error Enlaces de variables
20 2.2.4..Mensajes SNMP Existen siete tipos de PDU: •!GetRequest: Petición del gestor al agente para recibir el valor de un objeto o un conjunto de objetos •!SetRequest: Petición del gestor al agente para cambiar el valor de algún objeto o de un conjunto de objetos. •!GetNextRequest: Petición del gestor al agente para descubrir las variables disponibles y sus valores. Se recorre toda la MIB a partir del OID especificado. •!GetBulkRequest: Versión optimizada del mensaje GetNextRequest. Petición del gestor al agente solicitando multiples iteraciones de GetNextRequest. •!Response: Mensaje de respuesta del agente al gestor devolviendo variables y ACKs a los mensajes GetRequest, SetRequest, GetNextRequest y GetBulkRequest. •!Trap: Notificación asíncrona del agente al gestor. Permiten conocer eventos determinados sin necesidar de realizar una petición. •!InformRequest: Mensaje asíncrono con asentimiento de gestor a gestor. 2.2.5 SNMP4EM SNMP Library for EventMachine [3] (SNMP4EM) es una gema de Ruby que extiende Ruby-SNMP para combinarlo con la libraría EventMachine consiguiendo mayor rendimiento y escalabilidad. Permite realizar programas para acceder a un gran número de dispositivos. 2.2.5.1..Características Principales •!SNMP v1 y v2 •!Peticiones SNMP Get, GetNext, Set y Walk. GetBulk y WalkBulk para clientes SNMPv2. •!Capacidad de realizar peticiones de múltiples OIDs en paralelo. 2.2.5.2..Aplicación en el sistema Este proyecto pretende utilizar el protocolo SNMP en su versión 2c para obtener datos relevantes de cada uno de los puntos de acceso marca CISCO haciendo peticiones al Wireless LAN Controller que los gestiona. Estos datos de interés son: •!Nombre. •!Dirección MAC. •!Número de clientes conectados. •!Tiempo en funcionamiento. Así mismo, puesto que el objetivo principal es la escalabilidad del programa, realizaremos peticiones del tipo GetBulkRequest para los OIDs necesarios.
2.3..Google Maps JavaScript API v3 La API de los mapas de Google en su tercera versión [4], supone una potente herramienta para trabajar con mapas en una aplicación Web, ofreciendo numerosos servicios y utilidades para visualización de datos, direccionamiento, etc. 2.3.1..Heatmap.js Heatmap.js [7] es una librería Javascript que facilita la visualización de datos en tres dimensiones. Con menos de 3kB gzip, presume de ser la librería de visualización de mapas de calor más avanzada de la web. Su versión 2.0 destaca: •!Su rapidez, con un nuevo modulo de renderizado •!Más consistente, por manejar aún más puntos de información (40.000+) •!Extensible La librería permite crear mapas de calor sobre Google Maps gracias al plugin gmaps-heatmap.js Figura 2 - Google Maps Heatmaps Plugin 2.3.2..Aplicación en el Sistema La aplicación a desarrollar pretende incluir un sistema de análisis para la gran cantidad de información que redBorder consigue obtener de la red. Para estudiar el comportamiento de una gran masa de puntos geolocalizados, los mapas de calor son la herramienta perfecta. Además, la librería heatmap.js facilita el desarrollo y permite escalar hasta una gran cantidad de puntos simultaneos. 2.4..Druid Druid [9] es un sistema de base de datos distribuida de código abierto. Está diseñado para manejar rápidamente inmensas cantidades de eventos en tiempo real. Druid funciona como un cluster de nodos especializados que interactúan entre ellos. El cluster incluye depenedencias a servicios externos para coordinación (Apache Zookeeper), almacenamiento de metadatos (Mysql), y almacenamiento profundo (HDFS, S3, Apache Cassandra, etc.).
22 2.5..redBorder redBorder [5] es producto desarrollado por la empresa ENEO Tecnología S.L. Supone todo un ecosistema de soluciones de seguridad informática basado en análisis Big Data y extensible a entornos Cloud. Incluye múltiples aplicaciones y servicicios como son redBorder IPS, redBorder FLOW, redBorder Malware o redBorder Social. Todos ellos se le ofrecidos al usuario sobre una potente (a la vez que sencilla) interfaz web. 2.5.1..redBorder FLOW redBorder FLOW es el módulo para análisis y visualización de datos obtenidos de la red. Permite aplicar al tráfico numerosos filtros presentando la información sobre diferentes tipos de gráficos y mapas. Figura 3 - Arquitectura redBorder FLOW
3.MODELOS 3.1..Introducción Nuestro proyecto gestiona principalmente cuatro entidades del sistema redBorder como modelos de Ruby on Rails: Sensor, OverlayMap, AccessPoint y GpsMarker. En este apartado estudiaremos en el diseño de los mismos y cómo se relacionan entre sí. 3.2..Sensor.rb 3.2.1..Introducción Los sensores son máquinas o puntos finales de la API que, organizados jerárquicamente, gestionan la información usada en la plataforma. El modelo Sensor pertenece al sistema redBorder y es ampliamente utilizado en toda la plataforma. Para este proyecto se ha utilizado una parte del mismo y se ha extendido para implementar las funcionalidades propuestas. En este apartado estudiaremos únicamente los aspectos de interés para nuestra herramienta de gestión y análisis. 3.2.1.1..Tipo Dominio En redBorder los sensores de tipo dominio son semejantes a directorios en un Sistema Operativo, permiten agrupar otros sensores para así restringir el ámbito de operación de un usuario en el sistema. A su vez, un sensor de tipo dominio puede tener diferentes subtipos: •!Genérico •!Organización •!Campus •!Edificio •!Plano 3.2.1.2..Tipo Flow Los sensores de este tipo representan equipos de red que podemos conectar al sistema redBorder configurando su dirección IP, comunidad SNMP, localización, etc. Para este proyecto los sensores Flow serán principalmente Wireless LAN Controllers de CISCO a los que irán conectados los Puntos de Acceso WiFi. La información es el petróleo del siglo 21 Y la analítica es la combustión - Peter Sondergaard -
24 3.2.2..Atributos del modelo Como modelo de Ruby on Rails, la entidad ‘Sensor’ supondrá una tabla en la Base de Datos así como una clase en nuestra aplicación. A continuación estudiaremos los diferentes campos de interés que tiene la tabla Sensor: Atributo Tipo Descripción Id Integer Identificador único del sensor Name String Nombre asignado al sensor y que se le mostrará al usuario en la aplicación. Parent_id Integer Referencia externa al sensor de tipo dominio inmediatamente superior en el sistema jerárquico Latitude String Latitud obtenida o asignada para el sensor Longitude String Longitud obtenida o asignada para el sensor Type Integer Tipo de sensor (Dominio/Flow) Domain_type Integer Tipo de Dominio Lft Integer Campo para el algoritmo de jerarquía Rgt Integer Campo para el algoritmo de jerarquía Ip String Dirección IP del sensor Property Text Contiene información adicional en formato JSON para sensores de tipo ‘plano’ 3.2.2.1..Property El campo property contiene información adicional en formato JSON sobre sensores de tipo ‘plano’. Este tipo de sensor tiene especial interés en el proyecto puesto que pretendemos localizar Puntos de Acceso WiFi sobre el plano de una habitación y establecer relaciones entre su posición en el plano y la posición real del dispositivo. Para ello, estudiaremos a continuación los diferentes campos dentro de property: 3.2.2.1.1..Dimensiones Anchura y altura del plano en píxeles.
3.2.2.1.2..Coordenadas En coordenadas encontramos los puntos floor_ne y floor_sw que almacenan los puntos Latitud, Longitud de las esquinas superior derecha e inferior izquierda del plano. Así mismo, los campos rotation, matrix_trans y matrix_offset contienen el resultado de un algoritmo matemático desarrollado para traducir fácilmente los puntos XY de la pantalla a coordenadas geográficas y viceversa. Para ello necesitaremos del modelo GpsMarker, que estudiaremos en el punto 3.5, donde desarrollaremos este proceso matemático y su implementación en el sistema. 3.2.2.1.3..Nivel Nivel del plano dentro del edificio, orientado al desarrollo de los mapas en 3D que veremos en el punto 5.7. 3.3.3..Jerarquía Los sensores están organizados jerárquicamente dentro del sistema. De este modo, podemos distinguir entre ancestros o descendientes con el objetivo último de realizar eficientes consultas a la Base de Datos para obtener información de interés. 3.3.3.1..NestedSet El modelo NestedSet es una técnica particular para representar entidades organizadas jerárquicamente en bases de datos relacionales. Permite resolver el problema por el cual el álgebra relacional y las operaciones SQL basadas en él son incapaces de expresar todas las operaciones sobre jeraquías directamente, presentando un alto coste computacional. El algoritmo asigna dos números a cada nodo del sistema de a cuerdo a una estructura en forma de árbol y los almacena como atributos. De este modo las consultas se simplifican ya que solamente necesitamos comparar estos dos números para conocer hijos y ancestros del nodo. Por otro lado, cada vez que se añade o elimina un nodo se requiere un proceso de renumeración computacionalmente costoso. Podemos entender el algoritmo atendiendo al siguiente ejemplo: Dadas las entidades Universidad, Departamento, Asignatura y Profesor utilizaremos NestedSet para organizar y realizar consultas sobre el siguiente sistema en forma de árbol:
32 conectado a cada AP cLApUpTime ! 1.3.6.1.4.1.9.9.513.1.1.1.1.6 Lectura Este objeto representa el tiempo en centenas de segundos desde que el AP fue reiniciado por última vez. 4.4..Punto de partida El script inicial incluido en redBorder utilizaba la gema Ruby-SNMP y el mensaje Get-Next Request para obtener información de los APs. El programa resultó no ser nada escalable presentando unos tiempos de ejecución demasiado altos conforme aumentábamos el número de APs. La ejecución del mismo en nuestro entorno de pruebas presentaba el siguiente resultado: 1 $1rb_scan_wlc.rb1–h18.25.222.801–c1public1 RESULT:1 Access1Points:11721 Clients:14181 Time:1258.871seconds1 1 4.5..Solución La solución hallada para el enorme problema de escalabilidad que presenta el script inicial consiste en utilizar la gema SNMP4EM estudiada anteriormente y el mensaje BulkWalk Request, consiguiendo el siguiente resultado: ! $!rb_scan_wlc_v2.rb!–h!8.25.222.80!–c!public! RESULT:! Access!Points:!172! Clients:!435! Time:!9.52!seconds! ! Esta nueva versión del programa permitirá utilizar eficientemente el protocolo SNMP, resultando ser altamente escalable. El tiempo de ejecución se mantiene constante al aumentar considerablemente el número de APs, llegando a conseguir escanear más de 1500 dispositivos en menos de 20 segundos.
4.6..CheckAccessPoint Conseguida una solución escalable, pasaremos a adaptar el script para usarse como proceso automático en el sistema redBorder. El programa debe recorrer los diferentes sensores de tipo Flow que presentan configuración SNMP y realizar la petición, guardando en la base de datos los nuevos APs descubiertos, o actualizando los datos de los ya existentes. Los Jobs en redBorder se programan para ejecutarse en segundo plano definiendo el tiempo entre cada ejecución. En particular, el job que escanea los APs se ejecutará cada 5 minutos. En el Anexo A podemos ver el código Ruby del programa. A continuación veremos un diagrama que representa la lógica que sigue dicho programa: Figura 5 - Diagrama CheckAccessPoint
34 5.VISTA ACCESS POINTS 5.1..Introducción En este apartado estudiaremos el diseño de una interfaz gráfica para la gestión de APs dentro de la aplicación Web de redBorder. La herramienta será el punto de encuentro entre el sistema (empresa) y el usuario gestor de la red (cliente) por lo que nos centraremos en cumplir objetivos propios del modelo de User Experience (UX) tales como arquitectura de la información, diseño visual, usabilidad, funcionalidad, tipografía, interfaz, contenido, etc. 5.2..Árbol de sensores El árbol de sensores, conocido en la aplicación como tree, es el punto de partida para acceder a la vista de APs. En él se listan los sensores a los que el usuario tiene acceso, organizados jerárquicamente y presentados de forma similar al explorador de archivos de un Sistema Operativo común. Figura 6 - Árbol de sensores Design is not just what it looks like and feels like. Design i show it works - Steve Jobs -
En la figura vemos cómo se listan los sensores de nuestro escenario de pruebas (4.2). Encontramos el sensor Flow WLC_Hyatt y el sensor Campus SantaClara Convention donde se encuentra el Edificio Hyatt con catorce Planos en él. Además, la tabla muestra un resumen con las principales características de cada sensor, como número de APs, última vez que fue escaneado y dirección IP. 5.2.1..Lógica A continuación vemos como se ha desarrollado esta sección en Ruby on Rails siguiendo el esquema MVC: Figura 7 - MVC árbol de sensores 5.3..Tabla de Puntos de Acceso Desde el árbol de sensores podemos acceder a la vista propia de APs. Un elemento importante de la misma es la tabla que muestra información de los puntos de acceso conectados al sensor, bien porque se han escaneado utilizando CheckAccessPoint en el caso de ser un WLC o porque se han asignado a un sensor plano hijo. En la Figura 8 podemos ve run ejemplo de la misma para nuestro entorno de pruebas: Figura 8 - Tabla de Puntos de Acceso WiFi
36 5.3.1..Filtros Algo importante a la hora de implementar una interfaz de gestión para un alto número de puntos de acceso son los filtros y el buscador. Gracias a ello podemos acceder rápidamente al AP deseado o encontrar aquellos que cumplan una determinada condición: •!Up: APs que CheckAccessPoint ha escaneado y se encuentran activos. •!Down: APs ya existentes no encontrados en el último escaneo, se consideran apagados. •!Unknown: APs que nunca han sido detectados por CheckAccessPoints. •!Unallocated: APs que no han sido colocados sobre el plano y/o mapa. Con respecto a la lógica que siguen los filtros, cada uno es una enlace a una ruta que gestiona un controlador. El controlador correspondiente consultará al modelo AccessPoint utilizando el selector SQL necesario y la respuesta vendrá dada en código Javascript de forma que mantenga en la tabla los APs encontrados (identificándolos por un atributo id de HTML5) y oculte el resto. De igual forma, el buscador es un filtro por nombre o dirección MAC que mostrará las filas de la tabla que coincidan con la búsqueda. 5.3.2..Asignar un Plano Recordemos que un AP puede pertenecer a un sensor de tipo Flow, a uno de tipo Plano o a ambos a la vez. De esta forma, una operación frecuente en nuestra interfaz podría ser asignar un conjunto de APs pertenecientes a un sensor Flow a uno de tipo Plano. Figura 9 - Asignar AP a Plano En la figura vemos como es posible seleccionar un conjunto de filas, momento en el que aparece el botón Assign floor, el cual nos muestra un listado de sensores de tipo plano donde podemos seleccionar el deseado. Como ya hemos visto en otras operaciones en Rails, Assign floor será un enlace a una ruta, nuestro controlador consultará al modelo en busca de un listado de sensores de tipo Plano, que instanciará y lo ofrecerá a la respuesta JavaScript, encargada de mostrar el selector renderizando el formulario. De igual forma, el evento “click en Assign” será atendido por una función JavaScript que añadirá al formulario una tabla con los ids de los APs seleccionados antes de enviar el formulario. El controlador que gestiona el envio de este formulario hará una actualización en el modelo de todos los APs incluidos asignándoles el sensor_id elegido. 5.3.3..Edición Otra de las acciones posibles en esta vista es la edición de un AP haciendo click en el icono correspondiente. En la figura 10 podemos ver la ventana modal emergente:
Figura 10 - Edición AP Llegados a este punto podemos ver un proceso común en toda nuestra herramienta, los formularios de edición en forma de ventana modal. Figura 11 - MVC Ventana modal
38 5.3.4..Borrado También se le ofrece al administrador la opción de eliminar un punto de acceso del sistema. Como en las acciones anteriores, el método destroy controlador atenderá la petición HTTP DELETE a access_points/destroy, recibiendo la id del AP correspondiente y eliminándolo de la Base de Datos. La respuesta Javascript del mismo eliminaría la fila de la tabla. 5.4..Tiles En la herramienta llamamos Tiles a una de las vistas dentro del apartado de Puntos de Acceso. Tiles nos muestra los sensores descendientes del sensor en el que nos encontramos permitiéndonos movernos por todo el árbol de sensores. Figura 12 - Vista Tiles En la figura 12 vemos un ejemplo de la vista Tiles accediendo desde el sensor de tipo Edificio Hyatt en nuestro escenario. Cada cuadro nos muestra un resumen de cada sensor hijo: número de APs y su estado, número de sensores hijos, número de dominios hijos y número de planos hijos. Así mismo, cada cuadro toma como imagen la del (o los) OverlayMap(s) asociados a sensores hijos. Figura 13 - MVC Vista Tiles
5.5..Mapa El mapa nos permite situar los APs vinculados al sensor en las coordenadas reales en las que se encuentran situados. Se ha desarrollado en JavaScript haciendo uso de la librería de GoogleMaps estudiada. Además, hacer click sobre un AP en el mapa nos permite filtrarlo en la tabla y nos muestra un cuadro con información relevante. Además, se mantiene el código de colores usado en los filtros de forma que se le permite al usuario identificar rápidamente un AP de interés. En la figura 14 vemos un ejemplo accediendo desde el sensor de tipo plano Lobby, la recepción del hotel, donde vemos se encuentran localizados 20 APs, 2 de ellos en estado Down. Figura 14 - Vista Mapa de APs Además, se ha incluido la librería MarkClusterPlus.js que nos permite agrupar los marcadores (en este caso APs) cuando alcanzamos un determinado zoom: Figura 15 - MarkClusterPlus
40 5.5.1..Lógica El mapa se construye utilizando la librería redBorderMaps.js, incluida en el Anexo B. Esta librería extiende GoogleMapsAPI.js para su uso en el entorno de redBorder. Incluimos los APs utilizando el método addMarker, el cual recibe las siguientes opciones: •!Content: contenido de la caja de texto asociada al marcador, HTML indicando nombre, MAC, latitud y longitud del AP. •!filterWhenClick: verdadero/falso. Indica si se debe filtrar en la tabla al hacer click sobre el marcador. •!markerId: id del marcador. En nuestro caso, id propio del AP en el modelo AccessPoint. •!Draggable: verdadero/falso. Indica si se permite arrastrar el marcador o no •!draggEndCallback: función a ejecutar cuando se arrastra un marcaor. Esta función se encargará de realizar la petición Ajax que actualice las columnas Latitud, Longitud, X e Y para el AP que se ha movido. •!Icon: icono asociado al marcador. Hemos creado un icono circular cuyo color dependerá del estado del AP (rojo, verde, naranja). De este modo, el siguiente diagrama representa la lógica que sigue el proceso de dibujar los puntos de acceso WiFi en el mapa: Figura 16 - Lógica APs en mapa
5.5.2..Opciones Al hacer click con el botón derecho del raton sobre el mapa podemos acceder a un conjunto de opciones, entre las que destaca la creación o localización de un AP en el punto donde se ha hecho click: Figura 17 - Opciones sobre AP en mapa 5.5.2.1..Creación Figura 18 - Creación AP La creación de un AP permite añadirlo al sistema sin necesidad de que CheckAccessPoint lo detecte. Como estamos creándolo desde el mapa asociado al sensor de tipo Campus ‘SantaClara Convention’, tendremos que asociarlo a un sensor Floor (Plano) y/o uno Flow. Con respecto al selector de plano, deberemos ofrecer al usuario un listado con todos los sensores Plano descendientes del sensor en el que nos encontremos y que además contengan al punto donde hemos hecho click. Para ello, se ha desarrollado el método contains? para el modelo Sensor. Este método recibe un punto latitud,longitud y devuelve verdadero/falso según si el plano contiene al punto o no. Para ello utiliza los puntos Nordeste y Sudoeste del plano, así como su ángulo de rotación.
48 6.3..Escenario de pruebas Esta funcionalidad se desarrollo durante el evento Mobile Wolrd Congress de Barcelona, donde redBorder fue uno de los sistemas de monitorización y análisis de red. Como resultado pudimos estudiar la evolución del número de dispositivos WiFi situados en cada punto del evento a lo largo de un día. Una herramienta de estas características permite estudiar patrones de comportamiento para actuar en consecuencia. Algunos ejemplos podrían ser: reforzar las medidas de seguridad en un área en un instante determinado, orientar una campaña de publicida, etc. Figura 28 – Clientes en MWC, 03:05 Figura 29 - Clintes en MWC, 07:50 Figura 30 - Clientes en MWC, 14:20
7.LOCATION 7.1..Motivación Otra herramienta de analítica desarrollada en el proyecto es el módulo Location. Redborder Flow consigue localizar un dispositivo (cliente) y registrar en el sistema de BigData su dirección MAC en un determinado lugar en un instante de tiempo. Además, gracias a que los diferentes sensores y APs se encuentran organizados en zonas, campus, edificios o plantas, podemos obtener el flujo de clientes entre distintos lugares en un intervalo de tiempo y presentarlo al usuario para su posterior análisis. 7.2..D3 Chords La forma idónea de presentar al usuario esta información es mediante gráficos Chords de D3.js [8], que representan relaciones entre distintas entidades. Para implementar estos diagramas se ha desarrollado las librerías Javascript sensor_location.js sensor_location.js i chords.js que utiliza d3.js. Figura 31 - Ejemplo gráfico Chord In God we trust. all others must bring data - W. Edwards Deming -
50 7.3..Lógica Siguiendo el Modelo Vista Controlador, una petición a /location será gestionada por el controlador , que hará peticiones a Druid para conseguir una matriz de datos y enviarla a chords.js para representar los gráficos. Figura 32 - MVC Location “Data” será un JSON a partir del cual podremos formar una matriz para cada gráfico (Zonas, Edificios, Campus, Plantas). Para un entorno en el que existieran las zonas A,B,C,D la matriz Zonas tendría la siguiente estructura: ! → ! ! → & & → ! & → & ! → ( ! → g & → ( & → g ( → ! ( → & g → ! g → & ( → ( ( → g g → ( g → g Donde cada columna y fila corresponden a una zona y cada posición representa el número de clientes que se han desplazado de una zona a otra en el intervalo indicado. El código dedicado a procesar la matriz de datos y construir los gráficos se encuentra en el Anexo C.
7.4..Pruebas A continuación podemos ver algunas imágenes que muestran el resultado en nuestro entorno de pruebas en Bay Area, EEUU. Figura 33 - Ejemplo módulo Location
52 8.PRESUPUESTO En esta sección estiramos el coste del proyecto, basándonos en las horas dedicadas y el equipamento necesario para instalar todo el sistema: Presupuesto de desarrollo ( Horas de trabajo/euros ) Periodo de trabajo Número de horas Precio/Hora Total Septiembre 2014 – Agosto 2015 613 horas 30€/hora 18.390€ Presupuesto por equipamiento para desarrollo Equipamiento Cantidad Precio/Hora Total Anfitrión para VMware ESXi (Entorno de virtualización de servidores). 1 5.000€ 5.000€ En el anfitrión se virtualizan servidores que soportan los distintos servicios involucrados en el proyecto, estos servidores tienen las siguientes características: •!Memoria: 15 Gigas de RAM. •!4 CPUs: modelo “Intel(R) Xeon(R) CPU E5-1650 0 @ 3.20GHz”. •!Sistema redBorder, basado en CentOs. Para llevar acabo este proyecto se ha calculado un presupuesto total de 23.390 €.
9.CONCLUSIONES 9.1. Conclusiones Por último, concluimos el proyecto habiendo desarrollado una herramienta estable y funcional que poder incluir en el sistema redBorder. Este documento recoge el resultado de un largo proceso de investigación, aprendizaje, superación personal y principalmente líneas y horas de código. Hemos trabajado con tecnologías punteras de código abierto para el desarrollo de aplicaciones Web como lo son Ruby on Rails, HTML5, CSS3, jQuery, D3.js, etc. Por otro lado, al haber trabajado con el apoyo de la empresa ENEO Tecnología, hemos adquirido enormes capacidades de trabajo en equipo. Atendiendo al resultado del proyecto, hemos conseguido implementar el total de los objetivos propuestos: CheckAccessPoint para obtener información de un gran número de Puntos de Acceso WiFi conectados al sistema, el entorno de gestión de APs sobre mapas y planos, así como las herramientas de análisis que muestran al usuario los datos enriquecidos que obtiene redBorder. 9.1. Futuras mejoras El próximo paso de este proyecto será su adaptación a un entorno “en la nube”, donde ya no sería posible tener acceso via SNMP con los equipos de red de los clientes. Se pretende migrar el Job CheckAccessPoint de SNMP al sistema Big Data de redBorder funcionando en un cluster distribuido con Apache Zookeper, Apache Hadoop, Samza, utilizando colas de mensajes Kafka para programar un proceso de enriquecimiento y su posterior almacenamiento en Druid.
54
Referencias. [1] Michael Hartl, Ruby on Rails Tutorial, 3ª Edición. Disponible: https://www.railstutorial.org/book [2] D. Harrington, R. Pressuhn, B. Wijnen, Diciembre 2002. An Architecture for Describing Simple Network management Protocol (SNMP) Management Frameworks. RFC 3411 Disponible: https://www.ietf.org/rfc/rfc3411.txt [3] Norman Elton, Diciembre 2014. SNMP Library for EventMachine. Disponible: https://github.com/normelton/snmp4em [4] Google Inc. 2015, Documentación Google Maps Javascript API. Disponible: https://developers.google.com/maps/documentation/javascript/ [5] ENEO Tecnología, 2015. redBorder, empowering leading networks. Disponible: http://redborder.net/ [6] Devesh Pujari, Presanna Vieswakumar. Cisco Systems, Inc. Enero 2006. CISCO-LWAPP-AP-MIB. Disponible: http://tools.cisco.com/Support/SNMP/do/BrowseMIB.do?local=en&step=2&mibName=CISCOLWAPP-AP-MIB [7] Patrick Wied, 2015, Heatmap.js. Sitio Web del proyecto Disponible: http://www.patrick-wied.at/static/heatmapjs/ [8] Mike Dewar, 2012. Getting Started with D3. O’Reilly. ISBN: 978-1-449-32879-5 [9] Druid.io, Documentación. Disponible: http://druid.io/docs/latest/design/index.html
56 ANEXO.A:.Código.CheckAccessPoint. #######################################################################1 #!Copyright!(c)!2014!ENEO!Tecnologia!S.L.1 #!This!file!is!part!of!redBorder.1 #!redBorder!is!free!software:!you!can!redistribute!it!and/or!modify1 #!it!under!the!terms!of!the!GNU!Affero!General!Public!License!as!published!by1 #!the!Free!Software!Foundation,!either!version!3!of!the!License,!or1 #!(at!your!option)!any!later!version.1 #!redBorder!is!distributed!in!the!hope!that!it!will!be!useful,1 #!but!WITHOUT!ANY!WARRANTY;!without!even!the!implied!warranty!of1 #!MERCHANTABILITY!or!FITNESS!FOR!A!PARTICULAR!PURPOSE.!!See!the1 #!GNU!Affero!General!Public!License!for!more!details.1 #!You!should!have!received!a!copy!of!the!GNU!Affero!General!Public!License1 #!along!with!redBorder.!If!not,!see!<http://www.gnu.org/licenses/>.1 #######################################################################1 1 #1 #!This!job!uses!SNMP4EM,!gem!that!extends!RubybSNMP!to!use!the1 #!asynchronous!EventMachine!library!for!added!performance!and1 #!scalabilit.!This!allows!code!to!scale!monitoring!applications1 #!to!access!a!very!hight!number!of!devices.1 #1 #!Supports!only!SNMPv2c1 #1 1 module1Redborder1 11module1Jobs1 1111class1CheckAccessPointsStatusJob1<1Struct.new(:verbose)1 111111include1Redborder::Jobs::JobHelper1 111111#!General!OIDs1 111111NAME1=1'1.3.6.1.4.1.9.9.513.1.1.1.1.5'1 111111MAC1=1'1.3.6.1.4.1.9.9.513.1.2.3.1.1'1 111111CLIENTS1=1'1.3.6.1.4.1.9.9.513.1.1.1.1.54'1 111111UPTIME1=1'1.3.6.1.4.1.9.9.513.1.1.1.1.6'1 111111#!Location!OIDs1 111111LAT1=1'1.3.6.1.4.1.9.9.616.1.6.1.1.3'1 111111LNG1=1'1.3.6.1.4.1.9.9.616.1.6.1.1.4'1 111111#!Meraki!OIDs1 111111MERAKI1=1'1.3.6.1.4.1.29671.1.1.4.1'1 111111MERAKI_MAC1=1'1.3.6.1.4.1.29671.1.1.4.1.1.'1 111111MERAKI_NAME1=1'1.3.6.1.4.1.29671.1.1.4.1.2.'1 111111MERAKI_IP1=1'1.3.6.1.4.1.29671.1.1.4.1.7.'1 111111MERAKI_STATUS1=1'1.3.6.1.4.1.29671.1.1.4.1.3.'1 111111MERAKI_CLIENTS1=1'1.3.6.1.4.1.29671.1.1.4.1.5'1 1 1 1 111111def1perform1 11111111#!Loop!for!every!FLOW!sensor1 11111111Sensor.where(type:1Sensor::FLOW,1is_deleted:1false)1
1111111111.where.not(ip:1nil).each1do1|flow|1 1111111111snmp_community1=1get_snmp_community(flow)1 1111111111snmp_version1=1get_snmp_version(flow)1 1111111111next1if1snmp_version1==1:SNMPv11 1 1111111111begin1 111111111111EM.run1do1 11111111111111snmp1=1 1111111111111111SNMP4EM::Manager.new(1 111111111111111111host:1flow.ip,1 111111111111111111community:1snmp_community,1 111111111111111111version:1snmp_version1 1111111111111111)1 11111111111111request1=1 11111111111111snmp.bulkwalk([NAME,1MAC,1CLIENTS,1 11111111111111111111111111111UPTIME,1LAT,1LNG])1 11111111111111request.errback1do1 1111111111111111EM.stop1 11111111111111end1 11111111111111request.callback1do1|response|1 1111111111111111#!All!Access!Points!DOWN!so!we'll!set!UP!SNMP!detected!APs1 1111111111111111AccessPoint.where(switch_id:1flow.id).update_all(1 111111111111111111status:1AccessPoint::STATUS[:DOWN],1 111111111111111111clients:10,1 111111111111111111uptime:10)1 1111111111111111#!Update!Sensor!last!check!in1 1111111111111111flow.property[:check_in_time]1=1Time.now.to_i1 1111111111111111flow.save1 1111111111111111#!Handle!response1 1111111111111111flow_response(flow,1response)1 1111111111111111EM.stop1 11111111111111end1 111111111111end1 1111111111rescue1SNMP::RequestTimeout1 111111111111next1 1111111111end1 11111111end1 11111111#!Loop!for!every!MERAKI!sensor1 11111111Sensor.where(type:1Sensor::MERAKI,1is_deleted:1false)1 1111111111.where.not(ip:1nil).each1do1|meraki|1 1111111111snmp_community1=1get_snmp_community(meraki)1 1111111111snmp_version1=1get_snmp_version(meraki)1 1111111111next1if1snmp_version1==1:SNMPv11 1111111111begin1 111111111111EM.run1do1 11111111111111snmp1=1SNMP4EM::Manager.new(1 1111111111111111host:1meraki.ip,1 1111111111111111port:1161_00,1 1111111111111111community:1snmp_community,1 1111111111111111version:1snmp_version1 11111111111111)1 11111111111111request1=1snmp.bulkwalk(MERAKI)1 11111111111111request.errback1do1 1111111111111111EM.stop1 11111111111111end1 11111111111111request.callback1do1|response|1 1111111111111111#!Update!Sensor!last!check!in1 1111111111111111meraki.property[:check_in_time]1=1Time.now.to_i1 1111111111111111meraki.save1 1111111111111111#!Handle!response1 1111111111111111meraki_response(meraki,1response)1 1111111111111111EM.stop1
64 11111111111111format.html1{1redirect_to1sensors_url1}1 11111111111111format.json1{1render1json:1{1query:1true,1id:
[email protected]}1}1 11111111111111format.js1 111111111111end1 1111111111end1 11111111end1 1 11111111#!bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb1 11111111#!End!Sensors!methods1 11111111#!bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb1 1 11111111#1 11111111#!=>!Search!a!flow!sensor!based!on!the!first!letter!of!its!name1 11111111#1 11111111def1flow_letter_search1 1111111111#!Get!the!letter1 1111111111@letter1=1params[:id]1 1 1111111111#!Check!the!letter1 1111111111if1[*"A".."Z"].include?1@letter1 111111111111sensor_ids1=1if1params[:sensor_id].nil?1 11111111111111Sensor.where(type:1Sensor::FLOW,1is_deleted:1false).where("LOWER(name)1 LIKE1?",1"#{@letter.downcase}%").select{|s|1current_user.can?(:read,1s)}.map(&:id)1 111111111111else1 11111111111111@sensor1=1Sensor.find(params[:sensor_id])1
[email protected]_children.where(type:1Sensor::FLOW,1is_deleted:1 false).where("LOWER(name)1LIKE1?",1"#{@letter.downcase}%").select{|s|1 current_user.can?(:read,1s)}.map(&:id)1 111111111111end1 111111111111@sensors1=1Sensor.where(id:1sensor_ids).order('LOWER(name)1ASC')1 111111111111render1:flow_search1 1111111111else1 111111111111#!Something!went!wrong1 111111111111redirect_to1flow_sensors_path1 1111111111end1 1 11111111end1 1 11111111def1flow_search1 1111111111@search1=1params[:search]1 1111111111sensor_ids1=1if1params[:sensor_id].nil?1 111111111111Sensor.where(type:1Sensor::FLOW,1is_deleted:1false).where("LOWER(name)1 LIKE1?",1"%#{@search.downcase.split.join('%')}%").select{|s|1current_user.can?(:read,1 s)}.map(&:id)1 1111111111else1 111111111111@sensor1=1Sensor.find(params[:sensor_id])1
[email protected]_children.where(type:1Sensor::FLOW,1is_deleted:1 false).where("LOWER(name)1LIKE1?",1 "%#{@search.downcase.split.join('%')}%").select{|s|1current_user.can?(:read,1 s)}.map(&:id)1 1111111111end1 1111111111@sensors1=1Sensor.where(id:1sensor_ids).order("LOWER(name)1ASC")1 11111111end1 1 11111111def1flow_load_more1 1 1111111111#!Get!the!page1 1111111111@page1=1params[:page].to_i1 1 1111111111#!Get!the!sensors1
1111111111if1params[:sensor_id].nil?1 111111111111sensor_ids1=1Sensor.where(type:1Sensor::FLOW,1is_deleted:1false)1 1111111111111111111111111.select{|s|1current_user.can?(:read,1s)}.map(&:id)1 1111111111else1 111111111111@sensor1=1Sensor.find(params[:sensor_id])1
[email protected]_children1 1111111111111111111111111.where(type:1Sensor::FLOW,1is_deleted:1false)1 1111111111111111111111111.select{|s|1current_user.can?(:read,1s)}.map(&:id)1 1111111111end1 1 1 1111111111@sensors1=1Sensor.unscoped.where(id:1sensor_ids).order('LOWER(name)1 ASC').offset(@page1*1@sensors_page).take(@sensors_page)1 1 1111111111#!Set!the!current!letter!empty1 1111111111before_sensor1=1Sensor.unscoped.where(id:1 sensor_ids).order(:name).offset((@page1*1@sensors_page)1s11).first1 1 1111111111if1before_sensor.nil?1 111111111111@current_letter1=1''1 1111111111else1 111111111111@current_letter1=1before_sensor.secondary_name[0,1].upcase1 1111111111end1 1 1111111111respond_to1do1|format|1 11111111111format.js1{}1 11111111111format.json1{1render1json:1@sensors1}1 1111111111end1 1 11111111end1 1 11111111#1 11111111#!=>!Search!a!mse!sensor!based!on!the!first!letter!of!its!name1 11111111#1 11111111def1mse_letter_search1 1 1111111111#!Get!the!letter1 1111111111@letter1=1params[:id]1 1 1111111111#!Check!the!letter1 1111111111if1[*"A".."Z"].include?1@letter1 111111111111sensor_ids1=1if1params[:sensor_id].nil?1 11111111111111Sensor.where(type:1Sensor::MSE,1is_deleted:1false).where("LOWER(name)1 LIKE1?",1"#{@letter.downcase}%").select{|s|1current_user.can?(:read,1s)}.map(&:id)1 111111111111else1 11111111111111@sensor1=1Sensor.find(params[:sensor_id])1
[email protected]_children.where(type:1Sensor::MSE,1is_deleted:1 false).where("LOWER(name)1LIKE1?",1"#{@letter.downcase}%").select{|s|1 current_user.can?(:read,1s)}.map(&:id)1 111111111111end1 111111111111@sensors1=1Sensor.where(id:1sensor_ids).order('LOWER(name)1ASC')1 111111111111render1:mse_search1 1111111111else1 111111111111#!Something!went!wrong1 111111111111redirect_to1mse_sensors_path1 1111111111end1 1 11111111end1 1 11111111def1mse_search1 1111111111@search1=1params[:search]1 1111111111sensor_ids1=1if1params[:sensor_id].nil?1
66 111111111111Sensor.where(type:1Sensor::MSE,1is_deleted:1false).where("LOWER(name)1 LIKE1?",1"%#{@search.downcase.split.join('%')}%").select{|s|1current_user.can?(:read,1 s)}.map(&:id)1 1111111111else1 111111111111@sensor1=1Sensor.find(params[:sensor_id])1
[email protected]_children.where(type:1Sensor::MSE,1is_deleted:1 false).where("LOWER(name)1LIKE1?",1 "%#{@search.downcase.split.join('%')}%").select{|s|1current_user.can?(:read,1 s)}.map(&:id)1 1111111111end1 1111111111@sensors1=1Sensor.where(id:1sensor_ids).order("LOWER(name)1ASC")1 11111111end1 1 11111111def1mse_load_more1 1 1111111111#!Get!the!page1 1111111111@page1=1params[:page].to_i1 1 1111111111#!Get!the!sensors1 1111111111if1params[:sensor_id].nil?1 111111111111sensor_ids1=1Sensor.where(type:1Sensor::MSE,1is_deleted:1false)1 1111111111111111111111111.select{|s|1current_user.can?(:read,1s)}.map(&:id)1 1111111111else1 111111111111@sensor1=1Sensor.find(params[:sensor_id])1
[email protected]_children1 1111111111111111111111111.where(type:1Sensor::MSE,1is_deleted:1false)1 1111111111111111111111111.select{|s|1current_user.can?(:read,1s)}.map(&:id)1 1111111111end1 1 1 1111111111@sensors1=1Sensor.unscoped.where(id:1sensor_ids).order('LOWER(name)1 ASC').offset(@page1*1@sensors_page).take(@sensors_page)1 1 1111111111#!Set!the!current!letter!empty1 1111111111before_sensor1=1Sensor.unscoped.where(id:1 sensor_ids).order(:name).offset((@page1*1@sensors_page)1s11).first1 1 1111111111if1before_sensor.nil?1 111111111111@current_letter1=1''1 1111111111else1 111111111111@current_letter1=1before_sensor.secondary_name[0,1].upcase1 1111111111end1 1 1111111111respond_to1do1|format|1 11111111111format.js1{}1 11111111111format.json1{1render1json:1@sensors1}1 1111111111end1 1 11111111end1 1 11111111#1 11111111#!=>!Search!a!meraki!sensor!based!on!the!first!letter!of!its!name1 11111111#1 11111111def1meraki_letter_search1 1111111111#!Get!the!letter1 1111111111@letter1=1params[:id]1 1 1111111111#!Check!the!letter1 1111111111if1[*"A".."Z"].include?1@letter1 111111111111@sensors1=1if1params[:sensor_id].nil?1
11111111111111current_user.sensor.self_and_descendants1 111111111111else1 11111111111111@sensor1=1Sensor.find(params[:sensor_id])1 1 11111111111111if1current_user.cannot?(:read,1@sensor)1 1111111111111111Sensor.none1 11111111111111else1
[email protected]_and_descendants1 11111111111111end1 111111111111end1 111111111111@
[email protected](type:1Sensor::MERAKI,1is_deleted:1false)1 11111111111111111111111.where('LOWER(name)1LIKE1?',1"#{@letter.downcase}%")1 11111111111111111111111.reorder('LOWER(name)1ASC')1 111111111111render1:meraki_search1 1111111111else1 111111111111#!Something!went!wrong1 111111111111redirect_to1meraki_sensors_path1 1111111111end1 11111111end1 1 11111111def1meraki_search1 1111111111@search1=1params[:search]1 1 1111111111@sensors1=1if1params[:sensor_id].nil?1 111111111111current_user.sensor.self_and_descendants1 1111111111else1 111111111111@sensor1=1Sensor.find(params[:sensor_id])1 1 111111111111if1current_user.cannot?(:read,1@sensor)1 11111111111111Sensor.none1 111111111111else1
[email protected]_and_descendants1 111111111111end1 1111111111end1 1 1111111111@
[email protected](type:1Sensor::MERAKI,1is_deleted:1false)1 111111111111111111111.where('LOWER(name)1LIKE1?',1 "%#{@search.downcase.split.join('%')}%")1 111111111111111111111.reorder('LOWER(name)1ASC')1 11111111end1 1 11111111def1meraki_load_more1 1111111111#!Get!the!page1 1111111111@page1=1params[:page].to_i1 1 1111111111#!Get!the!sensors1 1111111111all_sensors1=1if1params[:id].nil?1 11111111111111111111111111current_user.sensor.self_and_descendants1 111111111111111111111111else1 11111111111111111111111111@sensor1=1Sensor.find(params[:id])1 11111111111111111111111111if1current_user.cannot?(:read,1@sensor)1 1111111111111111111111111111Sensor.none1 11111111111111111111111111else1
[email protected]_and_descendants1 11111111111111111111111111end1 111111111111111111111111end1 1 1111111111@sensors1=1all_sensors.where(type:1Sensor::MERAKI,1is_deleted:1false)1 111111111111111111111.reorder('LOWER(name)1ASC')1 111111111111111111111.offset(@page1*1@sensors_page)1 111111111111111111111.take(@sensors_page)1 1
68 1111111111#!Set!the!current!letter!empty1 1111111111before_sensor1=1all_sensors.reorder('LOWER(name)1ASC')1 11111111111111111111111111.offset((@page1*1@sensors_page)1s11).first1 1 1111111111if1before_sensor.nil?1 111111111111@current_letter1=1''1 1111111111else1 111111111111@current_letter1=1before_sensor.secondary_name[0,11].upcase1 1111111111end1 1 1111111111respond_to1do1|format|1 111111111111format.js1{}1 111111111111format.json1{1render1json:1@sensors1}1 1111111111end1 11111111end1 1 11111111#!POST!/sensors/create_fow1 11111111def1create_flow1 1111111111@sensor1=1Sensor.new(send("new_sensor_params_#{Sensor::FLOW}"))1
[email protected]_id1=1(current_user.super_admin?1?1Sensor.root.id1:1 current_user.sensor.id)1if1params[:sensor][:parent_id].blank?1 1111111111@parent_id1=1params[:sensor][:parent_id]1if1 params[:sensor][:parent_id].present?1 1111111111@permission1=1true1 1 1111111111respond_to1do1|format|1 111111111111if1current_user.cannot?(:manage,
[email protected])1 11111111111111@permission1=1false1 11111111111111#!No!permissions!on!parent.1 11111111111111format.html1do1 1111111111111111redirect_to1sensors_path,1 1111111111111111111111111111alert:1"You1don't1have1enough1privileges1to1do1that."1 11111111111111end1 11111111111111format.json1do1 1111111111111111render1json:1{1error:1"You1don't1have1enough1privileges1to1do1that."1 },1 11111111111111111111111status:1:unauthorized1 11111111111111end1 11111111111111format.js1do1 1111111111111111flash[:error]1=1"You1don't1have1privileges1to1do1that"1 11111111111111end1
[email protected] 11111111111111#!You!need!to!reload!the!user!sensor!rgt!value!before!rendering!menus1 11111111111111#!in!order!to!set!manage!privileges!for!the!current!user1 11111111111111current_user.reload1 1 11111111111111Delayed::Job.enqueue(Redborder::Jobs::RbWakeupChefClusterJob.new(true,1 current_user.id),1priority:15)1unless1DelayedJob.rb_wakeup_chef_cluster?1 11111111111111format.html1{1redirect_to1sensors_path,1notice:1"Sensor1was1 successfully1created.1It1can1take1up1to1101minutes1to1show1data."1}1 11111111111111format.json1{1head1:no_content1}1 11111111111111format.js1 111111111111else1 11111111111111format.html1{1render1action:1'new_flow'1}1 11111111111111format.json1{1render1json:
[email protected],1status:1 :unprocessable_entity1}1 11111111111111format.js1 111111111111end1 1111111111end1 11111111end1
1 11111111#!POST!/sensors/create_mse1 11111111def1create_mse1 1111111111#!MSE!has!same!params1 1111111111@sensor1=1Sensor.new(send("new_sensor_params_#{Sensor::MSE}"))1
[email protected]_id1=1(current_user.super_admin?1?1Sensor.root.id1:1 current_user.sensor.id)1if1params[:sensor][:parent_id].blank?1 1111111111@parent_id1=1params[:sensor][:parent_id]1if1 params[:sensor][:parent_id].present?1 1111111111@permission1=1true1 1 1111111111respond_to1do1|format|1 111111111111if1current_user.cannot?(:manage,
[email protected])1 11111111111111@permission1=1false1 11111111111111#!No!permissions!on!parent.1 11111111111111format.html1do1 1111111111111111redirect_to1sensors_path,1 1111111111111111111111111111alert:1"You1don't1have1enough1privileges1to1do1that."1 11111111111111end1 11111111111111format.json1do1 1111111111111111render1json:1{1error:1"You1don't1have1enough1privileges1to1do1that."1 },1 11111111111111111111111status:1:unauthorized1 11111111111111end1 11111111111111format.js1do1 1111111111111111flash[:error]1=1"You1don't1have1privileges1to1do1that"1 11111111111111end1
[email protected] 11111111111111#!You!need!to!reload!the!user!sensor!rgt!value!before!rendering!menus1 11111111111111#!in!order!to!set!manage!privileges!for!the!current!user1 11111111111111current_user.reload1 111111111111111 11111111111111Delayed::Job.enqueue(Redborder::Jobs::RbWakeupChefClusterJob.new(true,1 current_user.id),1priority:15)1unless1DelayedJob.rb_wakeup_chef_cluster?1 11111111111111format.html1{1redirect_to1sensors_path,1notice:1"Sensor1was1 successfully1created.1It1can1take1up1to1101minutes1to1show1data."1}1 11111111111111format.json1{1head1:no_content1}1 11111111111111format.js1 111111111111else1 11111111111111format.html1{1render1action:1'new_mse'1}1 11111111111111format.json1{1render1json:
[email protected],1status:1 :unprocessable_entity1}1 11111111111111format.js1 111111111111end1 1111111111end1 11111111end1 1 11111111#!POST!/sensors/create_meraki1 11111111def1create_meraki1 1111111111#!MSE!has!same!params1 1111111111@sensor1=1Sensor.new(send("new_sensor_params_#{Sensor::MERAKI}"))1
[email protected]_id1=1(current_user.super_admin?1?1Sensor.root.id1:1 current_user.sensor.id)1if1params[:sensor][:parent_id].blank?1 1111111111@parent_id1=1params[:sensor][:parent_id]1if1 params[:sensor][:parent_id].present?1 1111111111@permission1=1true1 1 1111111111respond_to1do1|format|1 111111111111if1current_user.cannot?(:manage,
[email protected])1 11111111111111@permission1=1false1 11111111111111#!No!permissions!on!parent.1 11111111111111format.html1do1
70 1111111111111111redirect_to1sensors_path,1 1111111111111111111111111111alert:1"You1don't1have1enough1privileges1to1do1that."1 11111111111111end1 11111111111111format.json1do1 1111111111111111render1json:1{1error:1"You1don't1have1enough1privileges1to1do1that."1 },1 11111111111111111111111status:1:unauthorized1 11111111111111end1 11111111111111format.js1do1 1111111111111111flash[:error]1=1"You1don't1have1privileges1to1do1that"1 11111111111111end1
[email protected] 11111111111111#!You!need!to!reload!the!user!sensor!rgt!value!before!rendering1 11111111111111#!menus!in!order!to!set!manage!privileges!for!the!current!user1 11111111111111current_user.reload1 111111111111111 11111111111111Delayed::Job.enqueue(1 1111111111111111Redborder::Jobs::RbWakeupChefClusterJob.new(true,1 111111111111111111111111111111111111111111111111111111111111current_user.id),1 1111111111111111priority:151 11111111111111)1unless1DelayedJob.rb_wakeup_chef_cluster?1 11111111111111format.html1do1 1111111111111111redirect_to1edit_sensor_path(@sensor),1 1111111111111111111111111111notice:1'Sensor1was1successfully1created.1\1 1111111111111111111111111111111111111It1can1take1up1to1101minutes1to1show1data.'1 11111111111111end1 11111111111111format.json1{1head1:no_content1}1 11111111111111format.js1 111111111111else1 11111111111111format.html1{1render1action:1'new_mse'1}1 11111111111111format.json1do1 1111111111111111render1json:
[email protected],1status:1:unprocessable_entity1 11111111111111end1 11111111111111format.js1 111111111111end1 1111111111end1 11111111end1 1 11111111[Sensor::FLOW,1Sensor::DOMAIN].each1do1|section|1 1111111111define_method("view_#{section}")1do1 111111111111@gps_markers1=1gps_markers1params[:id]1 111111111111show_aps1 111111111111render1"view_#{section}"1 1111111111end1 11111111end1 1 11111111def1load_more_aps1 1111111111@search1=1params[:search]1 1111111111@page1=1params[:page]1 1111111111@status1=1params[:status]1 1111111111show_aps1 1 1111111111respond_to1:js1 11111111end1 1 11111111def1access_points1 1111111111sensor1=1Sensor.find(params[:id])1 11111111111 1111111111if1current_user.can?(:read,1sensor)1 111111111111render1json:1{1aps:1sensor.access_points1}1
1111111111else1 111111111111render1json:1{1aps:1[],1status:1:unauthorized1}1 1111111111end1 11111111end1 1 11111111def1search_aps1 1111111111@search1=1params[:search]1 1111111111@status1=1params[:status]1 1111111111show_aps1 11111111111 1111111111respond_to1:js1 11111111end1 1 11111111def1search_gps_markers1 1111111111@page1=1params[:page]1 1111111111@gps_markers1=1gps_markers1params[:id]1 11111111111 1111111111respond_to1:js1 11111111end1 1 11111111#!Generate!form!for!creating!new!Access!Point!when1 11111111#!click!on!semsor!map!or!floor!image1 11111111def1new_access_point1 1111111111point1=1{}11111111111 1 1111111111#!point!comes!from!a!map!!!!!!!!!!1 1111111111if1params[:latLngFormat]1 111111111111point[:latitude]1=1params[:point][1].to_f1 111111111111point[:longitude]1=1params[:point][0].to_f1
[email protected]?1 11111111111111@
[email protected]{1|f|1f.contains?(point)}1 111111111111end1 1111111111else1111111111111 111111111111point[:x]1=1params[:point][0].to_f1 111111111111point[:y]1=1params[:point][1].to_f1111111111111 1111111111end1 1111111111@access_point1=1AccessPoint.new(point)11111111111 1111111111render1layout:1false11111111111 11111111end1 1 11111111def1create_access_point11111111111 1111111111@access_point1=1AccessPoint.new(access_point_params)1 1111111111if1@access_point.x1&&1@access_point.y11 111111111111@access_point.recalculate_lnglat1 1111111111elsif1@access_point.latitude1&&1@access_point.longitude1 111111111111@access_point.recalculate_xy1 1111111111end1 11111111111 1111111111respond_to1do1|format|1 111111111111if1@access_point.save1&&1@access_point.manage_permission?1 11111111111111@zones1=1[]1
[email protected]_flow?1||
[email protected]_domain?1||
[email protected]_meraki?1 1111111111111111sensors_ids1=1[]1
[email protected]_domains(true).map1do1|s|1 111111111111111111if1current_user.can?(:read,1s)1&&1!s.zones.empty?1 11111111111111111111sensors_ids1<<1s.id1 111111111111111111end1 1111111111111111end1 1111111111111111@zones1=1Zone.where(sensor_id:1sensors_ids).order('name1ASC')1 11111111111111end1 11111111111111@
[email protected]_points1 11111111111111#!We!need!this!to!display!the!dropd!down!assign!floor!if!doesn't1
72 11111111111111#!exist!the!access!point!table1
[email protected]_flow?1||
[email protected]_meraki?1 1111111111111111@assignable_floors1=1 111111111111111111current_user.sensor.self_and_descendants1 111111111111111111.where(domain_type:1Sensor::FLOOR)1 11111111111111else1 1111111111111111if1current_user.can?(:manage,1@sensor)1&&
[email protected]?1 111111111111111111@
[email protected] 11111111111111111111111111111111111111111.where(domain_type:1Sensor::FLOOR)1 1111111111111111end1 11111111111111end1 11111111111111format.html1{1redirect_to1view_sensor_path(@sensor),1notice:1'Access1 Point1was1successfully1created.'1}1 11111111111111format.js1{1flash[:notice]1=1"Access1Point1was1successfully1created."1}1 11111111111111format.json1{1head1:no_content1}1 111111111111else1 11111111111111format.html1{1redirect_to1view_sensor_path(@sensor),1alert:1 @access_point.errors.full_messages.map1{1|msg|1"<li>#{msg}</li>"1}.join1}1 11111111111111format.js1 11111111111111format.json1{1render1json:1@access_point.errors,1status:1 :unprocessable_entity1}1 111111111111end1 1111111111end1 11111111end1 1 11111111def1new_locate_access_point111111111111111111111 1111111111point1=1{}1 1 1111111111#!point!comes!from!a!map!!!!!!!!!!1 1111111111if1params[:latLngFormat]1 111111111111point[:latitude]1=1params[:point][1].to_f1 111111111111point[:longitude]1=1params[:point][0].to_f1
[email protected]?1 11111111111111@
[email protected]{1|f|1f.contains?(point)}1 111111111111end111111111111 1111111111#!point!comes!from!a!located!floor!image1 1111111111else11111111111 111111111111point[:x]1=1params[:point][0].to_f1 111111111111point[:y]1=1params[:point][1].to_f1111111111111 1111111111end1 1111111111@access_point1=1AccessPoint.new(point)1 1 1111111111#!unallocated!access!points1 1111111111@
[email protected]_points1 111111111111111111.where('latitude1is1NULL1OR1longitude1is1NULL')1 111111111111111111.map1{1|ap|1["#{ap.name}1|1#{ap.mac_address}",1ap.id]1}1 1111111111render1layout:1false1 11111111end1 1 11111111def1locate_access_point1 1111111111if1@access_point.update(access_point_params)1 111111111111if1@access_point.latitude1&&1@access_point.longitude1 11111111111111@access_point.recalculate_xy11 111111111111elsif1@access_point.x1&&1@access_point.y1 11111111111111@access_point.recalculate_lnglat1 111111111111end1 1111111111end1 11111111end1 1 11111111def1assign_floor_to_access_point111111111111111111
1111111111floor1=1Sensor.find_by(id:1params[:sensor_floor])1 1111111111return1if1floor.nil?1 1111111111@access_points1=1AccessPoint.where(id:1params[:access_points])1 1111111111@sensor1=1Sensor.find(params[:id])1 1111111111@access_points.each1do1|ap|1 111111111111next1unless1ap.manage_permission?1 111111111111ap.update(sensor_id:1floor.id)1 1111111111end1 11111111end1 1 11111111def1edit_access_point11111111111 1111111111@current_sensor1=1params[:top_sensor_id]1 1111111111@access_point1=1AccessPoint.find(params[:id])1 1111111111@sensors1=1Sensor.where(1 111111111111is_deleted:1false,1type:1[Sensor::FLOW,1Sensor::MERAKI]1 1111111111).select1{1|s|1current_user.can?(:read,1s)1}1 11111111111 1111111111@floors1=1Sensor.where(1 111111111111is_deleted:1false,1type:1Sensor::DOMAIN,1 111111111111domain_type:1Sensor::FLOOR1 1111111111).select1{1|s|1current_user.can?(:read,1s)1}1 1 1111111111render1layout:1false1 11111111end1 1 11111111def1update_access_point1 1111111111@access_point1=1AccessPoint.find(params[:id])1 1111111111current_location1=1{1 111111111111latitude:1@access_point[:latitude],1 111111111111longitude:1@access_point[:longitude],1 111111111111x:1@access_point[:x],1 111111111111y:1@access_point[:y]1 1111111111}1 1111111111#!Recalculate!location!if!it's!changed1 1111111111if1@access_point.manage_permission?1&&1 1111111111111@access_point.update(access_point_params)1 111111111111if1@access_point[:x]1!=1current_location[:x]1&&1 111111111111111@access_point[:y]1!=1current_location[:y]1 11111111111111@access_point.recalculate_lnglat1 111111111111elsif1@access_point[:latitude]1!=1current_location[:latitude]1&&1 111111111111111111@access_point[:longitude]1!=1current_location[:longitude]1 11111111111111@access_point.recalculate_xy1 111111111111end1 1111111111end11111111111 1111111111#!If!we!comes!from!relocate!an!access!point,!we!must!render!2001 1111111111if1params[:relocate]1 111111111111render1json:1@access_point1 1111111111elsif1@access_point.errors.empty?1 111111111111@sensor1=1Sensor.find(params[:access_point][:current_sensor])1 1111111111end1 11111111end1 1 11111111def1delete_access_point1 1111111111@ap_id1=1params[:id]1 1111111111@access_point1=1AccessPoint.find(@ap_id)1 1111111111@sensor1=1@access_point.sensor1 1111111111@access_point.destroy1if1@access_point.manage_permission?1 11111111end1 1 11111111def1delete_all_access_points1 1111111111sensor1=1Sensor.find(params[:id])1 1111111111if1current_user.can?(:manage,1sensor)1
80 1 11111111def1aps_to_csv_format(sensor)1 1111111111csv1=1'Name;IP1Address;Mac1Address;Sensor1IP;'1\1 1111111111111111"Latitude;Longitude;X;Y\n"1 1111111111csv1+=1sensor.access_points1 11111111111111111.map1{1|ap|1"#{ap.name};#{ap.ip_address};"1\1 11111111111111111111111111111"#{ap.mac_address};#{ap.switch_name};"1\1 11111111111111111111111111111"#{ap.latitude};#{ap.longitude};#{ap.x};#{ap.y}"1 1111111111111111111111}.join("\n")1 11111111end1 1 11111111def1require_super_administrative_privileges_flow1 1111111111require_super_administrative_privileges1 11111111end1 1 11111111define_method("sensor_params_#{Sensor::FLOW}")1do1 1111111111params1 111111111111.require(:sensor)1 111111111111.permit(:parent_id,1:ip,1:version_flow,1:general_description,1 11111111111111111111:nmsp,1:spanport,1:nmsp_client_stats,1:nmsp_wireless_health,1 11111111111111111111:dns_ptr_client,1:dns_ptr_target)1 11111111end1 1 11111111define_method("sensor_params_#{Sensor::MSE}")1do1 1111111111params.require(:sensor)1 1111111111111111.permit(:parent_id,1:ip,1:version_mse,1:general_description)1 11111111end1 1 11111111define_method("sensor_params_#{Sensor::MERAKI}")1do1 1111111111params.require(:sensor)1 1111111111111111.permit(:parent_id,1:ip,1:version_meraki,1:general_description,1 111111111111111111111111:meraki_validator)1 11111111end1 1 11111111define_method("new_sensor_params_#{Sensor::FLOW}")1do1 1111111111params1 111111111111.require(:sensor)1 111111111111.permit(:type,1:name,1:ip,1:parent_id)1 11111111end1 1 11111111define_method("new_sensor_params_#{Sensor::MSE}")1do1 1111111111params.require(:sensor).permit(:type,1:name,1:ip,1:parent_id,1:nmsp)1 11111111end1 1 11111111define_method("new_sensor_params_#{Sensor::MERAKI}")1do1 1111111111params.require(:sensor).permit(:type,1:name,1:ip,1:parent_id,1 11111111111111111111111111111111111111111:meraki_validator)1 11111111end1 1 11111111def1set_parent_id_flow1 1111111111set_parent_id1 11111111end1 1 11111111def1privileges_on_top_domain_flow1 1111111111privileges_on_top_domain1 11111111end1 1 11111111#1 11111111#!Initialize!Sensor!from!params!checking!if!user!has!permissions!on!it1 11111111#1
11111111def1initialize_sensor_if_permissions1 1111111111@sensor1=1Sensor.find(params[:id])1 1111111111return1if1current_user.can?(:manage,1@sensor)1 1111111111respond_to1do1|format|1 111111111111format.html1do1 11111111111111redirect_to1sensor_path(@sensor),1 1111111111111111alert:1"You1don't1have1enough1privileges1to1do1that."1 111111111111end1 111111111111format.json1do1 11111111111111render1json:1{1error:1"You1don't1have1enough1privileges1to1do1that."1},1 111111111111111111111status:1:unauthorized1 111111111111end1 111111111111format.js1do1 11111111111111render1json:1{1error:1"You1don't1have1enough1privileges1to1do1that."1},1 111111111111111111111status:1:unauthorized1 111111111111end1 1111111111end1 11111111end1 1 11111111#1 11111111#!Return!min!and!max!lnglat!point!for!given!floor1 11111111#1 11111111def1lnglat_corners(f)1 1111111111m_trans1=1f[:property][:matrix_trans]1 1111111111m_offset1=1f[:property][:matrix_offset]1 1111111111w1=1f[:property][:dimensions][:width]1 1111111111h1=1f[:property][:dimensions][:height]1 1111111111nw1=1PrimeInfrastructure::Functions1 111111111111111.point_to_lnglat(m_trans,1m_offset,1[0,10])1 1111111111ne1=1PrimeInfrastructure::Functions1 111111111111111.point_to_lnglat(m_trans,1m_offset,1[w,10])1 1111111111sw1=1PrimeInfrastructure::Functions1 111111111111111.point_to_lnglat(m_trans,1m_offset,1[0,1h])1 1111111111se1=1PrimeInfrastructure::Functions1 111111111111111.point_to_lnglat(m_trans,1m_offset,1[w,1h])1 1111111111{1111111111111 111111111111min_lng:1[nw[0,0],1ne[0,0],1sw[0,0],1se[0,0]].min,1 111111111111min_lat:1[nw[1,0],1ne[1,0],1sw[1,0],1se[1,0]].min,1 111111111111max_lng:1[nw[0,0],1ne[0,0],1sw[0,0],1se[0,0]].max,1 111111111111max_lat:1[nw[1,0],1ne[1,0],1sw[1,0],1se[1,0]].max,1111111111111 111111111111se:1se,1 111111111111sw:1sw,1 111111111111ne:1ne,1 111111111111nw:1nw1 1111111111}1 11111111end1 1 11111111#1 11111111#!Build!square!container!for!floors1 11111111#1 11111111def1buildFloorsContainer(lim)1 1111111111lat1=1(lim[:max_lat].abs1s1lim[:min_lat].abs).abs1 1111111111lng1=1(lim[:max_lng].abs1s1lim[:min_lng].abs).abs1 1 1111111111if1lat1>1lng1 111111111111dif1=1lat1s1lng;1 111111111111lim[:max_lng]1=1lim[:max_lng]1+1dif/21 111111111111lim[:min_lng]1=1lim[:min_lng]1s1dif/21 1111111111elsif1lng1>1lat1 111111111111dif1=1lng1s1lat;1 111111111111lim[:max_lat]1=1lim[:max_lat]1+1dif/21 111111111111lim[:min_lat]1=1lim[:min_lat]1s1dif/21
82 1111111111end1 1111111111lim1 11111111end1 1 11111111#1 11111111#!Get!Trans!Matrix!for!3d!floor!map!container1 11111111#1 11111111def1getTransMatrix(limits)1 1111111111#!define!default!size!for!access!points!3d!floors!container1 1111111111s1=15501 11111111111 1111111111matrix1=1Matrix[1 111111111111[0,10,10,10,11,10],1 111111111111[0,10,10,10,10,11],1 111111111111[s,10,10,10,11,10],1 111111111111[0,10,1s,10,10,11],1 111111111111[s,1s,10,10,11,10],1 111111111111[0,10,1s,1s,10,11]]1 1111111111111 1111111111lat_lng_column1=1Matrix.column_vector(1 111111111111[limits[:min_lng],1limits[:min_lat],1limits[:max_lng],1 1111111111111limits[:min_lat],1limits[:max_lng],1limits[:max_lat]])1 1 1111111111#!The!solution!of!the!system!above1 1111111111sol1=1matrix.inverse1*1lat_lng_column1 1 1111111111#!This!matrix!lets!us!transform!every!feet!point!in!latblng!points1 1111111111base_transf1=1Matrix[1 111111111111[sol[0,10],1sol[1,10]],1 111111111111[sol[2,10],1sol[3,10]]1 1111111111]1 1 1111111111#!The!difference!between!the!two!reference!systems1 1111111111diff1=1Matrix.column_vector([sol[4,10],1sol[5,10]])1 1 1111111111{1 111111111111matrix_trans:1base_transf.to_a,1 111111111111matrix_offset:1diff.to_a1 1111111111}1 11111111end1 1 11111111#1 11111111#!Initialize!floors!and!switches!depending!on!where!user!is1 11111111#!managing!access!points1 11111111#1 11111111def1initialize_floor_and_flow_sensors11111111111111
[email protected]_domain?1&&
[email protected]?1 111111111111@
[email protected]_and_descendants.joins(:gps_markers)1 1111111111111111111111.where(is_deleted:1false,1domain_type:1Sensor::FLOOR)1 1111111111111111111111.group('sensors.id').having('COUNT(gps_markers.id)1>12')1 111111111111@
[email protected]_and_descendants1 111111111111111111111111.where(is_deleted:1false)1 111111111111111111111111.where(type:1[Sensor::FLOW,1Sensor::MERAKI])1
[email protected]_flow?1||
[email protected]_meraki?1 111111111111@floors1=1Sensor.unscoped.find(current_user.sensor)1 1111111111111111111111.self_and_descendants.joins(:gps_markers)1 1111111111111111111111.where(is_deleted:1false,1domain_type:1Sensor::FLOOR)1 1111111111111111111111.group('sensors.id').having('COUNT(gps_markers.id)1>12')11111111111111111111111
[email protected]?1 111111111111@switches1=1current_user.sensor.self_and_descendants1
111111111111111111111111.where(is_deleted:1false)1 111111111111111111111111.where(type:1[Sensor::FLOW,1Sensor::MERAKI])1 1111111111end1 11111111end1 1 11111111#1 11111111#!Check!if!user!can!manage!selected!floor!and!switch!!!!!!!!!1 11111111#1 11111111def1permissions_on_selected_switch_and_floor1 1111111111ap_params1=1params.fetch(:access_point,1{})1 1111111111floor1=1Sensor.find_by(id:1ap_params[:sensor_id])1 1111111111switch1=1Sensor.find_by(id:1ap_params[:switch_id])1 1111111111return1if1(floor.nil?1||1current_user.can?(:manage,1floor))1&&1 11111111111111111111(switch.nil?1||1current_user.can?(:manage,1switch))1 1111111111respond_to1do1|format|1 111111111111format.html1do1 11111111111111redirect_to1sensor_path(@sensor),1 1111111111111111alert:1"You1don't1have1enough1privileges1to1do1that."1 111111111111end1 111111111111format.json1do1 11111111111111render1json:1{1error:1"You1don't1have1enough1privileges1to1do1that."1},1 111111111111111111111status:1:unauthorized1 111111111111end1 111111111111format.js1do1 11111111111111render1json:1{1error:1"You1don't1have1enough1privileges1to1do1that."1},1 111111111111111111111status:1:unauthorized1 111111111111end1 1111111111end1 11111111end1 1 11111111def1permissions_on_access_point11111111111 1111111111ap_params1=1params.fetch(:access_point,1{})1 1111111111unless1ap_params[:id].nil?1 111111111111@access_point1=1AccessPoint.find(ap_params[:id])1 1111111111end1 1111111111return1if1@access_point1&&1@access_point.manage_permission?1 1111111111respond_to1do1|format|1 111111111111format.html1do1 11111111111111redirect_to1sensor_path(@sensor),1 1111111111111111alert:1"You1don't1have1enough1privileges1to1do1that."1 111111111111end1 111111111111format.json1do1 11111111111111render1json:1{1error:1"You1don't1have1enough1privileges1to1do1that."1},1 111111111111111111111status:1:unauthorized1 111111111111end1 111111111111format.js1do1 11111111111111render1json:1{1error:1"You1don't1have1enough1privileges1to1do1that."1},1 111111111111111111111status:1:unauthorized1 111111111111end1 1111111111end1 1 11111111end1 1 11111111def1set_flow_sensors_per_page1 1111111111@sensors_page1=1Sensor::SENSORS_PAGE1 11111111end1 1 11111111def1redirect_to_view1 1111111111redirect_to1view_sensor_path(@sensor)
[email protected]?1 11111111end1 1 11111111def1sort_column1
84 1111111111params[:sort]1?1params[:sort]1:1"created_at"1 11111111end1 111111111 11111111def1sort_direction1 1111111111%w[asc1desc].include?(params[:direction])1?1params[:direction]1:1"desc"1 11111111end1 1 11111111def1access_point_params1 1111111111params.require(:access_point)1 111111111111.permit(:name,1:ip_address,1:mac_address,1:latitude,1:longitude,1 11111111111111111111:switch_name,1:switch_id,1:sensor_id,1:x,1:y,1:description)1 11111111end1 111111end1 1111end1 11end1 end1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 Sensors_flow.js /*!!!!! !*!Copyright!(c)!2014!ENEO!TecnologÌa!S.L.! !*!This!file!is!part!of!redBorder.! !*!redBorder!is!free!software:!you!can!redistribute!it!and/or!modify! !*!it!under!the!terms!of!the!GNU!Affero!General!Public!License!as!published!by! !*!the!Free!Software!Foundation,!either!version!3!of!the!License,!or! !*!(at!your!option)!any!later!version.! !*!redBorder!is!distributed!in!the!hope!that!it!will!be!useful,! !*!but!WITHOUT!ANY!WARRANTY;!without!even!the!implied!warranty!of! !*!MERCHANTABILITY!or!FITNESS!FOR!A!PARTICULAR!PURPOSE.!!See!the! !*!GNU!Affero!General!Public!License!for!more!details.! !*!You!should!have!received!a!copy!of!the!GNU!Affero!General!Public!License! !*!along!with!redBorder.!If!not,!see!<http://www.gnu.org/licenses/>.! !*/1 11 function1apply_ap_filter(target){1 11var1filters1=1$(".access_points_status1a.btnssuccess").length;1 1111var1sensor_id1=1target.data('sensorsid');1 1111var1url1=1"/sensors/"1+1sensor_id1+1'/search_aps';1 1111var1search1=1target.val();1 1111var1status1=1get_status_filters();1 1 1111if1(redBorder.currentAPs1!==1undefined)1{1 111111redBorder.currentAPs.highlight(search);1 1111}1111 11111 1111if1(search1==1''1&&1filters1==101&&1$("#access_points1table1tbody1 .hidden").length1>101){1 111111//!Show!the!saved!table!!!!!!!!!!!!! 111111$("#access_points1table1tbody1tr").each(function(index){1 11111111if($(this).hasClass('hidden')){1 1111111111$(this).removeClass('hidden')1 11111111}1else1{1 1111111111$(this).remove()1 11111111}1 111111});1 111111$('#access_points_status_offset').remove();1 111111$("#access_points_offset").css('display',1'block');1 1111111 1111}1else1{1111111 111111$("#access_points_offset").css('display',1'none');1 1 111111//!Save!the!current!table! 111111$("#access_points1table1tbody1tr").each(function(index){1 11111111if($(this).hasClass('ap_from_status')){1111111 1111111111$(this).remove();1 11111111}1else1{11111111111 1111111111$(this).addClass('hidden');1 11111111}1 111111});1 1 111111show_loading();1 1 111111$.ajax({1 11111111url:1url,1 11111111remote:1true,1
86 11111111type:1'GET',1 11111111dataType:1'script',1 11111111data:1{1 1111111111search:1search,1 1111111111status:1status1 11111111}1 111111}).done(function1()1{111111111 11111111if1($("#access_points_location_map").is(":visible")){1 1111111111$('.center_map_on_marker').show();1 11111111}1 111111});1 1111}1 1 1111return1false;1 }1 1 $(document).on('keypress',1"#access_points1input#search",1function(e){1 11if(e.which1==113)1{1 1111apply_ap_filter($(this));1 11}1 1 });1 1 $(document).on('click',1".access_points_status1a",1function(event){1 1 11//!Check!the!number!of!active!filters! 11var1filters1=1$(".access_points_status1a.btnssuccess").length;1 11var1filter_status1=1parseInt($(this).attr('datasstatus'));1 11var1sensor_id1=1$(this).attr('datassensor');1 11var1status;1 11var1search1=1$("#access_points1#search");1 11var1searchValue1=1search.val();1 1 11//!Check!the!status!of!the!button! 11if($(this).hasClass('btnssuccess')){1 1 1111$(this).removeClass('btnssuccess');1 1 1111if1(filters1==11){1 1 111111//!Check!if!we!have!some!search!term! 111111if(searchValue.length1>10){1 11111111//!Search!by!the!term! 11111111var1sensor_id1=1searchBox.data('sensorsid');1 11111111if1(filter_status1===13)1 1111111111var1url1=1"/sensors/"1+1sensor_id1+1'/search_unallocated_aps';1 11111111else1 1111111111var1url1=1"/sensors/"1+1sensor_id1+1'/search_aps';1 1 11111111$("#access_points_offset").css('display',1'none');1 1 11111111//!Save!the!current!table! 11111111$("#access_points1table1tbody1tr").each(function(index){1 1111111111if($(this).hasClass('ap_from_status')){1 111111111111$(this).remove();1 1111111111}1else1{1 111111111111$(this).addClass('hidden');1 1111111111}1 11111111});1 1
11111111show_loading();1 1 11111111$.ajax({1 1111111111url:1url,1 1111111111remote:1true,1 1111111111type:1'GET',1 1111111111dataType:1'script',1 1111111111data:1{1 111111111111search:1searchValue1 1111111111}1 11111111});1 1 111111}1else1{1 11111111//!Show!the!saved!table! 11111111$("#access_points1table1tbody1tr").each(function(index){1 1111111111if($(this).hasClass('hidden')){1 111111111111$(this).removeClass('hidden')1 1111111111}1else1{1 111111111111$(this).remove()1 1111111111}1 11111111});1 11111111$('#access_points_status_offset').remove();1 11111111$("#access_points_offset").css('display',1'block');1 111111}1 1 1111}1else1{1 111111$("#access_points_status_offset").css('display',1'none');1 1 111111//!Delete!the!rows!with!the!status!pulsed! 111111$("#access_points1table1tbody1tr.ap_from_status").remove()1 1 111111//!Get!status!filters! 111111status1=1get_status_filters();1 1 111111if1(searchValue1!=1undefined1&&1searchValue1!=1''){1 11111111$.getScript('/sensors/'1+1sensor_id1+1'/access_points/'1+1status1+1 '/status?search='1+1searchValue);1 111111}1else1{1 11111111$.getScript('/sensors/'1+1sensor_id1+1'/access_points/'1+1status1+1 '/status');1 111111}1 1 111111show_loading();1 1111}1 1 11}1else1{1 1 1111$("#access_points_offset").css('display',1'none');1 11111 1111$(this).addClass('btnssuccess')1 1 1111//!Save!the!current!table! 1111$("#access_points1table1tbody1tr").each(function(index){1 111111if($(this).hasClass('ap_from_status')){1 11111111$(this).remove();1 111111}1else1{1 11111111$(this).addClass('hidden');1 111111}1 1111});1 1 1111//!Get!status!filters! 1111status1=1get_status_filters();1
88 1 1111if1(search1!=1undefined1&&1search1!=1''){1 111111$.getScript('/sensors/'1+1sensor_id1+1'/access_points/'1+1status1+1 '/status?search='1+1search.val());1 1111}1else1{1 111111$.getScript('/sensors/'1+1sensor_id1+1'/access_points/'1+1status1+1'/status');1 1111}1 1 1111show_loading();1 1 11}1 1 11return1false;1 1 })1 1 /*! *!Get!the!current!active!filters! */1 function1get_status_filters()1{1 11//!Status! 11var1status1=1[];1 11//!Get!active!status! 11$('.access_points_status1a.btnssuccess').each(function(index){1 1111status.push($(this).attr('datasstatus'));1 11});1 11//!Return!status! 11return1status.join();1 }1 1 /*! *!Show!loading!in!the!table! */1 function1show_loading(){1 11$("#access_points1table1tbody").append("<tr1class='loading'><td1colspan='8'><i1 class='fa1fasrefresh1fasspin'></i>1Loading</td></tr>");1 }1 1 $(document).on('mouseenter',1"#access_points1tbody1tr",1function1(e)1{1 11var1id1=1$(this).attr('datasaccessspointsid')1 1 11if1(redBorder.currentAPs1!==1undefined)1{1 1111redBorder.currentAPs.highlight(id);1 11}1 });1 1 $(document).on('mouseenter',1"#gps_markers1tbody1tr",1function1(e)1{1 11var1id1=1$(this).attr('datasgpssmarkersid')1 1 11if1(redBorder.currentAPs1!==1undefined)1{1 1111redBorder.currentAPs.highlight(id);1 11}1 });1 1 $(document).on('mouseout',1"div#access_points",1function1(e)1{1 11if1(redBorder.currentAPs1!==1undefined)1{1 1111redBorder.currentAPs.highlight_by_names_and_types('ap');1 11}1 });1 1
$(document).on('click',1"li#access_points1a",1function1(e)1{111 11if1(redBorder.currentAPs1!==1undefined)1{1 1111redBorder.currentAPs.highlight_by_names_and_types('ap');1 11}1 });1 1 $(document).on('mouseout',1"div#gps_markers",1function1(e)1{111 11if1(redBorder.currentAPs1!==1undefined)1{1 1111redBorder.currentAPs.highlight_by_names_and_types('gps_markers');1 11}1 });1 1 $(document).on('click',1"li#gps_markers1a",1function1(e)1{111 11if1(redBorder.currentAPs1!==1undefined)1{1 1111redBorder.currentAPs.highlight_by_names_and_types('gps_markers');1 11}1 });1 1 $(document).on('click',1"tr[datasaccessspointsid]",1function1(e)1{1 11if1(e.shiftKey)1{1 1111var1selectedSerie1=1redBorder.lastSerieSelected;1 1 1111if1(selectedSerie1!==1undefined)1{1 111111var1selectedSeriesIndex1=1selectedSerie.index();1 111111var1myIndex1=1$(this).index();1 111111var1firstElement,1lastElement;1 1 111111if1(selectedSeriesIndex1<1myIndex)1{1 11111111firstElement1=1selectedSerie;1 11111111lastElement1=1$(this);1 111111}1else1if1(selectedSeriesIndex1>1myIndex)1{1 11111111firstElement1=1$(this);1 11111111lastElement1=1selectedSerie;1 111111}1 1 111111if1(firstElement1!==1undefined)1{1 11111111var1series1=1firstElement.nextUntil(lastElement);1 11111111series.click();1 111111}1 1111}1 11}1 11var1name1=1$(this).find('td[dataslabel="Name"]').html();1 11$(this).toggleClass('selected');1 11redBorder.lastSerieSelected1=1$(this);1 1 11if1(redBorder.currentAPs1!==1undefined)1{1 1111redBorder.currentAPs.highlight_by_names_and_types('ap');1 11}1 1 11if($('#access_points1table1tbody1tr.selected').length1>10){1 1 1111var1current_zones1=1[]1 1111var1ids1=1[];1 1 1 1111//!Load!the!selected!zones!to!add!to!remove!menu! 1111$('#access_points1table1tbody1tr.selected').each(function(index){1 111111var1items1=1[];1 1 111111$(this).find('td[dataslabel="Zones"]1span').each(function(index){1 11111111var1id1=1$(this).attr('dataszonesid')1 1
96 11redBorder.gps_markers_json1=1gps_markers;1 11redBorder.show_gps_markers1=1show_gps_markers;1 1 11show_floor_image();1 }1 1 function1show_floor_map(draggable)1{1 11if1(draggable1===1undefined)1draggable1=1false;1 11var1self1=1$('#access_points_location_map');1 11$(".center_map_on_marker").show();1 11$('.dropzone').hide();1 11$('#access_points_location_map').show();1 11$('#access_points_location_image').hide();1 1 11if1(self.data('loaded'))1return;1 11else1self.data('loaded',1true);1 1 11try1{1 1111redBorder.access_points_map1=1redBorder.map({1 111111container:1'access_points_location_map',1 111111draggableCursor:1'crosshair',1 111111clustered:1true,1 111111contextMenu:1true,1 111111scrollwheel:1false,1 111111fullScreen:1true1 1111});1 1 1111redBorder.aps_json.forEach(function1(ap)1{1 111111if(ap.longitude1!=1undefined1&&1ap.latitude1!=1undefined){1 11111111var1color,1tooltip;1 1 11111111tooltip1=1"<b>Name:</b>1"1+1ap.name1+1"<br1/>"1+1 1111111111"<b>MAC1Address:</b>1"1+11ap.mac_address1+1"<br1/>"1+1 1111111111"<b>Latitude:</b>1"1+1Math.round(ap.latitude1*110000)1/1100001+1"<br1/>"1+1 1111111111"<b>Longitude:</b>1"1+1Math.round(ap.longitude1*110000)1/110000;11111111111 1 11111111if1(ap.status1===11)1color1=1'green';1 11111111else1if1(ap.status1===12)1color1=1'red';1 11111111else1color1=1'orange';1 1 11111111redBorder.access_points_map.addMarker(ap.latitude,1ap.longitude,1{1 1111111111content:1tooltip,1 1111111111filterWhenClick:1true,1 1111111111markerId:1ap.id,1 1111111111draggable:1draggable,1 1111111111dragEndCallback:1function(marker,1event){11 111111111111var1url1=1"/sensors/access_point/"1+1ap.id1 111111111111$.ajax({1 11111111111111url:1url,1 11111111111111remote:1false,1 11111111111111type:1'POST',1111111 11111111111111dataType:1'json',1 11111111111111data:1{1 1111111111111111access_point:1{1 111111111111111111latitude:1event.latLng.lat(),1 111111111111111111longitude:1event.latLng.lng()1 1111111111111111},1 1111111111111111relocate:1true1 11111111111111}1 111111111111}).done(function(relocated_ap){1
11111111111111$('.rbsaps.zonesap[datasrbsapssid='+1relocated_ap.id1+']').remove();1 11111111111111if1(redBorder.currentAPs1!==1undefined)1{1 1111111111111111redBorder.currentAPs.add_aps([relocated_ap]);1 11111111111111}1 111111111111});1 1111111111},1 1111111111icon:1{1 111111111111path:1google.maps.SymbolPath.CIRCLE,1 111111111111scale:112,1 111111111111strokeWeight:11.3,1 111111111111strokeColor:1'dark'+1color,1 111111111111fillColor:1color,1 111111111111fillOpacity:1.851 1111111111}1 11111111});1 111111}1 1111});1 11}1catch1(err)1{1 1111console.log('Floor1map1'1+1err);1 11}1 }1 1 function1show_floor_image()1{1 11$(".center_map_on_marker").hide();1 11var1self1=1$('#access_points_location_image');1 1 11$('#access_points_location_map').hide();1 1 11//!Check!if!the!image!of!the!floor!exist! 11if($('#access_points_location_image').html()1==1''){1 1111$('.dropzone').show();1 11}1else1{1 1111$('#access_points_location_image').show();1 1 1111if1(self.data('loaded'))1return;1 1111else1self.data('loaded',1true);1 1 1111redBorder.currentAPs1=1redBorder.aps({1 111111show_gps_markers:1redBorder.show_gps_markers1 1111});1 1 1111if1(redBorder.currentAPs1!==1undefined)1{1 111111redBorder.currentAPs.add_aps(redBorder.aps_json);1 111111redBorder.currentAPs.add_gps_markers(redBorder.gps_markers_json);1 1111}1 11}1 }1 1 $(document).on('click',1'body.sensors1li[datasrole="show_map_no_aps"]',1function1(e)1 {1 11var1self1=1$(this),1 111111status1=1self.data('status');1 1 11if1(status1===1'enabled')1{1 1111self.data('status',1'disabled');1 1111self.find('a').html('<i1class="fa1fasphoto"></i>1Show1Map');1 1111$('.dropzone').show();1 1111$('#access_points_location_map').hide();1 11}1else1if1(status1===1'disabled')1{1 1111self.data('status',1'enabled');1 1111self.find('a').html('<i1class="fa1fasphoto"></i>1Show1Image');1 1111$('.dropzone').hide();1
98 1111$('#access_points_location_map').show();1 11}1 1 11return1false;1 });1 1 function1initRedBorderMapAPs(aps,1draggable)1{1 11if1(draggable1===1undefined)1draggable1=1false;1 11aps.forEach(function1(element)1{1 1111element.lat1=1element.latitude;1 1111element.lng1=1element.longitude;1 11});1 1 11redBorder.aps_json1=1aps;1 111 11show_floor_map();1 11$('.dropzone').hide();1 }1 1 function1show_wlc_map(draggable)1{111 11$("#access_points_3d_container").hide();1 11if1(draggable1===1undefined)1draggable1=1false;1 11var1self1=1$('#access_points_location_map');1 1 11$('#access_points_location_map').show();1 1 11if1(self.data('loaded'))1return;1 11else1self.data('loaded',1true);1 1 11try1{1 1111redBorder.access_points_map1=1redBorder.map({1 111111container:1'access_points_location_map',1 111111draggableCursor:1'crosshair',1 111111clustered:1true,1 111111contextMenu:1true,1 111111scrollwheel:1false,1 111111fullScreen:1true1 1111});1 1 1111redBorder.aps_json.forEach(function1(ap)1{1 111111if(ap.longitude1!=1undefined1&&1ap.latitude1!=1undefined){1 11111111var1color,1tooltip;1 1 11111111tooltip1=1"<b>Name:</b>1"1+1ap.name1+1"<br1/>"1+1 1111111111"<b>MAC1Address:</b>1"1+11ap.mac_address1+1"<br1/>"1+1 1111111111"<b>Latitude:</b>1"1+1Math.round(ap.latitude1*110000)1/1100001+1"<br1/>"1+1 1111111111"<b>Longitude:</b>1"1+1Math.round(ap.longitude1*110000)1/1100001+1"<br1/>"111111111111111111111 1 11111111if1(ap.status1===11)1color1=1'green';1 11111111else1if1(ap.status1===12)1color1=1'red';1 11111111else1color1=1'orange';1 1 11111111redBorder.access_points_map.addMarker(ap.latitude,1ap.longitude,1{1 1111111111content:1tooltip,1 1111111111filterWhenClick:1true,1 1111111111markerId:1ap.id,1 1111111111draggable:1draggable,1 1111111111dragEndCallback:1function(marker,1event){1111111111111 111111111111var1url1=1"/sensors/access_point/"1+1ap.id1 111111111111$.ajax({1
11111111111111url:1url,1 11111111111111remote:1false,1 11111111111111type:1'POST',1111111 11111111111111dataType:1'json',1 11111111111111data:1{1 1111111111111111access_point:1{1 111111111111111111latitude:1event.latLng.lat(),1 111111111111111111longitude:1event.latLng.lng()1 1111111111111111},1 1111111111111111relocate:1true1 11111111111111}1 111111111111});1 1111111111},1 1111111111icon:1{1 111111111111path:1google.maps.SymbolPath.CIRCLE,1 111111111111scale:112,1 111111111111strokeWeight:11.3,1 111111111111strokeColor:1'dark'+1color,1 111111111111fillColor:1color,1 111111111111fillOpacity:1.851 1111111111}1 11111111});1 111111}1 1111});1 11}1catch1(err)1{1 1111console.log('Floor1map1'1+1err);1 11}1 }1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
100 1 1 View_1.html.erbC <%=1title1([email protected]_root?1 11111111111111'Access1Points'1 [email protected]?1 11111111111111"Edit1#{@sensor.full_name}"1 111111111111else1 11111111111111"Info1from1#{@sensor.full_name}"1 111111111111end)1do1%>1 1 11<%[email protected]_root?1||[email protected]?1%>11111111111 1111<li>1 111111<%=1link_to1content_tag(:i,1nil,1class:1"fa1fasedit")1+1"Edit1 #{@sensor.full_name}",1edit_sensor_path(@sensor)1%>11 1111</li>1 11<%1end1%>1 11<li1datasstep="1"1datasintro="Click1here1to1see1more1options.1Export/delete1access1 points,1delete1image...">1 1111<%=1link_to1"#",1'datastoggle'1=>1"dropdown",1class:1"dropdownstoggle"1do1%>1 111111<%=1content_tag(:i,1nil,1class:1"fa1fasth")1%>1 111111Options1 111111<%=1content_tag(:span,1nil,1class:1"caret")1%>1 1111<%1end1%>1 1111<ul1class="dropdownsmenu">1 111111<%1if1can?(:manage,1@sensor)1&&1(@sensor.is_flow?1||[email protected]?)1%>1 11111111<li><%=1link_to1"Autoposition1Access1Points",1 geopositionate_aps_sensor_path(@sensor),1data:1{1confirm:1"Are1you1sure?1This1action1 will1modify1the1position1of1all1access1points1associated1to1this1floor."1}1%></li>1 111111<%1end1%>1 111111<li><%=1link_to1"Export1Access1Points",1export_aps_sensor_path(@sensor),1class:1 'export_objects'1%></li>1 111111<%1if1can?(:manage,1@sensor)1&&1(@sensor.is_flow?1||[email protected]?)1%>1 11111111<li><%=1link_to1"Import1Access1Points",1import_aps_sensor_path(@sensor),1 'datastoggle'1=>1"modal",1'datastarget'1=>1"#import_aps_modal",1"datasbackdrop"1=>1 "static"1%></li>1 111111<%1end1%>1 111111<%[email protected]?1%>1 11111111<%[email protected]_points.any?1%>1 1111111111<li1datasrole="show_gps_markers"1datasstatus="disabled"><%=1link_to1"Show1 GPS1Markers",1"#"1%></li>1 11111111<%1else1%>1 1111111111<li1datasrole="show_gps_markers"1datasstatus="enabled"><%=1link_to1"Hide1 GPS1Markers",1"#"1%></li>1 11111111<%1end1%>1 111111<%1end1%>1 111111<li>1 11111111<%=1link_to1'Delete1All1Access1Points',1 delete_all_access_points_sensor_path(@sensor),1:method1=>1:delete,1data:1{1confirm:1 "Are1you1sure?"1},1class:1"menusdivider"1%>1 111111</li>1 111111<%[email protected]_map1%>1 11111111<li>1 1111111111<%=1link_to1"Delete1Image",1image_sensor_path(@sensor),1method:1:delete,1 class:1'delete_image_button'1%>11 11111111</li>1
111111<%1end1%>1 1111</ul>1 11</li>1 11<%1if1(current_user.can?1:manage,1current_user.sensor)1&&1[email protected]?1%>1 1111<li1datasstep="2"1datasintro="Click1here1to1add1a1new1module1sensor.">1 111111<%=1link_to1"#",1'datastoggle'1=>1"dropdown",1class:1"dropdownstoggle"1do1%>1 11111111<%=1content_tag1:i,1nil,1class:1"fa1fasplus"1%>1 11111111Add1Sensors1 11111111<%=1content_tag(:span,1nil,1class:1"caret")1%>1 111111<%1end1%>1 111111<ul1class="dropdownsmenu">1 11111111<%1menu_items_for(:sensor_tree_menu)1do1|node|1%>1 1111111111<li><%=1render_menu_node(node,1@sensor)1%></li>1 11111111<%1end1%>1 111111</ul>1 1111</li>1 1111<li1datasstep="3"1datasintro="Click1here1to1add1a1new1domain.">1 111111<%=1render1partial:1'add_domain'1%>1 1111</li>1 11<%1end1%>1 11<li1datasstep="4"1datasintro="Click1here1to1change1the1sensor1view.1Tree,1map,1 tiles,1modules1sensors...">1 1111<%=1link_to1"#",1'datastoggle'1=>1"dropdown",1class:1"dropdownstoggle"1do1%>1 111111<%=1content_tag1:i,1nil,1class:1"fa1faslist"1%>1 111111View1 111111<%=1content_tag(:span,1nil,1class:1"caret")1%>1 1111<%1end1%>1 1111<ul1class="dropdownsmenu">1 111111<%1menu_items_for(:sensors_view_menu_alt)1do1|node|1%>1 11111111<li><%=1render_menu_node(node,1@sensor,1session[:sensor_view])1%></li>1 111111<%1end1%>1 1111</ul>1 11</li>1 <%1end1%>1 1 <%=1sensor_breadcrumb(@sensor,1'view_sensor_path',1false,1home:1{1name:1'Sensors',1 url:1sensors_path1})1%>1 1 <%=1render1partial:1'sensor_access_points_tabs',1locals:1{1sensor:1@sensor1}1%>1 1 <article1id="access_points_from_sensor"1datassensor="1<%[email protected]%>"1datas draggable="<%=1current_user.admin1%>">1 11<div1class="row">1 1111<div1class="colsmds12"1datasstep="5"1datasintro="This1table1shows1the1sensor1list1 in1a1mosaic1way.1You1can1access1them1by1clicking1on1any1of1the1tiles."1datas position="top">1 111111<%=1render1partial:1'access_points_3d'1%>1 111111<%=1render1partial:1'sensors_children_view'1%>1111111111111 111111<%[email protected]?1%>1 11111111<%=1render1partial:1'access_points_location'1%>1 11111111<%[email protected](domain_type:1Sensor::FLOOR).count1>111%>1 1111111111<%=1render1partial:1'floor_switch'1%>1 11111111<%1end1%>1 111111<%1else1%>1 11111111<%=1render1partial:1'access_points_wlc_location'1%>1 111111<%1end1%>1 1111</div>1 11</div>1 11<div1class="row">1 1111<div1class="colsmds12"1datasstep="6"1datasintro="This1table1shows1some1data1 related1to1the1sensor1selected.1Access1points,1zones..."1datasposition="top">1 111111<%[email protected]?1%>1
102 11111111<ul1class="nav1navstabs1sensorsviewstabs">1 1111111111<li1id="access_points"1class="active"><%=1link_to1"Access1Points",1 "#access_points",1:'datastoggle'1=>1"tab"1%></li>1 1111111111<li1id="gps_markers"><%=1link_to1"GPS1Markers",1"#gps_markers",1:'datas toggle'1=>1"tab"1%></li>1 1111111111<li1id="zones"><%=1link_to1"Zones",1"#zones",1:'datastoggle'1=>1"tab"1 %></li>1 1111111111<%1if1current_user.admin?1%>1 111111111111<li1id="edit"><%=1link_to1"Edit",1"#edit",1:'datastoggle'1=>1"tab"1 %></li>1 1111111111<%1end1%>1 11111111</ul>1 1 11111111<div1class="tabscontent">1 1111111111<div1class="tabspane1fade1in1active"1id="access_points">1 111111111111<%=1render1partial:1'access_points'1%>1 1111111111</div>1 1111111111<div1class="tabspane1fade"1id="gps_markers">1 111111111111<%=1render1partial:1'gps_markers'1%>1 1111111111</div>1 1111111111<div1class="tabspane1fade"1id="zones">1 111111111111<%=1render1partial:1'zones'1%>1 1111111111</div>1 1111111111<%1if1current_user.admin?1%>1 111111111111<div1class="tabspane1fade"1id="edit">1 11111111111111<%=1render1partial:1'edit_floor'1%>1 111111111111</div>1 1111111111<%1end1%>1 11111111</div>1 1 111111<%1else1%>1 11111111<ul1class="nav1navstabs1sensorsviewstabs">1 1111111111<li1id="access_points"1class="active"><%=1link_to1"Access1Points",1 "#access_points",1:'datastoggle'1=>1"tab"1%></li>1 1111111111<li1id="zones"><%=1link_to1"Zones",1"#zones",1:'datastoggle'1=>1"tab"1 %></li>1 11111111</ul>1 1 11111111<div1class="tabscontent">1 1111111111<div1class="tabspane1fade1in1active"1id="access_points">1 111111111111<%=1render1partial:1'access_points'1%>1 1111111111</div>1 1111111111<div1class="tabspane1fade"1id="zones">1 111111111111<%=1render1partial:1'zones'1%>1 1111111111</div>1 11111111</div>1 111111<%1end1%>1 1111</div>1 11</div>1 </article>1 1 <%1content_for1:modals1do1%>1 11<%#1Create1modals1%>1 11<%=1create_edit_modals1['ap',1'gps',1'zone',1'locate_ap']1%>1 11<%=1create_modals1['import_aps_modal',1'new_domain_modal',1['new_gps_marker_modal',1 'xlarge'],1['edit_gps_marker_modal',1'xlarge'],1'new_sensor_modal']1%>1 <%1end1%>1
view_5.html.erb <%=1title1"Info1from1#{@sensor.full_name}"1do1%>1 11<%[email protected]_root?1%>1 1111<li1datasrole="show_wlc_floors"1datasstatus="enabled"11 1111datasdraggable="<%=1current_user.admin?1%>">1 111111<%=1link_to1"#"1do1%>1 11111111<%=1content_tag(:i,1nil,1class:1"fa1fasphoto",1style:1"display:1none")1%>1 11111111<img1src="<%=asset_path('icons/floor.svg')%>"1class="customsicon"/>1 11111111<span>Show1Floors</span>1 111111<%1end1%>1 1111</li>1111111 1111<li1datasstep="1"1datasintro="Click1here1to1edit1this1sensor.">1 111111<%=1link_to1content_tag(:i,1nil,1class:1"fa1fasedit")1+1"Edit1 #{@sensor.full_name}",1edit_sensor_path(@sensor)1%>11 1111</li>1 11<%1end1%>1 11<li1datasstep="2"1datasintro="Click1here1to1see1more1options.1Import,1export1or1 delete1access1points.">1 1111<%=1link_to1"#",1'datastoggle'1=>1"dropdown",1class:1"dropdownstoggle"1do1%>1 111111<%=1content_tag(:i,1nil,1class:1"fa1fasth")1%>1 111111Options1 111111<%=1content_tag(:span,1nil,1class:1"caret")1%>1 1111<%1end1%>1 1111<ul1class="dropdownsmenu">1 111111<li><%=1link_to1"Export1Access1Points",1export_aps_sensor_path(@sensor)1%></li>1 111111<%1if1can?(:manage,1@sensor)1&&1(@sensor.is_flow?1||1(@sensor.is_domain?1&&1 @sensor.domain_type1==1Sensor::FLOOR))1%>1 11111111<li><%=1link_to1"Import1Access1Points",1import_aps_sensor_path(@sensor),1 'datastoggle'1=>1"modal",1'datastarget'1=>1"#import_aps_modal",1"datasbackdrop"1=>1 "static"1%></li>1 111111<%1end1%>1 111111<li>1 11111111<%=1link_to1'Delete1All1Access1Points',1 delete_all_access_points_sensor_path(@sensor),1:method1=>1:delete,1data:1{1confirm:1 "Are1you1sure?"1},1class:1"menusdivider"1%>1 111111</li>1 111111<li>1 11111111<%=1link_to1'Save1Map1as1Sensor1Image',1class:1"menusdivider"1%>1 111111</li>1 1111</ul>1 11</li>1 <%1end1%>1 1 <%=1sensor_breadcrumb(@sensor,1'view_sensor_path',1false,1home:1{1name:1'Sensors',1 url:1sensors_path1})1%>1 1 <article>1 11<div1class="rowsfluid">1 1111<div1class="accordion1span12">1111111 111111<%=1render1partial:1'access_points_mosaic'1%>1 111111<%=1render1partial:1'access_points_wlc_location'1%>1 111111<div1datasstep="4"1datasintro="This1table1shows1some1data1related1to1the1sensor1 selected.1Access1points..."1datasposition="top">1 11111111<%=1render1partial:1'access_points'1%>1 111111</div>1
104 1111</div>1 11</div>1 </article>1 1 <%1content_for1:modals1do1%>1 11<%=1create_modals1['import_aps_modal',1'new_locate_ap_modal',1'edit_ap_modal',1 'new_ap_modal',1'edit_gps_marker_modal',1'new_gps_marker_modal']1%>1 <%1end1%>1 Access_points.html.erbC <div1id="access_points"1class="row">1 11<div1class="colsmds121colsmarginstl">1 1111<%1if1@access_points.empty?1%>11111 111111<div1class="alert1alertswarning">1 111111111There1are1no1access1points1defined1on1this1location.1 111111</div>1 111111<div1class="clearfix1marginsbm1hidden">1 1111<%1else1%>1 111111<div1class="clearfix1marginsbm">1 1111<%1end1%>1111111 11111111<input1id="search"1name="search"1type="text"1class="formscontrol1formsfluids large1pullsleft"1placeholder="Search"1value="<%=1@search1%>"1datassensorsid="<%=1 @sensor.id1%>"></input>1 11111111<%1if1current_user.admin1%>1 1111111111<div1class="editszonesselector1assignsfloors1dropdown1pullsleft1class1 hidden">1 111111111111<%=1link_to1"#",1:'datastoggle'1=>1"dropdown",1:class1=>1"btn1btnssmall1 dropdownstoggle"1do1%>1 11111111111111<%=1content_tag(:i,1nil,1class:1"fa1fasedit")1%>1Edit1zones1 11111111111111<%=1content_tag(:span,1nil,1class:1"caret")1%>1 111111111111<%1end1%>1111 111111111111<%=1render1partial:1'dropdown_edit_zones'1%>111111 1111111111</div>1 1111111111<%[email protected]?1%>1 111111111111<div1class="editszonesselector1dropdown1pullsleft1class1hidden">1 11111111111111<%=1link_to1"#",1:'datastoggle'1=>1"dropdown",1:class1=>1"btn1btnssmall1 dropdownstoggle"1do1%>1 1111111111111111<%=1image_tag('icons/floor.svg',1class:1'customsicon')1%>1Assign1 floor1 1111111111111111<%=1content_tag(:span,1nil,1class:1"caret")1%>1 11111111111111<%1end1%>1111 11111111111111<%=1render1partial:1'dropdown_assign_floor'1%>1 111111111111</div>1 1111111111<%1end1%>1 11111111<%1end1%>1 11111111<div1class="access_points_status1pullsright">1 1111111111<%=1link_to1"<i1class=\"fa1fascircle\"1style=\"color:1 #{status_to_color(1)};1marginsright:15px;\"></i><span>Up1 (#{@sensor.access_points_group_by_status(AccessPoint::STATUS[:UP])})</span>".html_saf e,1"#",1id:1"status_1",1class:1"btn1btnssmall",1data:1{status:11,1sensor:[email protected],1 count:[email protected]_points_group_by_status(AccessPoint::STATUS[:UP])}1%>1 1111111111<%=1link_to1"<i1class=\"fa1fascircle\"1style=\"color:1 #{status_to_color(2)};1marginsright:15px;\"></i><span>Down1 (#{@sensor.access_points_group_by_status(AccessPoint::STATUS[:DOWN])})</span>".html_s afe,1"#",1id:1"status_2",1class:1"btn1btnssmall",1data:1{status:12,1sensor:1 @sensor.id,1couunt:1 @sensor.access_points_group_by_status(AccessPoint::STATUS[:DOWN])}1%>1 1111111111<%=1link_to1"<i1class=\"fa1fascircle\"1style=\"color:1
#{status_to_color(0)};1marginsright:15px;\"></i><span>Unknown1 (#{@sensor.access_points_group_by_status(AccessPoint::STATUS[:UNKNOWN])})</span>".htm l_safe,1"#",1id:1"status_0",1class:1"btn1btnssmall",1data:1{status:10,1sensor:1 @sensor.id,1count:1 @sensor.access_points_group_by_status(AccessPoint::STATUS[:UNKNOWN])}1%>1 1111111111<%=1link_to1"<i1class=\"fa1fasquestionscircle\"1style=\"marginsright:1 5px;\"></i><span>Unallocated1 (#{@sensor.unallocated_access_points.count})</span>".html_safe,1"#",1id:1"status_3",1 class:1"btn1btnssmall",1data:1{status:13,1sensor:[email protected],1count:1 @sensor.unallocated_access_points.count}1%>1 11111111</div>1 111111</div>1 111111<%1if1@access_points.empty?1%>1 11111111<table1class="table1tablescondensed1tableshover1tablesstriped1hidden1tables fluid">1 111111<%1else1%>1 11111111<table1class="table1tablescondensed1tableshover1tablesstriped1tablesfluid1">1 111111<%1end1%>1 11111111<thead>1 1111111111<%=1render1partial:1"headers_ap",1locals:1{page:1@page}1%>1 11111111</thead>1 11111111<tbody>1 1111111111<%=1render1partial:1'sensors/access_point',1collection:1@access_points1%>1 11111111</tbody>1 111111</table>1 111111<%1unless1@access_points.nil?1||1@access_points.blank?1%>1 11111111<div1id='access_points_offset'1class='textscenter'>1 1111111111<%=1render1partial:1'access_points_offset',1locals:1{1page:1@page,1search:1 @search,1status:1@status1}1%>1 11111111</div>1 111111<%1end1%>1 11</div>1 </div>1 1 <scriptCtype="text/javascript">1 1 11$('div#access_points1table.table').tablesorter({1 1111headers:1{16:1{1sorter:1false1},117:1{1sorter:1false1}}1 11});1 1 </script>C Access_point.html.erb <tr1id="access_point_<%=1access_point.id1%>"1datasaccessspointsid="<%=1 access_point.id1%>"1<%=1"class=\"ap_from_status\"1datasaccessspoints status=\"#{access_point.status}\""
[email protected]?1%>>1 11<td1dataslabel='Name'1class="noselect"><%=1access_point.name1%></td>1 11<td1dataslabel='IP1Address'><%=1access_point.ip_address1%></td>1 11<td1dataslabel='MAC1Address'><%=1access_point.mac_address1%></td>1 11<td1dataslabel='Sensor'><%1if1access_point.switch1%>1 1111<%1if1@sensor1!=1access_point.switch1%>1 111111<%=1link_to1access_point.switch.name,1view_sensor_path(access_point.switch),1 class:1"btn1btnssmall"1%>1 1111<%1else1%>1 111111<%=1link_to1access_point.switch.name,1"#",1class:1"btn1btnssmall1disabled"1%>1 1111<%1end1%>1 11<%1end1%></td>1 11<td1dataslabel='Location'><%1if1access_point.sensor1%>1
112 New_locate_access_point.html.erb <div1class="modalsheader">111 11<button1type="button"1class="close"1datasdismiss="modal"><span1arias hidden="true">×</span><span1class="srsonly">Close</span></button>1 11<h31class="modalstitle">Locate1Access1Point</h3>1 </div>1 <%=1simple_form_for(@access_point,1as:1:access_point,1url:1 locate_access_point_sensor_path(@sensor),1html:1{1remote:1true,1method:1:post1})1do1 |f|1%>1 11<%[email protected]?1%>1 1111<div1class="modalsbody">1 111111<div1class="alert1alertsinfo">1 111111There1are1no1access1points1unallocated1 111111</div>1 1111</div>1 1 1111<div1class="modalsfooter">1 111111<%=1link_to1"Back",1"#",1:class1=>1"btn1btnssecondary",1:'datasdismiss'1=>1 "modal"1%>1 1111</div>1 11<%1else1%>1 1111<div1class="modalsbody">1 111111<%[email protected]?1&&
[email protected]?1%>1 11111111<div1class="alert1alertsinfo">1 1111111111There1isn't1any1Floor1for1this1location1 11111111</div>1111111 111111<%1end1%>1 11111111<div1class="row">1 11111111<div1class="colsmds12">1 1111111111<div1class="formsgroup1select1optional">1 111111111111<label1class="chosenscontainer">Access1Point</label>1 111111111111<%=1select_tag('access_point[id]',1options_for_select(@aps,1nil),1 multiple:1false,1class:1'chosensselect1formscontrol')1%>1 1111111111</div>1 1111111111<%[email protected]?1%>1 111111111111<div1class="formsgroup1select1optional">11111111111 11111111111111<label1class="chosenscontainer">Floor</label>1 11111111111111<%=1select_tag('access_point[sensor_id]',1 options_for_select([['Unnasigned',1nil]][email protected]{1|f|1[f.path_name,1f.id]1},1 nil),1multiple:1false,1class:1'chosensselect1formscontrol')1%>1 111111111111</div>1 1111111111<%1end1%>1 1111111111<%[email protected]_flow?1&&1[email protected]_meraki?1%>1111111111111 111111111111<div1class="formsgroup1select1optional1select_sensor_flow_1",1 style="display:1none">1 11111111111111<label1class="chosenscontainer">Flow1Sensor</label>1 11111111111111<%=1select_tag('access_point[switch_id]',1 options_for_select([['Unnasigned',1nil]][email protected]{1|f|1[f.path_name,1f.id]1},1 nil),1multiple:1false,1class:1'chosensselect1formscontrol',1disabled:1true)1%>1 111111111111</div>1 1111111111<%1end1%>1 1111111111<scriptCtype="text/javascript">1 111111111111initChosen();1 1111111111</script>1 11111111</div>1 111111</div>1 111111<div1class="row">1 11111111<div1class="colsmds6">1 1111111111<%1if1@access_point.latitude1&&1@access_point.longitude1%>1
111111111111<%=1f.input1:latitude,1input_html:1{1value:1 @access_point.latitude.round(5)1},1disabled:1true1%>1 1111111111<%1elsif1@access_point.x1&&1@access_point.y1%>1 111111111111<%=1f.input1:x,1input_html:1{1value:1@access_point.x.round(2)1},1 disabled:1true1%>1 1111111111<%1end1%>1 11111111</div>1 11111111<div1class="colsmds6">1 1111111111<%1if1@access_point.latitude1&&1@access_point.longitude1%>1 111111111111<%=1f.input1:longitude,1input_html:1{1value:1 @access_point.longitude.round(5)1},1disabled:1true%>1 1111111111<%1elsif1@access_point.x1&&1@access_point.y1%>1 111111111111<%=1f.input1:y,1input_html:1{1value:1@access_point.y.round(2)1},1 disabled:1true%>1 1111111111<%1end1%>1 11111111</div>1 111111</div>1 1111111 111111<%=1f.input1:description1%>1 111111<%=1f.input1:latitude,1input_html:1{1value:1@access_point.latitude1},1as:1 :hidden1%>1111111 111111<%=1f.input1:longitude,1input_html:1{1value:1@access_point.longitude1},1as:1 :hidden1%>1111111 111111<%=1f.input1:x,1input_html:1{1value:1@access_point.x1},1as:1:hidden1%>1111111 111111<%=1f.input1:y,1input_html:1{1value:1@access_point.y1},1as:1:hidden1%>1111111 1 1111</div>1 1111<div1class="modalsfooter">1 111111<%=1f.button1:submit,1:value1=>1"Locate1Access1Point",1:class1=>1"btn1btns success",1data:1{disable_with:1"Please1wait..."}1%>1 111111<%=1link_to1"Cancel",1"#",1:class1=>1"btn1btnssecondary",1:'datasdismiss'1=>1 "modal"1%>1 1111</div>11111 11<%1end1%>1 <%1end1%>1 Locate_access_point.js <%1if1@access_point.errors.any?1&&1@access_point.errors[:floor_or_switch].present?1%>111 11$('.need_select_floor_flow').remove()1 11var1error_msg1=11 1111"<div1class='alert1alertswarning1need_select_floor_flow'>You1need1to1select1at1 least1Floor1or1Flow1Sensor1for1selected1Access1Point</div>";1 11$('#new_locate_ap_modal1.modalsbody').prepend(error_msg);1 11$('#new_locate_ap_modal1.select_sensor_flow_').show();1 11$('select#access_point_sensor_id').parent().addClass('error');1 11$('select#access_point_switch_id').parent().addClass('error');1 11$('select#access_point_switch_id').prop('disabled',1 false).trigger('chosen:updated');1 1 11$(document).on('change',1'#access_point_id',1function()1{1 1111$('select#access_point_switch_id').prop('disabled',1 true).trigger('chosen:updated');1 1111$('#new_locate_ap_modal1.select_sensor_flow_').hide();1 1111$('select#access_point_sensor_id').parent().removeClass('error');1 1111$('.need_select_floor_flow').remove()1 11});1 11return1false1 <%1else1%>111
114 11$('#new_locate_ap_modal').modal('hide');111 11var1access_point1=1JSON.parse('<%=1@access_point.to_json.html_safe1%>'),1 111111color,1 111111tooltip,1 111111unallocated_aps1=1$('.access_points_status1a#status_3').data('count')1s11;1 1 11$('.access_points_status1a#status_3').data('count',1unallocated_aps);1 111 11tooltip1=1"<b>Name:</b>1"1+1access_point.name1+1"<br1/>"1+1 11"<b>MAC1Address:</b>1"1+11access_point.mac_address1+1"<br1/>"1+1 11"<b>Latitude:</b>1"1+1access_point.latitude1+1"<br1/>"1+1 11"<b>Longitude:</b>1"1+1access_point.longitude;1 1 11$('.access_points_status1a#status_3')1 1111.find('span').text("Unallocated1("1+1unallocated_aps1+1")");1 1 11if1(access_point.status1===11)1color1=1'green';1 11else1if1(access_point.status1===12)1color1=1'red';1 11else1color1=1'gray';1 11<%
[email protected]?1%>1 1111redBorder.currentAPs.add_aps([access_point]);1 11<%1end1%>1 11redBorder.access_points_map.addMarker('<%=1@access_point.latitude1%>',1'<%=1 @access_point.longitude1%>',1{1 1111content:1tooltip,1 1111filterWhenClick:1true,1 1111markerId:1access_point.id,1 1111draggable:1false,1 1111icon:1{1 111111path:1google.maps.SymbolPath.CIRCLE,1 111111scale:112,1 111111strokeWeight:11.3,1 111111strokeColor:1'dark'1+1color,1 111111fillColor:1color,1 111111fillOpacity:1.851 1111}1 11},1true);1 11111 11if1($('#access_point_'1+1access_point.id).find('a.center_map_on_marker').length1===1 0){11111 1111$('#access_point_'1+1access_point.id1+1'1.last')1 111111.prepend("<a1class='btn1center_map_on_marker'><i1class='fa1fasmaps marker'></i></a>");1 11}1else{1 1111$('#access_point_'1+1access_point.id1+1'1.center_map_on_marker').show();1 11}1 <%1end1%>1 delete_access_point.js $('table1tbody1tr#access_point_<%=1@ap_id1%>').remove();1 $('.rbsaps.zonesap[datasrbsapssid=<%=1@ap_id1%>]').remove();1 1 var1table_length1=1$('div#access_points1table1tbody1tr').length;1 if1(table_length1==10)1{1 11$('div#access_points1div.colsmds12').replaceWith('<div1class="colsmds121colsmargins tl"><div1class="alert1alertswarning">There1are1no1access1points1defined1on1this1 location.</div></div>');1 }1else1{1
11var1unknown_aps1=1<%
[email protected]_access_points.count1%>;1 11$('.access_points_status1a#status_0').data('count',1unknown_aps);1 11$('.access_points_status1a#status_0')1 1111.find('span').text("Unknown1("1+1unknown_aps1+1")");1 }1 redBorder.access_points_map.deleteMarker(<%=1@ap_id1%>)1 threeDimensionsFlors.js /*!!!!! !*!Copyright!(c)!2014!ENEO!TecnologÌa!S.L.! !*!This!file!is!part!of!redBorder.! !*!redBorder!is!free!software:!you!can!redistribute!it!and/or!modify! !*!it!under!the!terms!of!the!GNU!Affero!General!Public!License!as!published!by! !*!the!Free!Software!Foundation,!either!version!3!of!the!License,!or! !*!(at!your!option)!any!later!version.! !*!redBorder!is!distributed!in!the!hope!that!it!will!be!useful,! !*!but!WITHOUT!ANY!WARRANTY;!without!even!the!implied!warranty!of! !*!MERCHANTABILITY!or!FITNESS!FOR!A!PARTICULAR!PURPOSE.!!See!the! !*!GNU!Affero!General!Public!License!for!more!details.! !*!You!should!have!received!a!copy!of!the!GNU!Affero!General!Public!License! !*!along!with!redBorder.!If!not,!see!<http://www.gnu.org/licenses/>.! !*/1 1 redBorder.threeDimensionsFloors1=1function1(container,1data)1{111 1 11var1$container1=1$(container);1 1 11drawFloors(data,1$container);1 11initHandlers(data);1 1 11/*! !!*!Function!that!draws!the!floors!over!a!container!(!$selector!).!Receives!an!array! with!the!floor!data!and!the!container!element.! !!*!Also!sets!up!necessary!handlers!for!each!floor! !!*!@param!floorData!!An!array!with!the!floor!data! !!*!@param!$selector!!Container!in!which!the!floor!maps!will!be!drawn! !!*!*/1 11function1drawFloors(floorData,1$selector){1 1111$selector.append('<div1class="label"></div>');1 1111var1top_level1=10;1 11111 1111for1(var1i1=10;1i1<1floorData.length;1i++){1 111111var1floor1=1floorData[i],1 1111111111level_n1=10,1 1111111111$level_selector;1 1 111111if1(floor.level1!==1undefined){1 11111111level_n1=1floor.level;1 11111111if1(level_n1>1top_level)1top_level1=1level_n;1 111111}1 1 111111if1(1($level_selector1=1$selector.find('.level[dataslevel="'1+1level_n1+1 '"]')).length1==10){111//!level!container!is!unexistant,!create!it! 11111111$selector.append('<div1class="level"1dataslevel="'1+1level_n1+1'"></div>');1 11111111$level_selector1=1$selector.find('.level[dataslevel="'1+1level_n1+1'"]');1 111111}1 1 111111$level_selector.append('<a1class="blocksuisbut"1href="/sensors/'1+1floor.id1
116 +'/view"><div1class="floor"1style="bottom:1'1+1floor.sw[1][0]1+1'px;1left:1'1+1 floor.sw[0][0]1+1'px;1transformsorigin:1bottom1left;1transform:1rotate('1+1 floor.rotation1+1'deg)"'1+1''1+1 11111111111111111111111111111'datasid="'1+1floor.id1+1'"1datasparent="'1+1 floor.parent1+1'"1datastitle="'1+1floor.name1+1'"1datasup="'1+1floor.access_points.up1 +1'"1datasdown="'1+1floor.access_points.down1+1'"1'1+1 11111111111111111111111111111'datasunknown="'1+1floor.access_points.unknown1+1'"1 datasunallocated="'1+1floor.access_points.unallocated1+1'"1+1datasbottom="'1+1 floor.sw[1][0]+1'"1+1datasleft="'1+1floor.sw[0][0]1+1'"1>'1+1 111111111111111111111111111111'<img1src="'1+1floor.image_url1+1'"1alt=""1width="'1+1 floor.width1+1'"1height="'1+1floor.height1+1'"1/>'1+1 111111111111111111111111111111drawFloorsAccessPoints(floor)1+1 111111111111111111111111111111'</div></a>');1 1 1111}1 11111 1111$("#access_points_3d_container1.floor").hover(function(){1 111111$(this).css('zsindex',1'1');1 111111$("#access_points_3d_container1.floor").not(this).addClass("unfocused");1 111111 $('.access_points_circles').not($(this).parent().find('.access_points_circles')).addC lass("unfocused");1 111111showFloorInfoWindow('.info',1{1title:1$(this).data('title'),1content:1{1up:1 $(this).data('up'),1down:1$(this).data('down'),1unallocated:1 $(this).data('unallocated'),1unknown:1$(this).data('unknown')1}1});1 1111},1function(){1 111111$(this).css('zsindex',1'0');1 111111$("#access_points_3d_container1.floor").not(this).removeClass("unfocused");1 111111 $('.access_points_circles').not($(this).parent().find('.access_points_circles')).remo veClass("unfocused");1 111111hideFloorInfoWindow('.info');1 1111});1 1 1111$(".access_points_circles").hover(function(){1 111111showApInfoWindow('.infosap',1{1title:1$(this).data('name'),1content:1{ip:1 $(this).data('ipsaddress'),1mac:1$(this).data('macsaddress')1}1});1 1111},1function(){1 111111hideApInfoWindow('.infosap');1 1111});1 1111$container.parent().css("marginstop",1top_level1*11001+1"px");1 1111scatterLevels();1 11}1 1 11/*! !!*!Draws!the!access!points!of!a!floor!over!said!floor!in!it's!corresponding! positions.! !!*!@param!floor!!The!data!for!the!containing!floor! !!*!*/1 11function1drawFloorsAccessPoints(floor){1 1111var1aps1=1floor.access_points.aps,1 11111111result1=1'';1 1111for1(var1i1=10;1i1<1aps.length;1i++){1 111111var1ap1=1aps[i],1 1111111111status;1 1111111 111111if1(ap.status1===11)1status1=1'up';1 111111else1if1(ap.status1===12)1status1=1'down';1 111111else1status1=1'unknown';1 1
111111var1marker1=1'1\1 111111<i1datasname="'1+1ap.name1+1'"1datasipsaddress="'1+1ap.ip_address1+1'\1 111111"1datasmacsaddress="'1+1ap.mac_address1+1'"1class="access_points_circles1aps'1+1 status1+1'"1datasname="'1+1ap.name1+1'"style="1\1 111111position:1absolute;1top:1'1+1ap.y1+1'px;1\1 111111left:1'1+1ap.x1+1'px;1textsshadow:101015px1dark'1+1status1+1';"></i>';1 111111result1=1result1+1marker;1 1111}1 1111return1result;1 11}1 1 11/*! !!*!In!order!to!present!the!user!with!a!3D!effect!floor!maps!should!be!scattered!and! rotated!accordingly.! !!*!Each!new!level!adds!an!additional!40!px!separation.! !!*!Rotation!is!user!defined!via!GUI!controls.! !!*!@param!rotation!!User!defined!rotation!as!an!integer!expressed!on!degrees.! !!*!*/1 11function1scatterLevels(rotation){1 1111$(".level").each(1function(1index1){1 111111var1$floor1=1$(this).find(".floor");1 111111var1level1=1$(this).data("level");1 111111$floor.each(function1(index)1{1 11111111if1(rotation1!==1undefined){11111111111 1111111111if1(rotation1==1s901||1rotation1==1270){1 111111111111$(this).css("bottom",1parseFloat(1$(this).data("bottom"))1s1401*1level1);1 111111111111$(this).css("left",1parseFloat(1$(this).data("left"))1+1401*1level1);1 111111111111$('.topsdomain1img').css("boxsshadow",1"s5px1s5px115px1 rgba(33,33,33,0.5)");1 111111111111$('.topsdomain').css("boxsshadow",1"s20px1s20px1100px1 rgba(22,22,22,0.5)");1 1111111111}1else1if1(rotation1==11801||1rotation1==1s180){1 111111111111$(this).css("bottom",1parseFloat(1$(this).data("bottom"))1s1401*1level1);1 111111111111$(this).css("left",1parseFloat(1$(this).data("left"))1s1401*1level1);1 111111111111$('.topsdomain1img').css("boxsshadow",1"5px1s5px115px1 rgba(33,33,33,0.5)");1 111111111111$('.topsdomain').css("boxsshadow",1"20px1s20px1100px1 rgba(22,22,22,0.5)");1 1111111111}1else1if1(rotation1==1s2701||1rotation1==190){1 111111111111$(this).css("bottom",1parseFloat(1$(this).data("bottom"))1+1401*1level1);1 111111111111$(this).css("left",1parseFloat(1$(this).data("left"))1s1401*1level1);1 111111111111$('.topsdomain1img').css("boxsshadow",1"5px15px115px1 rgba(33,33,33,0.5)");1 111111111111$('.topsdomain').css("boxsshadow",1"20px120px1100px1rgba(22,22,22,0.5)");1 1111111111}1else{1 111111111111$(this).css("left",1parseFloat(1$(this).data("left")1)1+140*level1);1 111111111111$(this).css("bottom",1parseFloat(1$(this).data("bottom")1)1+140*level1);1 111111111111$('.topsdomain1img').css("boxsshadow",1"s5px15px115px1 rgba(33,33,33,0.5)");1 111111111111$('.topsdomain').css("boxsshadow",1"s20px120px1100px1 rgba(22,22,22,0.5)");1 1111111111}1 11111111}1else{1 1111111111$(this).css("left",1parseFloat(1$(this).data("left")1)1+140*level1);1 1111111111$(this).css("bottom",1parseFloat(1$(this).data("bottom")1)1+140*level1);1 11111111}1 111111});1 1111});1 11}1 1 11/*! !!*!Initializes!handlers!for!user!events!such!as!clicking!or!contextual!menus!
118 !!*!*/1 11function1initHandlers(){1 1111$(':not(.contextsmenu)').bind("click",1function1(event)1{1 111111$('.contextsmenu').remove();1 1111});1 1 1111$('#access_points_3d_container1.floor').bind("contextmenu",1function(event)1{1 111111event.preventDefault();1 111111$('.contextsmenu').remove();1 111111$('<div1class="contextsmenu">'1+1 11111111'<a1class="blocksuisbut"1href="/sensors/'1+1$(this).data("id")1+1 '/view"><li>Go1to1floor</li></a>'1+1 11111111'<a1class="blocksuisbut"1href="/sensors/'1+1$(this).data("parent")1 +'/view"><li>Go1to1domain</li></a>'1+1 111111'</div>')1 1111111111.appendTo('body')1 1111111111.css({1 111111111111position:1"absolute",1 111111111111top:1event.pageY1+1'px',1 111111111111left:1event.pageX1+1'px'1 1111111111});1 1111});1 1 1111$('.rotatesleft').bind("click",1function(event){1 111111rotation1=1$container.data('rotation');1 111111if1(rotation1==1s270)1rotation1=10;1 111111else1rotation1=1rotation1s190;1 111111$container.data('rotation',1rotation);1 111111$container.css("transform",1"rotate("+1rotation1+"deg)");1 111111scatterLevels(rotation);1111111 1111});1 1111$('.rotatesright').bind("click",1function(event){1 111111rotation1=1$container.data('rotation');1 111111if1(rotation1==1270)1rotation1=10;1 111111else1rotation1=1rotation1+190;1 111111$container.data('rotation',1rotation);1 111111$container.css("transform",1"rotate("+1rotation1+"deg)");1 111111scatterLevels(rotation);1111111 1111});1 11}1 1 11/*! !!*!Shows!and!fills!an!info!window!with!the!floor!data!when!the!user!hovers!over!any! floor.! !!*!@param!selector!!CSS!selector!for!the!info!window!container! !!*!@param!data!!!!!!Floor!data!used!to!fill!the!info!window! !!*!*/1 11function1showFloorInfoWindow(selector,1data){1 1111$info1=1$(selector);1 1111$info.find('.title').text(data.title);1 1111$info.find('[datasholder="up"]').text(data.content.up);1 1111$info.find('[datasholder="down"]').text(data.content.down);1 1111$info.find('[datasholder="unallocated"]').text(data.content.unallocated);1 1111$info.find('[datasholder="unknown"]').text(data.content.unknown);1 1111$info.fadeIn(100)1 11}1 1 11/*! !!*!Hides!previously!described!info!window! !!*!*/1
11function1hideFloorInfoWindow(selector){1 1111$(selector).hide();1 11}1 1 11/*! !!*!Shows!and!fills!an!info!window!with!an!access!point!information!when!the!user! hovers!over!it! !!*!@param!selector!!CSS!selector!for!the!info!window!container! !!*!@param!data!!!!!!Access!Point!data!used!to!fill!the!info!window! !!*!*/1 11function1showApInfoWindow(selector,1data){1 1111$info1=1$(selector);1 1111$info.find('.title').text(data.title);1 1111$info.find('[datasholder="ipsaddress"]').text(data.content.ip);1 1111$info.find('[datasholder="macsaddress"]').text(data.content.mac);1 1111$info.fadeIn(100)1 11}1 1 11/*! !!*!Hides!the!previously!described!AP!info!window! !!*!*/1 11function1hideApInfoWindow(selector){1 1111$(selector).hide();1 11}1 1 1 };1 1 functions.rb #######################################################################1 #!Copyright!(c)!2014!ENEO!Tecnologia!S.L.1 #!This!file!is!part!of!redBorder.1 #!redBorder!is!free!software:!you!can!redistribute!it!and/or!modify1 #!it!under!the!terms!of!the!GNU!Affero!General!Public!License!as!published!by1 #!the!Free!Software!Foundation,!either!version!3!of!the!License,!or1 #!(at!your!option)!any!later!version.1 #!redBorder!is!distributed!in!the!hope!that!it!will!be!useful,1 #!but!WITHOUT!ANY!WARRANTY;!without!even!the!implied!warranty!of1 #!MERCHANTABILITY!or!FITNESS!FOR!A!PARTICULAR!PURPOSE.!!See!the1 #!GNU!Affero!General!Public!License!for!more!details.1 #!You!should!have!received!a!copy!of!the!GNU!Affero!General!Public!License1 #!along!with!redBorder.!If!not,!see!<http://www.gnu.org/licenses/>.1 #######################################################################1 1 require1'matrix'1 1 module1PrimeInfrastructure1 11module1Functions1 1111#1 1111#!=>!Calculates!the!rotation!of!the!image,!the!points!where1 1111#!!!!the!image!should!be!placed!on!the!map!and!also!saves!the1 1111#!!!!image!as!an!overlay!map1 1111#1 1111#!!!!@param!dimensions!Array!with!width!and!height!of!the!image1 1111#!!!!in!that!order1 1111#1 1111#!!!!@param!gps!Array!with!GpsMarkers!objects1 1111#1
120 1111#!!!!Note:!The!axises!always!start!on!the!upper!left!corner.1 1111#1 1111def1self.overlay_data(dimensions,1gps)1 111111#!We!need!3!GPS!markers!and!width!and!height!to!get1 111111#!the!rotation!of!the!image!and!position!that!image1 111111#!on!an!overlay!map1 1 111111return1if1gps.length1<131||1dimensions.length1!=121 1 111111#!Parse!strings!to!floats1 111111#!gps.each!do!|gps_marker|1 111111#!!gps_marker.each!{!|k,!v|!gps_marker[k]!=!v.to_f!}1 111111#!end1 1 111111#!Lets!get!the!dimensions!of!the!map!in!feets1 111111width1=1dimensions[0]1 111111height1=1dimensions[1]1 1 111111#!We!use!these!matrixes!below!to!build!an!expression!that!lets!us1 111111#!transform!every!point!in!latblng!coordinates1 111111matrix1=1Matrix[1 11111111[gps[0].x,1gps[0].y,10,111111110,111111111,10],1 11111111[0,111111110,11111111gps[0].x,1gps[0].y,10,11],1 11111111[gps[1].x,1gps[1].y,10,111111110,111111111,10],1 11111111[0,111111110,11111111gps[1].x,1gps[1].y,10,11],1 11111111[gps[2].x,1gps[2].y,10,111111110,111111111,10],1 11111111[0,111111110,11111111gps[2].x,1gps[2].y,10,11]11111 111111]1 1 111111lat_lng_column1=1Matrix.column_vector(1 11111111[gps[0].longitude,1gps[0].latitude,1gps[1].longitude,1 111111111gps[1].latitude,1gps[2].longitude,1gps[2].latitude]1 111111)1 1 111111#!The!solution!of!the!system!above1 111111sol1=1matrix.inverse1*1lat_lng_column1 1111111 111111#!This!matrix!lets!us!transform!every!feet!point!in!latblng!points1 111111base_transf1=1Matrix[1 11111111[sol[0,10],1sol[1,10]],1 11111111[sol[2,10],1sol[3,10]]1 111111]1 1 111111#!The!difference!between!the!two!reference!systems1 111111diff1=1Matrix.column_vector([sol[4,10],1sol[5,10]])1 1 111111#!The!points!on!the!lngblat!system1 111111nw_lng_lat1=1point_to_lnglat(base_transf,1diff,1[0,10])1 111111ne_lng_lat1=1point_to_lnglat(base_transf,1diff,1[width,10])1 111111sw_lng_lat1=1point_to_lnglat(base_transf,1diff,1[0,1height])1 111111se_lng_lat1=1point_to_lnglat(base_transf,1diff,1[width,1height])11 111111diagonal_vector1=1ne_lng_lat1s1sw_lng_lat1 111111center1=1sw_lng_lat1+1diagonal_vector1/1121 1 111111#!The!vector!that!goes!north!to!their!image1 111111north_vector1=1ne_lng_lat1s1se_lng_lat1 1 111111#!The!image!rotation1 111111rotation_rad1=1Math.acos(north_vector[1,10]1/1Math.sqrt(north_vector[0,10]**21+1 north_vector[1,10]**2))1
111111rotation_deg1=1(north_vector[0,10]1/1north_vector[0,10].abs)1*1(rotation_rad1*1 1801/1Math::PI)1 1 111111#!If!GPS!points!are!wrong,!rotation_rad!could!be!NaN1 111111return1{rotation:10,1floor_ne:1"0.0,0.0",1floor_sw:1"0.0,0.0"}1if1 rotation_rad.nan?1 111111#!Lets!calculate!the!points!with!the!image!rotated1 111111#!Longitude!is!X,!and!Latitude!is!Y1 111111#!Northbeast!point1 111111ne_vector1=1center1s1ne_lng_lat1 111111r1=1Math.sqrt(ne_vector[0,10]**21+1ne_vector[1,10]**2)1 111111alpha1=1Math.atan2(ne_vector[1,10],1ne_vector[0,10])1 111111alpha21=1alpha1+1rotation_rad1*1(north_vector[0,10]1/1north_vector[0,10].abs)1 111111rot_ne_lng1=1(center[0,10]1s1r1*1Math.cos(alpha2)).to_f.round(6)1 111111rot_ne_lat1=1(center[11,10]1s1r1*1Math.sin(alpha2)).to_f.round(6)1 1 111111#!Northbwest!point1 111111nw_vector1=1center1s1nw_lng_lat1 111111r1=1Math.sqrt(nw_vector[0,10]**21+1nw_vector[1,10]**2)1 111111alpha1=1Math.atan2(nw_vector[1,10],1nw_vector[0,10])1 111111alpha21=1alpha1+1rotation_rad1*1(north_vector[0,10]1/1north_vector[0,10].abs)1 111111rot_nw_lng1=1(center[0,10]1s1r1*1Math.cos(alpha2)).to_f.round(6)1 111111rot_nw_lat1=1(center[11,10]1s1r1*1Math.sin(alpha2)).to_f.round(6)1 1 111111#!Southbwest!point1 111111sw_vector1=1center1s1sw_lng_lat1 111111r1=1Math.sqrt(sw_vector[0,10]**21+1sw_vector[1,10]**2)1 111111alpha1=1Math.atan2(sw_vector[1,10],1sw_vector[0,10])1 111111alpha21=1alpha1+1rotation_rad1*1(north_vector[0,10]1/1north_vector[0,10].abs)1 111111rot_sw_lng1=1(center[0,10]1s1r1*1Math.cos(alpha2)).to_f.round(6)1 111111rot_sw_lat1=1(center[1,10]1s1r1*1Math.sin(alpha2)).to_f.round(6)1 1 111111#!Southbeast!point1 111111se_vector1=1center1s1se_lng_lat1 111111r1=1Math.sqrt(se_vector[0,10]**21+1se_vector[1,10]**2)1 111111alpha1=1Math.atan2(se_vector[1,10],1se_vector[0,10])1 111111alpha21=1alpha1+1rotation_rad1*1(north_vector[0,10]1/1north_vector[0,10].abs)1 111111rot_se_lng1=1(center[0,10]1s1r1*1Math.cos(alpha2)).to_f.round(6)1 111111rot_se_lat1=1(center[1,10]1s1r1*1Math.sin(alpha2)).to_f.round(6)1 1 111111#!If!the!image!needs!to!be!rotated,!ne!and!sw!points!need!to!be!recalculated1 1 111111floor_ne1=1[1 11111111[rot_ne_lat,1rot_sw_lat,1rot_se_lat,1rot_nw_lat].max,1 11111111[rot_ne_lng,1rot_sw_lng,1rot_se_lng,1rot_nw_lng].max1 111111].join(',')1 1111111 111111floor_sw1=1[1 11111111[rot_ne_lat,1rot_sw_lat,1rot_se_lat,1rot_nw_lat].min,1 11111111[rot_ne_lng,1rot_sw_lng,1rot_se_lng,1rot_nw_lng].min1 111111].join(',')1 1 111111{1 11111111floor_ne:1floor_ne,1 11111111floor_sw:1floor_sw,1 11111111rotation:1rotation_deg,1 11111111matrix_trans:1base_transf.to_a,1 11111111matrix_offset:1diff.to_a1 111111}1 1111end1 1 1111def1self.point_to_lnglat(m_trans,1m_offset,1point)1
128 1111google.maps.event.addListener(searchBox,1'places_changed',1function()1{1 111111var1places1=1searchBox.getPlaces(),1 1111111111placeBounds;1 1 111111if1(places.length1==10)1{1 11111111return;1 111111}1 1 111111//!For!each!place,!get!the!icon,!place!name,!and!location.! 111111placeBounds1=1new1google.maps.LatLngBounds();1 111111for1(var1i1=10,1place;1place1=1places[i];1i++)1{1 11111111placeBounds.extend(place.geometry.location);1 111111}1 1 111111map.fitBounds(placeBounds);1 1111});1 11}1 1 11//! 11//!Add!a!class!to!the!map!to!change!the!opacity! 11//!of!custom!markers!and!clusters! 11//! 11function1setMarkersOpacity(opacity){1 1111//!TODO!don't!ignore!the!opacity! 1111if(opacity1==11){1 111111$container.removeClass('opacitysmarkers');1 1111}1else1{1 111111$container.addClass('opacitysmarkers');1 1111}1 11}1 1 11//!Replace!marker!icon!when!select!it!in!table! 11function1selectMarker(id){1 1 111111if1(oldMarker1!=1undefined){1 11111111for(var1i1=10;1i1<1markers.length;1i++){1 1111111111if1(markers[i].id1==1oldMarker.id){1 111111111111markers[i].setIcon(oldMarker.icon);1 111111111111center();1 111111111111i1=1markers.length;1 1111111111}1 11111111}1 11111111if(oldMarker.id1==1id)1{1 1111111111oldMarker1=1undefined;1 1111111111return;1 11111111}1 111111}1 11111 111111for1(var1i1=10;1i1<1markers.length;1i++){1 11111111if1(markers[i].id1==1id){1 1111111111oldMarker1=1$.extend(true,1{},1markers[i]);11111111111 1111111111var1icon1=1markers[i].icon;1 1111111111icon.strokeWeight1=12.5;1 1111111111icon.strokeColor1=1"yellow";1 1 1111111111markers[i].setIcon(icon);1 1111111111map.setCenter(markers[i].getPosition());1 1111111111map.setZoom(20);1 1111111111i1=1markers.length;1 11111111}1
111111}1 11111 11}1 1 1 11//!! 11//!Adds!a!new!marker!to!the!map!at!the!position!specified! 11//!marker_opts!=!{! 11//!!!content:!The!tooltip!shown!when!clicking!on!a!marker! 11//!!!draggable:!Specifies!if!the!marker!should!be!draggable!or!not! 11//!!!onMove:!Event!fired!when!the!marker!is!moved! 11//!}! 11//! 1 11function1addMarker(lat,1lng,1markerOpts,1nocenter)1{1 1111var1marker,1info,1latLng;1 1 1111//!If!the!map!is!not!initialized,!we!save!the!marker!to!add!it! 1111//!later!on,!when!the!map!is!initialized! 1111if1(!wasInitialize())1{1 111111markersToAdd1=1markersToAdd.concat([{1 11111111lat:1lat,1 11111111lng:1lng,1 11111111opts:1markerOpts1 111111}]);1 1 111111return;1 1111}1 1 1111markerOpts1=1markerOpts1||1{};1 1111markerOpts.draggable1=1markerOpts.draggable1||1false;1 1111markerOpts.filterWhenClick1=1markerOpts.filterWhenClick1||1false;1 1111markerOpts.content1=1markerOpts.content1||1false;1 1111markerOpts.infoBox1=1markerOpts.infoBox1||1false;1 1111markerOpts.infoBoxCloseURL1=1markerOpts.infoBoxCloseURL1||1'/assets/icons/maps close.png';1 1111markerOpts.pixelOffset1=1markerOpts.pixelOffset1||1false;1 1111markerOpts.onMove1=1markerOpts.onMove1||1false;11111 1 1111latLng1=1new1google.maps.LatLng(lat,1lng,1false);1 1 1111marker1=1new1google.maps.Marker({1 111111position:1mapOptions.center,1 111111map:1map,1 111111draggable:1markerOpts.draggable,1 111111raiseOnDrag:1true,1 111111icon:1markerOpts.icon,1 111111position:1latLng1 1111});1 1 1111if1(markerOpts.markerId1!=1undefined){1 111111marker.set("id",1markerOpts.markerId);1 1111}1 1 1111if1(opts.clustered)1{1 111111markerClusterer.addMarker(marker,1true);1 1111}1 1 1111markers.push(marker);1 1111bounds.extend(latLng);1 1111if1(nocenter1===1undefined1||1nocenter1===1false){1 111111center();1
130 1111}1 1 1111if1(markerOpts.onMove1!==1false)1{1 111111google.maps.event.addListener(marker,1'dragend',1function(mouseEvent)1{1 11111111markerOpts.onMove(marker,1mouseEvent);1 111111});1 1111}1 1 1111if1(markerOpts.infoBox1!==1false1&&1markerOpts.pixelOffset1!==1false){1 1 11111111info1=1new1InfoBox({1 111111111111content:1markerOpts.infoBox,1 111111111111map:1map,1 111111111111closeBoxMargin:1"0",1 111111111111pixelOffset:1markerOpts.pixelOffset,1 111111111111closeBoxURL:1markerOpts.infoBoxCloseURL1 11111111});1 1 11111111infos.push(info);1 1 11111111google.maps.event.addListener(marker,1'click',1function1(e)1{1 111111111111info.open(map,1marker);1 111111111111map.panTo(latLng);1 11111111});1 1111}1 1 1111if1(markerOpts.content1!==1false1&&1(markerOpts.infoBox1===1false1&&1 markerOpts.pixelOffset1===1false))1{1 111111info1=1new1google.maps.InfoWindow({1 11111111content:1markerOpts.content111111111 111111});1 1 111111infos.push(info);1 1111111 111111google.maps.event.addListener(marker,1'click',1function1(e)1{1 11111111if1(markerOpts.filterWhenClick1===1true){1 1111111111$("#search").val(markerOpts.content.split("MAC1Address:</b>1 ")[1].split("<br1/>")[0]);1 1111111111apply_ap_filter($("#search"));1 1111111111$("#search").val('');1 1111111111$('.center_map_on_marker').show();1 11111111}1 11111111infos.forEach(function1(i)1{1i.close();1});1 11111111info.open(map,1this);1 111111});1 1 111111google.maps.event.addListener(info,1'closeclick',1function1(e)1{1 11111111$("#search").val('');1 11111111apply_ap_filter($("#search"));1 111111});1 1111}1 1 1111if1(markerOpts.onClick1!==1undefined)1{1 111111var1fun1=1markerOpts.onClick;1 111111google.maps.event.addListener(marker,1'click',1function(mouseEvent)1{1 11111111fun(marker,1map,1mouseEvent);1 111111});1 1111}1 1 1111if1(opts.draggable1===1true1){1
111111if1(markerOpts.icon1!==1undefined){1 11111111google.maps.event.addListener(marker,'dragstart',function(event)1{1 1111111111marker.icon.fillOpacity1=10.3;1 1111111111marker.icon.strokeWeight1=10.3;1 1111111111marker.setIcon(marker.icon);1 11111111});1 111111}1 111111google.maps.event.addListener(marker,'dragend',function(event)1{1 11111111if1(markerOpts.icon1!==1undefined)1marker.setIcon(markerOpts.icon);1 11111111if1(markerOpts.dragEndCallback1!==1undefined)1 markerOpts.dragEndCallback(marker,1event);1 111111});1 1111}1 11}1 1 11//! 11//!Adds!a!list!of!markers!to!the!map.! 11//!list:!Array!of!objects!with!the!data! 11//!markerOpts!=!{! 11//!!!lat:!The!objects'!property!where!the!latitude!is!located! 11//!!!lng:!The!objects'!property!where!the!longitude!is!located! 11//!}! 11//! 11function1addMarkers(list,1markersOpts,1nocenter)1{1 1111if1(list1===1undefined1||1list.length1===10)1return;1 1111if1(markersOpts1===1undefined)1markersOpts1=1{};1 1 1111var1lngPty,1latPty;1 1111latPty1=1markersOpts.lat1||1'lat';1 1111lngPty1=1markersOpts.lng1||1'lng';1 1 1111list.forEach(function1(d)1{1 111111addMarker(d[latPty],1d[lngPty],1{1 11111111content:1d.description1 111111});1 1111});1 11}1 1 11function1addCustomMarker(lat,1lng,1markerOpts,1nocenter)1{1 1111var1marker,1info,1latLng;1 1 1111//!If!the!map!is!not!initialized,!we!save!the!marker!to!add!it! 1111//!later!on,!when!the!map!is!initialized! 1111if1(!wasInitialize())1{1 111111markersToAdd1=1markersToAdd.concat([{1 11111111lat:1lat,1 11111111lng:1lng,1 11111111opts:1markerOpts1 111111}]);1 1 111111return;1 1111}1 1 1111markerOpts1=1markerOpts1||1{};1 1111markerOpts.draggable1=1markerOpts.draggable1||1false;1 1111markerOpts.content1=1markerOpts.content1||1false;1 1111markerOpts.onMove1=1markerOpts.onMove1||1false;1 1111markerOpts.text1=1(markerOpts.text1===1undefined)1?1''1:1markerOpts.text;1 1111markerOpts.backgroundColor1=1markerOpts.backgroundColor1||1'blue';1 1111markerOpts.fontColor1=1markerOpts.fontColor1||1'white';1 1 1111latLng1=1new1google.maps.LatLng(lat,1lng,1false);1
132 1 1111customMarker1=1new1CustomMarker(latLng,1map,1markerOpts);1 1 1111if1(opts.clustered)1{1 111111markerClusterer.addMarker(customMarker,1true);1 1111}1 1 1111customMarkers.push(customMarker);1 1111bounds.extend(latLng);1 1111if1(nocenter1===1undefined1||1nocenter1===1false){1 111111center();1 1111}1 1 11}1 1 11function1addCustomMarkers(list,1markersOpts)1{1 1111if1(list1===1undefined1||1list.length1===10)1return;1 1111if1(markersOpts1===1undefined)1markersOpts1=1{};1 1 1111var1lngPty,1latPty;1 1111latPty1=1markersOpts.lat1||1'lat';1 1111lngPty1=1markersOpts.lng1||1'lng';1 1 1111list.forEach(function1(d)1{1 111111addCustomMarker(d[latPty],1d[lngPty],1{1 11111111content:1d.description1 111111});1 1111});1 11}1 1 11/*! !!!*!If!the!cluster!option!is!active,!repaint!clusters! !!!*/1 11function1repaintCluster(){1 1111if(opts.clustered1&&1markerClusterer1!==1undefined){1 111111markerClusterer.repaint();1 1111}1 11}1 1 11function1getMarkers()1{1 1111return1markers;1 11}1 1 11function1deleteMarker(id){1 1111var1i1=1markers.map(function(d){return1d.id}).indexOf(id),1 11111111marker;1 1 1111markers[i].setMap(null);1 1111marker1=1markers.splice(i,11)[0];1 1111if1(markerClusterer1!==1undefined){1 111111markerClusterer.removeMarker(marker);1 1111}1 11}1 1 11function1removeMarkers()1{1 1111for1(var1i1=10;1i1<1markers.length;1i++){1 111111markers[i].setMap(null);1111111 1111}1 1111markers1=1[];1 1111if1(markerClusterer1!==1undefined){1
111111markerClusterer.clearMarkers();1 1111}1 11}1 1 11function1getCustomMarkers()1{1 1111return1customMarkers;1 11}1 1 11function1removeCustomMarkers()1{1 1111for1(var1i1=10;1i1<1customMarkers.length;1i++){1 111111customMarkers[i].setMap(null);111111 1111}11111 1111customMarkers1=1[];1 1111if1(markerClusterer1!==1undefined){1 111111markerClusterer.clearMarkers();1 1111}1 11}1 1 11function1fullScreen(){1 1111var1self1=1$container.parent();1 1111if1(self.hasClass('full_screen_map')){1 111111map.setOptions({scrollwheel:1false});1 1111}1 1111else{1 111111map.setOptions({scrollwheel:1true});1 1111}1 11111 1111self.toggleClass('full_screen_map');1 1111$('.expandsmap').toggleClass("fasexpand");1 1111$('.expandsmap').toggleClass("fascompress");1 1111$container.toggleClass('full_screen_map_child');1111 1111google.maps.event.trigger(map,'resize');1 11111 11}1 1 11function1addPolyline(positions,1opts,1identifier)1{1 1111//!Force!the!map! 1111opts['map']1=1map;1 1 1111//!Get!the!color! 1111if(opts['strokeColor']1===1undefined){1 111111opts['strokeColor']1=1getPolylineColor();1 1111}1 1 1111var1polyline1=1new1google.maps.Polyline(opts);1 1111var1markers1=1[];1 1111var1bounds1=1new1google.maps.LatLngBounds();1 1 1111//!Set!the!markers!in!the!line! 1111var1z1=10;1 1111var1path1=1[];1 1111path[z]1=1polyline.getPath();1 11111 1111for1(var1i1=10;1i1<1positions.length;1i++)1//LOOP!TO!DISPLAY!THE!MARKERS! 1111{1 111111var1pos1=1positions[i];1 1 111111//!We!only!show!the!first!and!last!marker! 111111if1(i1===101||1i1===1(positions.length1s11)){1 11111111var1marker1=1new1google.maps.Marker({1 111111111111position:1pos,1 111111111111map:1map1
134 11111111});1 11111111markers.push(marker);1 111111}1 1111111 111111bounds.extend(pos);1 111111path[z].push(pos);1//PUSH!THE!NEWLY!CREATED!MARKER'S!POSITION!TO!THE!PATH!ARRAY! 1111}1 1 1111//!Store!the!polyline!with!an!identifier! 1111polylines.push({1 111111id:1identifier,1 111111line:1polyline,1 111111markers:1markers1 1111})1 1 1111//!Center!the!map!in!the!polyline! 1111map.fitBounds(bounds);1 1 1111//!Return!the!color!used! 1111return1opts['strokeColor'];1 11}1 1 11function1getPolylines(){1 1111return1polylines;1 11}1 1 11function1removePolyline(identifier)1{1 1111var1index1=1s1;1 1 1111for1(var1i1=10;1i1<1polylines.length;1i++){1 111111if1(polylines[i].id1===1identifier){1 11111111polylines[i].line.setMap(null);1 11111111index1=1i;1 11111111//!Delete!all!markers! 11111111while(polylines[i].markers.length1>10){1 1111111111polylines[i].markers.pop().setMap(null);1 11111111}1 111111}1 1111}1 1 1111if1(index1>=10)1{1 111111polylines.splice(index,11);1 1111}1 11}1 1 11function1getPolylineColor(){1 1111var1color;1 1 1111if1(palette.length1>=1polylines.length)1{1 111111color1=1palette[polylines.length1s11];1 1111}1else1{1 111111color1=1getRandomColor();1 1111}1 1 1111return1color;1 11}1 1 11function1getRandomColor()1{1 1111var1letters1=1'0123456789ABCDEF'.split('');1 1111var1color1=1'#';1
11111 1111for1(var1i1=10;1i1<16;1i++1)1{1 11111111color1+=1letters[Math.floor(Math.random()1*116)];1 1111}1 1 1111return1color;1 11}1 1 11function1addPolygon(vertexes,1opts)1{1 1111var1polygon,1latLngs1=1[];1 1 1111//!If!the!map!is!not!initialized,!we!save!the!marker!to!add!it! 1111//!later!on,!when!the!map!is!initialized! 1111if1(!wasInitialize())1{1 111111polygonsToAdd1=1polygonsToAdd.concat({1 11111111vertexes:1vertexes,1 11111111opts:1opts1 111111});1 1 111111return;1 1111}1 1 1111vertexes.forEach(function1(v)1{1 111111latLngs.push(new1google.maps.LatLng(v.lat,1v.lng));1 1111});1 1 1111polygon1=1new1google.maps.Polygon({1 111111paths:1latLngs,1 111111clickable:1opts.clickable,1 111111fillColor:1opts.fillColor,1 111111fillOpacity:1opts.fillOpacity,1 111111strokeWeight:1opts.strokeWeight,1 111111strokeOpacity:1opts.strokeOpacity1 1111});1 1 1111//!Create!a!listener!on!click!if!there!is!a!content!opt! 1111if1(opts.content1!==1undefined)1{1 111111var1polygon_bounds1=1new1google.maps.LatLngBounds();1 1 111111latLngs.forEach(function(xy,i)1{1 11111111bounds.extend(xy);1 11111111polygon_bounds.extend(xy);1 111111});1 1 111111var1info1=1new1google.maps.InfoWindow({1 11111111content:1opts.content,1 11111111position:1polygon_bounds.getCenter()1 111111});1 1 111111infos.push(info);1 1 111111google.maps.event.addListener(polygon,1'rightclick',1function1(e)1{1 11111111infos.forEach(function1(i)1{1i.close();1});1 11111111info.open(map);1 111111});1 1111}1 1 1111if1(opts.onClick1!==1undefined)1{1 111111var1fun1=1opts.onClick;1 111111google.maps.event.addListener(polygon,1'click',1function(mouseEvent)1{1 11111111fun(polygon,1map,1mouseEvent);1 111111});1
136 1111}1 1 1111if1(opts.onDblClick1!==1undefined)1{1 111111var1func1=1opts.onDblClick;1 111111google.maps.event.addListener(polygon,1'dblclick',1function(mouseEvent)1{1 11111111func(polygon,1map,1mouseEvent);1 111111});11 1111}1 1 1111polygon.setMap(map);1 1111polygons.push(polygon);1 11}1 1 11function1getPolygons()1{1 1111return1polygons;1 11}1 1 11function1removePolygons(){1 1111polygons.forEach(1function(d)1{1 111111d.setMap(null);1 1111});1 1 1111polygons1=1[];1 11}1 1 11//! 11//!Creates!a!listener!on!the!idle!event! 11//!Executes!the!passed!fun!passing!it!the!current!google!map! 11//!instance!and!the!mouseEvent!that!was!fired! 11//! 11function1onLoad(fun)1{1 1111google.maps.event.addListenerOnce(map,1'idle',1function(mouseEvent)1{1 111111fun(map,1mouseEvent);1 1111});1 11}1 1 11//! 11//!Creates!a!listener!on!the!click!event! 11//!Executes!the!passed!fun!passing!it!the!current!google!map! 11//!instance!and!the!mouseEvent!that!was!fired! 11//! 11function1onClick(fun)1{1 1111google.maps.event.addListener(map,1'click',1function(mouseEvent)1{1 111111fun(map,1mouseEvent);1 1111});1 11}1 1 11//! 11//!Creates!a!listener!on!the!dragend!event! 11//!Executes!the!passed!fun!passing!it!the!current!google!map! 11//!instance!and!the!mouseEvent!that!was!fired! 11//! 11function1onDrag(fun)1{1 1111google.maps.event.addListener(map,1'dragend',1function(mouseEvent)1{1 111111fun(map,1mouseEvent);1 1111});1 11}1 1 11//!! 11//!Reloads!the!map!using!a!resize!
11//! 11function1reload()1{1 1111google.maps.event.trigger(map,1'resize');1 1111/*if!(markerClusterer!!==!undefined)!{! !!!!!!markerClusterer.repaint();! !!!!}*/1 1111$container.css('opacity',1'1');1 11}1 1 11//!! 11//!Centers!the!map!automatically!based!on!the!markers! 11//! 11function1center()1{1 1111if1(!wasInitialize()1||1bounds.isEmpty())1return;1 1111map.fitBounds(bounds);1 11}1 1 11//! 11//!Extends!the!bounds!adding!a!LatLng!point! 11//! 11function1extendBounds(lat,1lng)1{1 1111bounds.extend(new1google.maps.LatLng(lat,1lng));1 11}1 1 11//! 11//!Returns!the!raw!google!map!instance! 11//! 11function1getMap()1{1 1111return1map;1 11}1 1 11function1wasInitialize()1{1 1111return1(map1!==1undefined);1 11}1 1 11function1setOptions(opts)1{1 1111map.setOptions(opts);1 11}1 1 11return1{1 1111initialize:1initialize,1 1111wasInitialize:1wasInitialize,1 1111addMarker:1addMarker,1 1111addMarkers:1addMarkers,1 1111removeMarkers:1removeMarkers,1 1111addCustomMarker:1addCustomMarker,1 1111addCustomMarkers:1addCustomMarkers,1 1111repaintCluster:1repaintCluster,1 1111removeCustomMarkers:1removeCustomMarkers,1 1111getMarkers:1getMarkers,1 1111selectMarker:1selectMarker,1 1111getCustomMarkers:1getCustomMarkers,1 1111addPolyline:1addPolyline,1 1111getPolylines:1getPolylines,1 1111removePolyline:1removePolyline,1 1111addPolygon:1addPolygon,1 1111getPolygons:1getPolygons,1 1111setMarkersOpacity:1setMarkersOpacity,1 1111reload:1reload,1 1111resize:1reload,1 1111deleteMarker:1deleteMarker,1 1111center:1center,1
144 1111$(document).on('click',1'#tab_'1+1opts.tab1+1'1table1td.serie1a.coord_serie',1 (function1(markers,1heatmapOverlay)1{1 111111return1function()1{1 11111111var1position1=1$(this).data('coord');1 1 11111111if1(position1!==1"")1{1 1111111111if1($(this).hasClass('markerAdded'))1{1 111111111111$(this).removeClass('markerAdded');1 1 111111111111markers[position].setMap(null);1 1111111111}1else1{1 111111111111var1latLngArray1=1position.split(',');1 1 111111111111if1(latLngArray.length1===12)1{1 11111111111111$(this).addClass('markerAdded');1 1 11111111111111var1latLng1=1new1google.maps.LatLng(latLngArray[0],1latLngArray[1]),1 1111111111111111element_for_title1=1$(this).parents('.selectables serie').find('td.last');1 1 11111111111111markers[position]1=1new1google.maps.Marker({1 111111111111111111position:1latLng,1 111111111111111111map:1heatmapOverlay.map,1 111111111111111111title:1element_for_title.attr('title')1||1element_for_title.text()1 11111111111111});1 111111111111}1 1111111111}1 11111111}1 111111}1 1111})({},1heatmapOverlay));1 11}1 1 11function1wasInitialize()1{1 1111return1(map1!==1undefined);1 11}1 1 11function1getRbMap()1{1 1111return1map;1 11}1 1 11function1getMap()1{1 1111return1map.getMap();1 11}1 1 11function1resize()1{1 1111if1(!wasInitialize())1return;1 1111map.reload();1 1111reloadData();1 11}1 1 11function1setData(nocenter)1{1 1111if(map1!==1undefined)1{1 111111data.data.forEach(function1(d)1{1 11111111map.extendBounds(d.lat,1d.lng);1 111111});1 1 111111if(nocenter1===1undefined1||1nocenter1===1false){1 11111111map.center();1 111111}1 1
111111heatmapOverlay.setDataSet(data);1 1111}1 11}1 1 11function1reloadData(_data,1nocenter)1{1 1111if1(_data1!==1undefined){1 111111data1=1parseData(_data);1 1111}1 1111heatmapOverlay.toggle();1 1111if1(opts.grid_heatmap1==1true)1 111111setQualityData();1 1111else1 111111setData(nocenter);1 1 1111//!Force!the!draw!of!heatmap! 1111setTimeout(function(){1 111111heatmapOverlay.draw();1 111111heatmapOverlay.toggle();1 1111},1400);1 11}1 1 11//!TODO.!Mix!with!simple!reloadData()! 11function1reloadData_dynamic(_data,1nocenter){1 1111if1(_data1!==1undefined){1 111111data1=1parseData(_data);1 1111}1 1 1111setData(nocenter);1 1 1111//!Force!the!draw!of!heatmap! 1111heatmapOverlay.draw();1 11}1 1 11function1addData(unparsedData)1{1 1111if1(unparsedData1===1undefined1||1unparsedData.length1===10)1return;1 1111var1parsedData1=1parseData(unparsedData);1 1 1111if1(parsedData.max1>1data.max)1data.max1=1parsedData.max;1 1111data.data1=1data.data.concat(parsedData.data);1 1 1111if1(opts.grid_heatmap1==1true)1 111111setQualityData();1 1111else1 111111setData();1 11}1 1 11function1getData()1{1 1111return1data;1 11}1 1 11function1setOptions(opts)1{1 1111map.setOptions(opts);1 11}1 1 11return1{1 1111initialize:1initialize,1 1111wasInitialize:1wasInitialize,1 1111resize:1resize,1 1111addData:1addData,1 1111getData:1getData,1 1111reloadData:1reloadData,1 1111reloadData_dynamic:1reloadData_dynamic,1
146 1111getRbMap:1getRbMap,1 1111getMap:1getMap,1 1111setOptions:1setOptions,1 1111setMax:1setMax1 11};1 }1 animatedHeatmaps.js /*!!!!! !*!Copyright!(c)!2014!ENEO!TecnologÌa!S.L.! !*!This!file!is!part!of!redBorder.! !*!redBorder!is!free!software:!you!can!redistribute!it!and/or!modify! !*!it!under!the!terms!of!the!GNU!Affero!General!Public!License!as!published!by! !*!the!Free!Software!Foundation,!either!version!3!of!the!License,!or! !*!(at!your!option)!any!later!version.! !*!redBorder!is!distributed!in!the!hope!that!it!will!be!useful,! !*!but!WITHOUT!ANY!WARRANTY;!without!even!the!implied!warranty!of! !*!MERCHANTABILITY!or!FITNESS!FOR!A!PARTICULAR!PURPOSE.!!See!the! !*!GNU!Affero!General!Public!License!for!more!details.! !*!You!should!have!received!a!copy!of!the!GNU!Affero!General!Public!License! !*!along!with!redBorder.!If!not,!see!<http://www.gnu.org/licenses/>.! !*/1 1 1redBorder.animatedHeatmaps1=1{}1 1 1redBorder.animatedHeatmap1=1function1(map,1opts)1{1 1 11/*!Times!as!Miliseconds!*/1 11var1start_time1=1new1Date(opts['start_time']*1000).setSeconds(0);1 11var1end_time1=1new1Date(opts['end_time']*1000).setSeconds(0);1 11var1time_step1=1opts['step'];1 11var1current_time1=1start_time;1 1 11var1heatmap1=1map;1 11var1granularity1=1opts['granularity'];1 11var1aggregation1=1opts['aggregation'];1 11var1buffer1=1[];1 111 11var1is_loading1=1false;1 11var1iteration1=10;1 11var1time_iteration1=10;1 11var1force_stop1=1false;1 11var1pause1=1false;1 11var1speed1=11;1 1 11function1stop(){1 1111force_stop1=1true;1 1111buffer1=1[];1 11}1 1 11function1end_animation(){1 1111iteration1=10;1 1111time_iteration1=10;1 1111current_time1=1start_time;1 1111heatmap.setOptions({draggable:1true,1panControl:1true});1 1111$('.faspause').addClass("fasplay");1 1111$('.faspause').removeClass("faspause");1 11}1
1 11function1update_time(time){1 1111var1d1=1new1Date(time);1 1111var1dd1=1d.getDate();1 1111var1mm1=1d.getMonth()+1;1 1111var1hh1=1d.getHours();1 1111var1mmm1=1d.getMinutes();1 1 1111if1(dd1<110)1dd1=1'0'+1dd;1 1111if1(mm1<110)1mm1=1'0'+1mm;1 1111if1(hh1<110)1hh1=1'0'+1hh;1 1111if1(mmm1<110)1mmm1=1'0'+1mmm;1 1 1111var1progress1=1(1001*1time_iteration1/1Math.floor((end_time1s1start_time)1/1 time_step));1 1 1111if1(progress1<180){1 111111$('.bol').css({"left":""+1progress1+"%"});11 1111}1else1{1 111111$('.bol').css({"left":""+1Math.floor(progress)1+"%"});11 1111}1 1 1111var1date1=1""+1d.getFullYear()1+1"s"1+1mm1+1"s"1+1dd1+1"1"1+1hh1+1":"1+1mmm1+1"";1 1111$('.time_box').html("<i1class='fa1fasclockso'></i>"+1"11"1+1date);1 11}1 1 11function1finish_with_no_data(flag){1 1111if1(!pause){1 111111time_iteration++;1 111111if1(flag1==1undefined){1 11111111var1empty1=1[];1 11111111heatmap.reloadData_dynamic(empty,1true);1 111111}1 111111if1((end_time1s1current_time)1<=1time_step){1 11111111end_animation();1 111111}1else{1 111111current_time1=1parseInt(current_time)1+1parseInt(time_step);1 111111update_time(current_time);1 111111setTimeout(finish_with_no_data,140000/1(speed1*1Math.floor((end_time1s1 start_time)1/1time_step)),1true);1 111111}1 1111}1 11}1 1 11function1start_animated_heatmap(){1 1111time_iteration++;1 1111if(!pause){1 111111update_time(current_time);1 1 111111if1(1buffer[iteration][0].timestamp1==1new1Date(current_time).toISOString()){1 11111111heatmap.reloadData_dynamic(buffer[iteration][0].result,1true);1 11111111iteration++;1 111111}1else{1 11111111var1empty1=1[];1 11111111heatmap.reloadData_dynamic(empty,1true);1 111111}1 1 111111if(iteration1<1buffer.length){1 111111current_time1=1parseInt(current_time)1+1parseInt(time_step);1 111111if1(!force_stop)1setTimeout(start_animated_heatmap,140000/1(speed1*1 Math.floor((end_time1s1start_time)1/1time_step)));1 111111}1else{1
148 11111111if1((end_time1s1current_time)1>1time_step){1 1111111111finish_with_no_data();1 11111111}1else{1 11111111end_animation();1 11111111}1 111111}1 1111}1 11}1 1 11function1loading(num,1status){1 1111if(status1==1undefined){1 111111$('.loading').html("Loading1"1+1Math.round(num)1+1"%");1 1111}1else1if1(status){1 111111$('.loading').html("Loading10%");1 1111}1else{1 111111$('.button_gmap').hide();1 111111$('.animated_player').show();1 1111}1 11}1 1 11function1receive_data(data){1 1111if1(data.length1>10)1buffer.push(data);1 1111loading(1(1001*1iteration++)1/1((end_time1s1start_time)/time_step)1);1 1 1111var1current_start1=1(start_time1+1iteration1*1time_step)/1000;1 1111var1current_end1=1(start_time1+1(iteration+1)1*1time_step)/1000;1 111 1111if1(1000*(current_start)1<1end_time){1 111111if(!force_stop)1request_data(1current_start,1current_end);11 1111}1 1111else{1 111111loading(0,1false);1 111111heatmap.setMax(Math.max.apply(Math,1buffer.map(function(x){return1 Math.max.apply(Math,1x[0].result.map(function(y){return1y[aggregation]}))})));1 111111iteration1=10;1 111111time_iteration1=10;1 111111current_time1=1start_time;1 1111}1 11}1 1 11$(document).on('click',1'.faspause',1function()1{1 1111if(force_stop)1return;1 1111pause1=1true;1 1111$('.faspause').addClass("fasplay");1 1111$('.faspause').removeClass("faspause");1 11});1 1 11$(document).on('click',1'.fasforward',1function()1{1 1111if(force_stop)1return;1 1111if1($('.acx').hasClass('fasplay'))1return;1 1111speed1=1speed1*12;1 1111if(speed1>18)1speed1=18;1 1111$('.speed').html("x"+speed);1 11});1 11$(document).on('click',1'.fasbackward',1function()1{1 1111if(force_stop)1return;1 1111if1($('.acx').hasClass('fasplay'))1return;1 1111speed1=1speed1/12;1 1111if(speed1<11)1speed1=11;1 1111$('.speed').html("x"+speed);1
11});1111111 1 11$(document).on('click',1'.fasstop',1function()1{1 1111if(force_stop)1return;1 1111pause1=1true;1 1111$('.acx').removeClass("faspause");1 1111$('.acx').removeClass("fasplay");1 1111$('.acx').addClass("fasplay");1 1111iteration1=10;1 1111time_iteration1=10;1 1111current_time1=1start_time;1 1111update_time(current_time);1 1111heatmap.setOptions({draggable:1true,1panControl:1true});1 1111heatmap.reloadData_dynamic(buffer[0][0].result,1true);1 11});1 1 11$(document).on('click',1'.fasplay',1function()1{1 1111if(force_stop)1return;1 1111pause1=1false;1 1111heatmap.setOptions({draggable:1false,1panControl:1false});1 1111$('.fasplay').addClass("faspause");1 1111$('.fasplay').removeClass("fasplay");1 1111if1(iteration1>=1buffer.length1&&1((end_time1s1current_time)1>1time_step)){1 111111finish_with_no_data();1 1111}1else{1 111111start_animated_heatmap();1 1111}1 11});1 1 1 11function1animated_heatmap(){1 1111force_stop1=1false;1 1111loading(0,1true);1 1111iteration1=10;1 1111time_iteration1=10;1 1111request_data(start_time/1000,1(start_time1+1iteration1*1time_step)/1000);1 11}1 1 11function1request_data(start,end){1 1111var1url1=11 "/flow/get_points?start_time="+start+"&end_time="+end+"&granularity="+granularity+"&a ggregation="+aggregation+"";111 1111$.ajax({1 1111111111url:1url,1 1111111111remote:1false,1 1111111111dataType:1'json',1 1111111111type:1'GET',1 11111111}).done(function1(data)1{1 1111111111receive_data(data);1 11111111});11 11}1 1 11$(document).on('click',1'.gmap_button',1function()1{1 1111if(force_stop)1return;1 1111$('.button_gmap').removeClass('gmap_button');1 1111$('.button_gmap').html("<i1class='fa1fasspinner1faspulse'></i> <span1 class='loading'></span>")1 1111animated_heatmap();1 11});1 1 11return{1 1111stop:1stop1
150 11}1 1 }1 1 flow/top_content.html.erb <div1id="tab_<%=1tab1%>"1class="tabspane1<%=1(session[:traffic][:tab].to_s1==1 tab.to_s)1?1'active'1:1''1%>">1 11<h31class="visiblesprint">1 1111<%=1Traffic::COLUMNS[tab.to_sym][:name]1or1 Traffic::COLUMNS[tab.to_sym][:columns].values.first1%>1 11</h3>1 11<div1class="tabsgraph">1 1111<%1if1[:src_map,1:dst_map,1:coordinates_map].include?1tab.to_sym1%>1 111111<div1id='<%=1tab.to_s1%>'1class="map_wrap_tops">1 11111111<div1class="map_container">1 1111111111<div1id="mapa_<%=1tab.to_s1%>"1class="map"></div>1 1111111111<%1if1tab.to_sym1==1:coordinates_map1&&
[email protected]_s1!=1"quality"1%>1 111111111111<div1class='gmap_button1button_gmap'><i1class="fa1fasplays circle"></i> <span1class='loading'>Animation</span></div>1 111111111111<div1class="time"></div>1 111111111111<div1class="animated_player">1 11111111111111<i1class="fa1fasbackward1play_b"></i>1 11111111111111<i1class="acx1fa1fasplay1play_b"></i>1 11111111111111<i1class="fa1fasforward1play_b"></i>1 11111111111111<i1class="fa1fasstop1play_b"></i>1 11111111111111<i1class="bar">1 1111111111111111<i1class="origin"></i>1 1111111111111111<i1class="bol"></i>1 11111111111111</i>1 11111111111111<i1class="speed1fa1play_b">x1</i>1 11111111111111<i1class="time_box"><i1class='fa1fasclockso'></i>03s03s2015109:52</i>1 111111111111</div>1 1111111111<%1end1%>1 11111111</div>1 1 11111111<scriptCtype="text/javascript">1 1 1111111111(function1()1{1 111111111111function1onLoadFun()1{1 11111111111111<%1OverlayMap.where(enabled:1true,11 1111111111111111111111111111sensor_id:1 current_user.sensors_with_permission.map(&:id)).each1do1|overlay_map|1%>1 1 11111111111111var1swBound1=1new1google.maps.LatLng(<%=1overlay_map.lat_sw1%>,1<%=1 overlay_map.lng_sw1%>);1 11111111111111var1neBound1=1new1google.maps.LatLng(<%=1overlay_map.lat_ne1%>,1<%=1 overlay_map.lng_ne1%>);1 11111111111111var1bounds1=1new1google.maps.LatLngBounds(swBound,1neBound);1 11111111111111var1srcImage1=1"<%=1image_overlay_map_path(overlay_map)1%>";1 1 11111111111111overlay1=1new1google.maps.GroundOverlay(srcImage,1bounds);1 11111111111111overlay.setMap(rb_<%=1tab.to_s1%>.getMap());1 11111111111111<%1end1%>1 1 11111111111111<%1if1tab.to_sym1==1:coordinates_map1&&
[email protected]_s1!=1"quality"1 %>1
1 1111111111111111if1(redBorder.animatedHeatmaps['flow']1!=1undefined){1 111111111111111111redBorder.animatedHeatmaps['flow'].stop();1 1111111111111111}1 1 1111111111111111redBorder.animatedHeatmaps['flow']1=1redBorder.animatedHeatmap(rb_<%=1 tab.to_s1%>,11 111111111111111111111111111111111111111111{start_time:1<%=1@start_time.to_i1%>,1 1111111111111111111111111111111111111111111end_time:1<%=1@end_time.to_i1%>,1 1111111111111111111111111111111111111111111aggregation:1"<%[email protected]_s1%>",1 1111111111111111111111111111111111111111111granularity:1"<%=1@granularity1%>",1 1111111111111111111111111111111111111111111step:1"<%=1 Redborder::Rbdruid::GRANULARITY_TYPE[@granularity.to_sym][:step]1%>"})1 11111111111111<%1end1%>1 111111111111}1 111111111111<%1if1tab.to_sym1==1:coordinates_map1%>111111111111111 11111111111111rb_<%=1tab.to_s1%>1=1redBorder.heatMap({1 1111111111111111container:1"mapa_coordinates_map",1 1111111111111111data:1JSON.parse('<%=1graph.to_json.html_safe1%>'),1 1111111111111111tab:1"coordinates_map",1 1111111111111111aggregation:1"<%=1@aggregation1%>",1 1111111111111111scrollwheel:1false,1111111111111111111 1111111111111111onLoad:1onLoadFun,1 1111111111111111force_max:1("<%=1@aggregation1%>"1==1'quality'1?11001:11),1 1111111111111111autoinit:1false,1 1111111111111111grid_heatmap:1<%
[email protected]_s1==1"quality"1?1"true"1:1"false"1 %>1 11111111111111});1 111111111111<%1else1%>1 11111111111111rb_<%=1tab.to_s1%>1=1redBorder.map({1 1111111111111111container:1"mapa_<%=1tab.to_s1%>",1 1111111111111111data:1JSON.parse('<%=1graph.to_json.html_safe1%>'),1 1111111111111111clustered:1true,1 1111111111111111searchBox:1false,1 1111111111111111scrollwheel:1false,1 1111111111111111autoinit:1false,1 1111111111111111onLoad:1onLoadFun1 11111111111111});1 111111111111<%1end1%>1 1 111111111111try1{1 11111111111111<%1if1session[:traffic][:tab].to_sym1==1tab.to_sym1%>11111111111111111 1111111111111111rb_<%=1tab.to_s1%>.initialize();1 11111111111111<%1end1%>1 111111111111}1catch1(err)1{1 11111111111111console.error("Couldn't1load1map1for1tab1<%=1tab.to_s1%>.1"1+1err);1 111111111111}1 1111111111})();1 11111111</script>1 111111</div>1 1111<%1elsif1tab.to_sym.eql?(:scatterplot)1%>1 111111<div1id="graph_<%=1tab.to_s1%>"></div>1 1111<%[email protected]_sym1!=1:all1%>1 111111<div1class="graph_container">1 11111111<div1class="graph_outliers"1datasproduct="flow"1datastype="tops"1datas graph="rb_<%=1tab.to_s1%>">1 1111111111<%=1content_tag1:i,1nil,1class:1"fa1fascrosshairs"1%>1 11111111</div>1 11111111<div1class="graph_arrow"1datasdirection="left"1datasproduct="flow"1datas type="tops"1datasgraph="rb_<%=1tab.to_s1%>">1 1111111111<%=1content_tag1:i,1nil,1class:1"fa1fasanglesleft"1%>1 11111111</div>1
152 11111111<div1id="graph_<%=1tab.to_s1%>"1class="graph_main"1datasrange="<%=1 @rangemilis1%>"1datasgranularity="<%=1@granularitymilis1%>"1datasgranularityssym="<%=1 @granularity1%>"></div>1 11111111<div1class="graph_arrow"1datasdirection="right"1datasproduct="flow"1datas type="tops"1datasgraph="rb_<%=1tab.to_s1%>">1 1111111111<%=1content_tag1:i,1nil,1class:1"fa1fasanglesright"1%>1 11111111</div>1 111111</div>1 1111<%1end1%>1 11</div>1 11<div1class="tablesinfo">1 1111<div>1 111111<%=1render1partial:1'general_info',1locals:1{rows:1traffics.size,1tab:1tab,1 step:110}1%>1 1111</div>1 11</div>1 11<div1class="tabstable"1id="tabstables<%=1tab.to_s1%>"1<%=1'datasstep="11"1datas intro="This1table1shows1all1the1data1represented1in1the1graph.1You1can1select1some1 rows1(SHIFT1mode1is1available)1and1filter1them1in1the1graph1and1other1tabs."1datas position="top"'.html_safe1if1(session[:traffic][:tab].to_s1==1tab.to_s)1%>>1 111 <%=1render1partial:1'tops_traffics',1locals:1{1tab:1tab,1content:1traffics}1%>1 11</div>1 1 11<%1if1tab.to_sym.eql?(:scatterplot)1%>1 1111<scriptCtype="text/javascript">1 111111var1rb_<%=1tab.to_s1%>1=1new1RB.Scatterplot({1 11111111container:1"graph_<%=1tab.to_s1%>",1 11111111width:1$('article#tops_traffics').css("width"),1 11111111autozoom:1true,1 11111111data:1JSON.parse('<%=1traffics.map{|t|1{src:1t.src,1dst:1t.dst,1timestamp:1 t.timestamp,1value:1t.send(@aggregation)}}.to_json.html_safe1%>')1 111111});1 1 111111$('#tab_<%=1tab.to_s1%>1.tabstable1tbody1tr').mouseenter(function1()1{1 11111111src1=1$('td:nthschild(1)',1this).data("ip").trim();1 11111111dst1=1$('td:nthschild(2)',1this).data("ip").trim();1 11111111if1(src1!==1""1&&1dst1!==1"")1{1 1111111111rb_<%=1tab.to_s1%>.highlight(src,1dst);1 11111111}1 111111});1 111111111 111111$('#tab_<%=1tab.to_s1%>1.tabstable1tbody1tr').mouseleave(function1()1{1 11111111src1=1$('td:nthschild(1)',1this).data("ip").trim();1 11111111dst1=1$('td:nthschild(2)',1this).data("ip").trim();1 11111111if1(src1!==1""1&&1dst1!==1"")1{1 1111111111rb_<%=1tab.to_s1%>.unhighlight(src,1dst);1 11111111}1 111111});1 1111</script>1 11<%1elsif1![:src_map,1:dst_map,1:coordinates_map].include?(tab.to_sym)1and1 @granularity.to_sym1!=1:all1%>1 1111<scriptCtype="text/javascript">1 1 111111var1step1=1<%=1Redborder::Rbdruid::GRANULARITY_TYPE[@granularity.to_sym][:step]1 %>;1 111111var1aggregation1=1JSON.parse('<%=1 Traffic::AGGREGATIONS[session[:traffic][:aggregation]].to_json.html_safe1%>');1 111111var1zoomable1=1($(window).width()1>1750);1 1 111111var1rb_<%=1tab.to_s1%>1=1new1RB.Serie({1
1111111111container:1"graph_<%=1tab.to_s1%>",1 1111111111width:1$('article#tops_traffics').width(),1 1111111111autoscaling:1true,1 1111111111units:1aggregation.unit,1 1111111111step:1step,1 1111111111zoomable:1zoomable,1 1111111111domain:1[<%=1@start_time.to_i1%>,1<%=1@end_time.to_i1%>1s1step1/11000],1 1111111111onZoomEvent:1function1()1{1 111111111111zoomGraph('<%=1tab.to_s1%>');1 1111111111},1 1111111111onZoomResetEvent:1function1()1{1 111111111111zoomResetGraph('<%=1tab.to_s1%>');1 1111111111}1 11111111});1 1 111111rb_<%=1tab.to_s1%>.add({data:1total,1name:1"Total",1type:1"line",1dashed:1true,1 color:1"#ccc",1stroke:11.5})1 1 111111if1(rb_<%=1tab.to_s1%>.elements.length1>10)1{1 11111111if1('<%=1@hide_total1%>'1==1'true')1{1 1111111111rb_<%=1tab.to_s1%>.hide('Total');1 11111111}1else1{1 1111111111rb_<%=1tab.to_s1%>.unhide('Total');1 11111111}1 111111};1 1 111111if1(<%
[email protected]%>1>10){1 11111111rb_<%=1tab.to_s1%>.add({data:1total_filtered,1name:1"Total1Filtered",1type:1 "line",1dashed:1true,1color:1"#F0F0F0",1stroke:10.5})1 111111111 11111111hide_total_filtered1=1($('li.hidesfiltered').css('display')1==1"none");1 11111111if1(hide_total_filtered)1{1 1111111111rb_<%=1tab.to_s1%>.hide('Total1Filtered');1 11111111}1 111111};1 1 1111</script>1 11<%1end1%>1 </div>1 traffics_controller#get_points def1get_points1
[email protected]('client_latlong',1[''])
[email protected]?(:client_latlong)1 1
[email protected]_json(1 111111query_type:1:group_by,1 111111granularity:1params[:granularity],1 111111group_by:1[:client_latlong],1 111111limit:11000,1 111111aggregation:1params[:aggregation],1 111111search:
[email protected],1 111111start_time:1Time.at(params[:start_time].to_i),1 111111end_time:1Time.at(params[:end_time].to_i)1 1111)111 1111render1json:1result1 end def load_tab @tab = params[:id].to_sym