Titulació: Enginyeria Industrial (pla 2003) Alumne (nom i cognoms): Marcelo Germán López Pérez Títol PFC: Proyecto de diseño de una red inalámbrica de sensores de bajo coste Director del PFC: David González Diez Convocatòria de lliurament del PFC: 2014/15 Q1 Contingut d’aquest volum: -CÓDIGO FUENTE- Índice de contenido 1. Nodo sensor .......................................................................................... 3 2. Programa de volcado de datos ................................................................ 8 2 1. Nodo sensor A continuación se muestra el código programado en Arduino para el nodo sensor. Este programa sirve de base para que los usuarios puedan realizar sus propias aplicaciones. /* Programa del nodo sensor desarrollado para la Wireless Sensor Network Proyecto de diseño de una red inalámbrica de sensores de bajo coste PFC - ETSIAT - UPC Desarrollado por: Marcelo Germán López Pérez Septiembre 2014 */ #include <avr/sleep.h> #include <avr/power.h> uint8_t addr[10]={0x00,0x13,0xA2,0x00,0x40,0xBA,0xBC,0x78,0x00,0x00};//64b its+16bits addresses bool COM=false;//SI EL NODO SE TRABA SE PUEDE REINICIAR EL XBee de forma remota con el comando FR bool sent=false; uint8_t value[5]={0,0,0,0,0}; uint32_t time; uint16_t frecuencia=5000; void pin2Interrupt(void) { /*Función ejecutar al despertarse el nodo La primera acción es desactivar la interrupción Para evitar que salte continuamente.*/ detachInterrupt(0); } void enterSleep(void) { /* setear la interrupción en el pin2*/ attachInterrupt(0, pin2Interrupt, LOW); delay(100); set_sleep_mode(SLEEP_MODE_PWR_DOWN); sleep_enable(); sleep_mode(); /* El nodo se irá a dormer aquí */ 3 /*tras despertarse y quitar la interrupción Lo siguiende es deshabilitar el sleep mode*/ sleep_disable(); } void setup() { Serial.begin(9600); pinMode(2,INPUT); } void loop() { //SI EL XBEE SE ENCUENTRA ENCENDIDO, CONECTADO A LA RED Y NO SE HAN ENVIADO DATOS TODAVIA if(digitalRead(2)==LOW && COM==true && sent==false){ //forma alternativa si el nodo no duerme //if(COM==true && (millis()-time)>frecuencia){ //incluir aquí las funciones para realizar mediciones en los sensores y enviar los datos a través de la red time=millis(); sent=true;//CONFIRMAR CON TX_STATUS } //SI la radio se ha apagado, sent y entrar en modo sleep else if(digitalRead(2)==HIGH){ sent=false; enterSleep(); } } void serialEvent(){ uint8_t frame_type; int packet_length; if(Serial.available()>4){//si se reciben más de cuatro bytes:delimiter, length y frame type if(Serial.read()==0x7E){ packet_length=Serial.read(); packet_length=packet_length*256+Serial.read(); frame_type=Serial.read(); //modificar el número de bytes de Serial_wait se acuerdo a la longitud recibida switch(frame_type){ case 0x8B: if(serial_wait(packet_length)==true)tx_status(); break; case 0x90: if(serial_wait(packet_length)==true)rx_packet(packet_length); break; case 0x8A: if(serial_wait(packet_length)==true)modem_status(); break; default:break; } } } } 4 //funcion necesaria para evitar leer más rapido de lo que el buffer se llena //se podría eliminar aumentadno la velocidad del UART bool serial_wait (uint8_t data_size){//cuidado con la declaracion de variable porque packet_length es int uint32_t timeout; timeout=millis(); while(Serial.available()<data_size && (millis()-timeout)<1000){} if(Serial.available()>=data_size)return true; else return false; } void rx_packet(int packet_size){ uint8_t payload[packet_size-12]; uint8_t check=0x90; for(uint8_t i=0;i<11;i++)check+=Serial.read();//no importa de quien viene la instruccion ni otras caracteristicas sólo se leen los 11 bytes para el checksum for(uint8_t i=0;i<sizeof(payload);i++){ payload[i]=Serial.read(); check+=payload[i]; } check+=Serial.read();//sumar checksum if(check==0xFF){ switch (payload[0]){ case 0x01: //incluir sensores sucseptibles de enviar mediciones por solicitud break; case 0x02: //incluir actuadores susceptibles de ser comandados de forma remota break; case 0x03: //incluir parámetros suceptibles de ser modificados de forma remota break; default: break; } } } void tx_status(){ uint8_t tx_status[7]; uint8_t check=0x8B; for(uint8_t i=0;i<sizeof(tx_status);i++){ tx_status[i]=Serial.read(); check+=tx_status[i]; } if(check==0xFF){ if(tx_status[4]==0x00);//éxito al enviar mensaje posible acción a tomar } } void modem_status(){ uint8_t modem_status[2]; uint8_t check=0x8A; for(uint8_t i=0;i<sizeof(modem_status);i++){ modem_status[i]=Serial.read(); 5 check+=modem_status[i]; } if(check==0xFF){ //checksum incorrecta desde el modem: 0xFF if(modem_status[0]==0x02) COM=true;//radio confirma que está conectada a la red else if(modem_status[0]==0x03)COM=false;//radio confirma que s eha desconectado de la red } } void tx_request(uint8_t *rfdata, int datasize){ uint8_t frame_type=0x10; uint8_t frame_ID=0x01; uint8_t broad_radius=0x00; uint8_t options=0x00; uint8_t checksum=0xFF; //1xframetype, 1xframeid, 8x64bitaddr, 2x16bitaddr, 1xbroadcastradio, 1xoptions int packet_lenght=14+datasize; //cálculo del checksum int sum=frame_type+frame_ID+broad_radius+options; for(uint8_t i=0; i<10; i++){sum+=addr[i];} for(uint8_t i=0; i<datasize;i++){sum+=rfdata[i];} checksum-=lowByte(sum); //transmisión del marco Serial.write(0x7E); Serial.write(highByte(packet_lenght)); Serial.write(lowByte(packet_lenght)); Serial.write(frame_type); Serial.write(frame_ID); for(uint8_t i=0; i<10;i++)Serial.write(addr[i]); Serial.write(broad_radius); Serial.write(options); for(uint8_t i=0; i<datasize;i++)Serial.write(rfdata[i]); Serial.write(checksum); } void ftohex (float number, uint8_t result [5]){ uint16_t aux; if(number<0){ result[0]=0x01; number=abs(number);//eliminar el signo } else result[0]=0x00; aux=number;//extraemos parte entera result[1]=highByte(aux); result[2]=lowByte(aux); //extraer la parte decimal number=number-aux; //A será inferior al número máximo para 16 bits 2^16=65536 //tambien el decimal debe ser mayor a 0, sino se quedaria en el bucle tener en cuenta los bits!!! while(number<6553.6 && number>0)number=number*10; aux=number; result[3]=highByte(aux); result[4]=lowByte(aux); } 6 float hextof(uint8_t data [5]){ int entero=data[1] <<8 +data[2]; float decimal=data[3] <<8 +data[4]; while (decimal>1) decimal=decimal/10; float val=(data[0]*(-2)+1)*(entero+decimal); return val; } 7 2. Programa de volcado de datos A continuación se muestra el código del programa desarrollado en Processing para la recogida y el almacenamiento de los datos provenientes de la red, generación de gráficos y comandos de actuación. /* Programa de procesado de datos para Wireless Sensor Network Proyecto de diseño de una red inalámbrica de sensores de bajo coste PFC - ETSIAT - UPC Desarrollado por: Marcelo Germán López Pérez Septiembre 2014 */ import processing.serial.*; Serial port; PFont font; Table table; Table rxdata; Table gdata; //variables del sistema int active_menu=0; //SON STRINGS porque las rutas van en string pero representan numeros hex porque es lo que se recibe el Xbee String active_node=""; String active_device=""; String active_parameter=""; String title; String title2; int dia=day(); String fecha=Date(); MenuButtons [] botones=new MenuButtons[5]; Parameter list=new Parameter(100, 500); Graphic show; MenuButtons [] points=new MenuButtons[3];//botones del gráfico //variables para actuación MenuButtons send; DragButton command; boolean cmd=false; byte [] value=new byte[5]; // variables para tratamiento de datos rx boolean header=false; int frame_type; 8 int packet_length; byte check=0; //variables para la edicion de la red boolean updated=false; boolean prop=false; int active_ID=0; int active_prop=1; MenuButtons [] edit=new MenuButtons[3];//botones del edit chart TextLine edit_ID=new TextLine("ID", 30, 50, 72); TextLine edit_name=new TextLine("Name", 120, 50, 72); Options edit_role=new Options("","", 42,85); //variables auxiliares para edicion ElementButton auxiliar=new ElementButton("", "", 0, 0, 0); String [] aux={"","","","","","",""}; void setup() { size(1024, 700); port=new Serial(this,Serial.list()[0], 9600); port.buffer(4);//ejecutar serialevent() si se reciben más de cuatro bytes:delimiter, length y frame type //new_node();//si los archivos existen no hace falta crearlos //se puede disminuir el frameRate para disminuir el trabajo del procesador frameRate(15); smooth(); font = loadFont("Tahoma-36.vlw"); textFont(font); //cargar los nodos de la red table= new Table(); rxdata=new Table(); gdata=new Table(); table=loadTable("data/Network.csv","header");//se necesita como minimo el coordinador en la lista para que el programa funcione. //crear los archivos de volcado de datos para todos los dispositivos de la red Filesgen(); //iniciar botones del menu botones[0]=new MenuButtons(width*9/10, height*19/20, 0); botones[1]=new MenuButtons(width*8/10, height*19/20, 1); botones[2]=new MenuButtons(width/20, height/20, 2); botones[3]=new MenuButtons(width/20, height*2/20, 3); botones[4]=new MenuButtons(width*9/10, height/20, 4); //iniciar botones del edit chart edit[0]=new MenuButtons(60,110, 5); edit[1]=new MenuButtons(32,140, 5); edit[2]=new MenuButtons(120,140, 5); edit[0].name="<- Place label"; edit[1].name="OK"; edit[2].name="Cancel"; 9 //inciar el gráfico show=new Graphic(width/2, height*2/5, 600, 320); points[0]=new MenuButtons(width/20, height/2, 5); points[1]=new MenuButtons(width/20, height/2+30, 5); points[2]=new MenuButtons(width/20, height/2+60, 5); points[0].name="Show all"; points[1].name="+50"; points[2].name="-50"; //variables para enviar comando send=new MenuButtons(width*2/3, 550, 5); send.name="Send command"; command=new DragButton(1, width*2/3, 500, 0, 1, 0, "default", "none" ); } void draw(){ ElementButton device=new ElementButton("", "", 0, 0, 0); background(204); fill(0); textSize(24); textAlign(CENTER,CENTER); //network nemu if(active_menu==0){ text("XBee Network", width/2, 24); for(int i=0;i<table.getRowCount();i++){ device.get_device(i);//recupera la información del nodo en la fila i device.draw_label(); } for(int i=0;i<botones.length;i++)if(i!=1)botones[i].draw_label(); } //node menu else if(active_menu==1){ text("Active node:"+title, width/2, 24); for(int i=0;i<table.getRowCount();i++){ device.get_device(i); device.draw_label(); } for(int i=1;i<botones.length;i++)botones[i].draw_label(); } //device menu else if(active_menu==2){ text("Active device:"+title, width/2, 24); text("Active parameter:"+title2, width/2, 48); textSize(14); if(show.fix==true)text("showing: "+show.N+" points", width/2, 96); else text("showing all points ("+gdata.getRowCount()+")", width/2, 96); if(botones[2].state==false && botones[3].state==false && gdata.getRowCount()>0)show.draw_graph();//dibujar si no estamos en modo edicion 10 list.draw_header(); for(int i=0; i< table.getRowCount(); i++){ list.get_line(i); list.draw_line(); } for(int i=1;i<botones.length;i++)botones[i].draw_label(); for(int i=0;i<points.length;i++)points[i].draw_label(); if(cmd==true){ command.draw_drag(); send.draw_label(); } } else active_menu=0; //edit mode if(botones[2].state==true || botones[3].state==true) { if(active_menu==0 || active_menu==1){ if(edit[0].state==true) { auxiliar.pos_x=mouseX; auxiliar.pos_y=mouseY; } auxiliar.draw_label(); } else{ if (botones[2].state==true){ int h=list.position[1]+20*(1+table.getRowCount()); fill(0); textAlign(CENTER, CENTER); text(aux[0],list.position[0],h);//ID text(aux[1],list.position[0]+40,h);//name text(int(aux[2]),list.position[0]+80,h);//type text(aux[3],list.position[0]+120,h);//unit text(int(aux[4]),list.position[0]+160,h);//min text(int(aux[5]),list.position[0]+200,h);//max text(int(aux[6]),list.position[0]+240,h);//default fill(255); ellipse(list.position[0]-20,h,10,10); fill(0); ellipse(list.position[0]-20,h,5,5); textAlign(LEFT); } list.edit_header(); } edit_chart(); } } //Funciones para las comunicaciones //Recepcion de datos por el puerto USB void serialEvent (Serial port){ if(header==false && port.read()==0x7E){ header=true; packet_length=port.read(); packet_length=packet_length*256+port.read(); frame_type=port.read(); check=byte(frame_type); } 11 else if(header==true && port.available()>=packet_length){ byte[] inBuffer = new byte[packet_length]; port.readBytes(inBuffer); for(int i=0;i<packet_length;i++)check+=inBuffer[i]; if(check==-1){ switch(frame_type){ case 0x8B: //if(inBuffer[4]==0)println("success"); //else println("fail"); break; case 0x90: rx_packet(inBuffer); break; case 0x8A: modem_status(inBuffer[0]); break; default: break; } } else println("rx_ERROR"); header=false; //while(port.available()>0)println(port.readBytes()); } } //procesado de datos recibidos void rx_packet(byte rfdata[]){ String hora=Time(); //dirección de origen String node=hex(rfdata[4])+hex(rfdata[5])+hex(rfdata[6])+hex(rfdata[7]); //tipo de mensaje int type=int(rfdata[11]); //dispositivo de origen int device=int(rfdata[12]); if (dia!=day()){ dia=day(); fecha=Date(); Filesgen(); } //si es un mensaje de medicion if (type==0x01){ //al paquete recibido le restamos la dirección, las opciones, el checksum, el dispositivo y el tipo de dato y nos quedan las mediciones int messurements=(packet_length-14)/5; float [] values=new float[messurements]; for(int i=0; i<messurements; i++){ int position=13+5*i; int sign=int(rfdata[position])*(-2)+1; int entero=int(rfdata[position+1])*256+int(rfdata[position+2]); float decimal=float(rfdata[position+3])*256+int(rfdata[position+4]); while (decimal>1) decimal=decimal/10; values[i]=sign*(entero+decimal); } //guardar las mediciones 12 rxdata=loadTable("data/"+fecha+"/"+node+"/"+hex(device,2)+".csv", "header"); TableRow newRow = rxdata.addRow(); newRow.setString(0, hora); for (int i=0; i<messurements; i++){ newRow.setFloat(i+1, values[i]); } saveTable(rxdata, "data/"+fecha+"/"+node+"/"+hex(device,2)+".csv"); //cargar el nuevo punto en la tabla del grafico requiere enviar todos los parámetros de un sensor en un mismo mensaje if(active_menu==2){ if(unhex(active_node)==unhex(node) && unhex(active_device)==device){ //cargar la medida para el parámetro en gráfico show.new_point(hora, values[int(active_parameter)-1], int(active_parameter)); /*esta estructura obliga a enviar las medidas en el mismo orden en que se han creado los parametros y en que estarán las columnas de las tablas de datos*/ //mostrar últimas medidas for(int i=0; i<messurements; i++) table.setString(i, 7, hora+" --> "+values[i]); } } } } void tx_request(byte []command, int commandsize){//http://stackoverflow.com/questions/12020344/passing -an-array-to-a-function-and-using-sizeof-in-arduino byte frame_type=byte(0x10); int lowint=unhex(active_node); byte [] addr={byte(0), byte(19), byte(162), byte(0), 0, 0, 0, 0, byte(255), byte(254)};//0013A200+ low part+ 16 bit unknown //pasar el string para formar la direccion for(int i=0; i<4; i++)addr[7-i]=byte(lowint >>8*i); byte frame_ID=byte(0x01); byte broad_radius=byte(0x00); byte options=byte(0x00); byte checksum=byte(0xFF); byte [] payload=new byte[2+commandsize+2];//getrowcount() payload[0]=byte(0x02); payload[1]=byte(int(active_device)); int position=2; for(int i=0; i<table.getRowCount(); i++){ if(i!=int(active_parameter)-1){ payload[position]=-1;//byte(0xFF) position++; } 13 else{ for(int j=0; j<commandsize; j++)payload[position+j]=command[j]; position+=5; } } //1xframetype, 1xframeid, 8x64bitaddr, 2x16bitaddr, 1xbroadcastradio, 1xoptions int packet_lenght=14+payload.length; int sum=frame_type+frame_ID+broad_radius+options; for(int i=0; i<10; i++){sum+=int(addr[i]);} for(int i=0; i<payload.length;i++)sum+=int(payload[i]); checksum-=byte(sum); /*println(hex(0x7E, 2)); println(hex(packet_lenght >> 8, 2)); println(hex(packet_lenght, 2)); println(hex(frame_type)); println(hex(frame_ID)); for(int i=0; i<10;i++)println(hex(addr[i])); println(hex(broad_radius)); println(hex(options)); for(int i=0; i<payload.length;i++)println(hex(payload[i])); println(hex(checksum));*/ port.write(byte(0x7E)); port.write(byte(packet_lenght >> 8)); port.write(byte(packet_lenght)); port.write(frame_type); port.write(frame_ID); for(int i=0; i<10;i++)port.write(addr[i]); port.write(broad_radius); port.write(options); for(int i=0; i<payload.length;i++)port.write(payload[i]); port.write(checksum); } void modem_status(byte status){ if (int(status)==0x00)println("dispositivo reiniciado"); else if(int(status)==0x06)println("coordinador iniciado"); } // Funciones relacionadas con el teclado y el ratón void mouseReleased(){ int i=0; boolean pressed=false; ElementButton device=new ElementButton("", "", 0, 0, 0);//inciado necesario if(mouseButton==LEFT){ //network menu if(active_menu==0){ //comprobar dispositivos en pantalla while(i<table.getRowCount() && pressed==false){ device.get_device(i); pressed=device.over(); 14 if(pressed==true && (botones[2].state==false && botones[3].state==false))device.action(); else if(pressed==true && botones[3].state==true){ active_node=device.ID;//seleccionar nodo a editar active_ID=i;//dispositivo activo correspondiente al que se encuentra en la fila i de la tabla updated=false; } else i++; } if(pressed==false){ i=0; //comprobar botones del menu en pantalla while(i<botones.length && pressed==false){ pressed=botones[i].over(); if(pressed==true && (botones[2].state==false && botones[3].state==false))botones[i].action(); else i++; } } } //node menu else if(active_menu==1){ //comprobar dispositivos en pantalla i=1; //ignorar la radio while(i<table.getRowCount() && pressed==false){ device.get_device(i); pressed=device.over(); if(pressed==true && (botones[2].state==false && botones[3].state==false))device.action(); else if(pressed==true && botones[3].state==true){ active_device=device.ID;//seleccionar dispositivo a editar active_ID=i;//dispositivo activo correspondiente al que se encuentra en la fila i de la tabla updated=false; } else i++; } if(pressed==false){ i=0; //comprobar botones en pantalla while(i<botones.length && pressed==false){ pressed=botones[i].over(); if(pressed==true && (botones[2].state==false && botones[3].state==false))botones[i].action(); else i++; } } } //device menu else { while (i<table.getRowCount() && pressed==false && botones[2].state==false){ pressed=list.over(i); if(pressed==true && (botones[2].state==false && botones[3].state==false))list.action(i); else if(pressed==true && botones[3].state==true){ 15 active_parameter=table.getString(i,0);//seleccionar dispositivo a editar active_ID=i;//dispositivo activo correspondiente al que se encuentra en la fila i de la tabla updated=false; } else i++; } if(pressed==false){ i=0; //comprobar botones en pantalla while(i<botones.length && pressed==false){ pressed=botones[i].over(); if(pressed==true && (botones[2].state==false && botones[3].state==false))botones[i].action(); else i++; } } //comprobar botones graph if(pressed==false){ i=0; //comprobar botones en pantalla while(i<points.length && pressed==false){ pressed=points[i].over(); if(pressed==true && i==0)show.fix=!show.fix; //se limita a 500 el número de puntos en pantalla else if(pressed==true && i==1 && show.N<500)show.N=show.N+50; else if(pressed==true && i==2 && show.N>50)show.N=show.N-50; else i++; } if(pressed==true){ gdata=loadTable("data/"+fecha+"/"+active_node+"/"+active_device+". csv", "header"); //eliminar línea por defecto String [] m1=match(gdata.getString(0, 0),"del"); if(m1!=null)gdata.removeRow(0); gdata.addColumn("Y"); show.column=int(active_parameter); if (gdata.getRowCount()>0)show.prepare_data(); } } if(pressed==false && cmd==true){ pressed=send.over(); if(pressed==true){ ftohex(command.value, value); tx_request(value, value.length); } } } //edit mode active if (botones[2].state==true || botones[3].state==true){ if(active_menu==0 || active_menu==1){ if(edit[0].over()==true)edit[0].state= !edit[0].state;//activar place device 16 else if (edit[0].state==true && edit[1].over()==false)edit[0].state=false;//situar nodo } else { i=1; while(i<7 && pressed==false){//se puden modificar 7 propiedades pressed=list.over_prop(i); if(pressed==true){ active_prop=i; prop=false; } else i++; } } //over ID TextLine if(edit_ID.over()==true) edit_ID.active=true; else edit_ID.active=false; //over Name TextLine if(edit_name.over()==true) edit_name.active=true; else edit_name.active=false; //option selected edit_role.over(); if (edit[1].over()==true && edit[0].state==false) {//guardar cambios close_edit(); botones[2].state=false; botones[3].state=false; edit_ID.word=""; } else if (edit[2].over()==true) {//cancelar cambios botones[2].state=false; botones[3].state=false; edit_ID.word=""; } } } } void mouseDragged(){ if(command.over()==true)command.action(); } void keyTyped() { if(edit_ID.active==true)edit_ID.type(); else if(edit_name.active==true)edit_name.type(); } //Funciónes relacionadas con el modo de edición //función del cuadro de edición void edit_chart(){ ElementButton device=new ElementButton("", "", 0, 0, 0); rectMode(CORNER); fill(204); rect(10,10,200,150); fill(0); 17 textSize(14); text("Edition chart", 75,30); //dibujar botones del chart for (int i=0; i<edit.length; i++)edit[i].draw_label(); //menus de la red o del nodo if(active_menu==0 || active_menu==1){ if(botones[3].state==true && updated==false){//actualizar el chart si seleccionamos un nodo diferente del actual device.get_device(active_ID); updated=true; //obtener propiedades del dispositivo seleccionado auxiliar.ID=device.ID; auxiliar.name=device.name; auxiliar.role=device.role; auxiliar.pos_x=device.pos_x; auxiliar.pos_y=device.pos_y; //pasarlas para que sean dibujadas en el chart edit_ID.word=auxiliar.ID; edit_name.word=auxiliar.name; edit_role.current=boolean(auxiliar.role-1-2*active_menu); } if(active_menu==0){//dibujar edit_role edit_role.names[0]="Router"; edit_role.names[1]="End. dev"; auxiliar.role=int(edit_role.current)+1; } else if(active_menu==1){ edit_role.names[0]="Sensor"; edit_role.names[1]="Actuator"; auxiliar.role=int(edit_role.current)+3; } edit_ID.draw_text();//dibujar lineas de texto edit_name.draw_text(); edit_role.draw_options(); auxiliar.ID=edit_ID.word; auxiliar.name=edit_name.word; } //menu del dispositivo else{ if(botones[3].state==true && updated==false){// si se cambia se parámetro //cargar el parámetro a modificar aux[0]=table.getString(active_ID, 0);//ID aux[1]=table.getString(active_ID, 1);//name aux[2]=str(table.getInt(active_ID, 2));//type aux[3]=table.getString(active_ID, 3);//units aux[4]=str(table.getInt(active_ID, 4));//min aux[5]=str(table.getInt(active_ID, 5));//max aux[6]=str(table.getInt(active_ID, 6));//default //actualizar propiedad actual edit_ID.word=aux[0]; edit_name.word=aux[active_prop]; updated=true; }//si se cambia de propiedad del parámetro else if(prop==false){ edit_name.word=aux[active_prop]; prop=true; } 18 edit_ID.draw_text(); edit_name.draw_text(); aux[0]=edit_ID.word; aux[active_prop]=edit_name.word; } } //Función encargada de guardar los cambios del modo de edición void close_edit(){ ElementButton device=new ElementButton("", "", 0, 0, 0); //modificar nodo if(active_menu==0){ if(botones[2].state==true){//if add node if(edit_ID.word!=""){//si nuevo nodo, agregar linea en la tabla y crear archivo de dispositivos del nodo TableRow newRow = table.addRow(); newRow.setString(0, auxiliar.ID);//ID newRow.setString(1, auxiliar.name); newRow.setInt(2, auxiliar.pos_x);//X newRow.setInt(3, auxiliar.pos_y);//Y newRow.setInt(4, auxiliar.role); //ROLE saveTable (table,"data/Network.csv"); //crear archivo de devices del nuevo nodo String [] new_node={"ID,name,X,Y,role", auxiliar.ID+","+auxiliar.name+","+str(width/2)+","+str(height/2)+" ,"+str(auxiliar.role) }; saveStrings("data/nodes/"+auxiliar.ID+"/devices.csv", new_node); //crear archivo de estado de comandos de la nueva radio en el nodo String [] new_radio={"command,value,,,,,,", "SH,0013A200,,,,,,", "SL,"+auxiliar.ID+",,,,,,", "ID,2014,,,,,,", "AP,1,,,,,,", "SM,0,,,,,,", "SP,3E8,,,,,,", "ST,1388,,,,,,", "SN,1,,,,,,", "SO,00,,,,,,"}; saveStrings("data/nodes/"+auxiliar.ID+"/"+auxiliar.ID+".csv", new_radio); } }//if edit node else{ if(edit_ID.word=="" && active_ID!=0 ){//delete node table.removeRow(active_ID); saveTable (table,"data/Network.csv"); active_ID=0;//para reiniciar } else if (edit_ID.word!=""){//condicion para poder modificar el nodo coordinador //modificar tabla de nodos table.setString(active_ID, 0, auxiliar.ID);//ID table.setString(active_ID, 1, auxiliar.name); table.setInt(active_ID, 2, auxiliar.pos_x);//X table.setInt(active_ID, 3, auxiliar.pos_y);//Y 19 if(active_ID!=0) table.setInt(active_ID, 4, auxiliar.role); //ROLE saveTable (table,"data/Network.csv"); //cargar y modificar la tabla de dispositivos del nodo //primera linea corespondiente a la radio del nodo //si se cambia el nombre no podrá cargar el archivo del nodo, por eso cargamos el String active_node table.clearRows(); table=loadTable("data/nodes/"+active_node+"/devices.csv","header") ; table.setString(0,0, auxiliar.ID);//ID table.setString(0,1, auxiliar.name);//name //no se modifica la posición del nodo en el menu del nodo //table.setInt(0,"X", auxiliar.pos_x); //table.setInt(0,"Y", auxiliar.pos_y); if(active_ID!=0) table.setInt(0,"role", auxiliar.role); saveTable (table,"data/nodes/"+auxiliar.ID+"/devices.csv"); //volver a cargar los nodos de la red, se podrían utilizar dos tablas table.clearRows(); table=loadTable("data/Network.csv","header"); } } } //modificar dispositivo else if(active_menu==1){ if(botones[2].state==true){//if add device if(edit_ID.word!=""){ //modificar la tabla del nodo activo TableRow newRow = table.addRow(); newRow.setString(0, auxiliar.ID);//ID newRow.setString(1, auxiliar.name);//name newRow.setInt(2, auxiliar.pos_x);//X newRow.setInt(3, auxiliar.pos_y);//Y newRow.setInt(4, auxiliar.role);//ROLE saveTable (table,"data/nodes/"+active_node+"/devices.csv"); //crear archivo del device S o A y el primer parametro //columna last no imprescindible String [] new_node={"ID,name,type,unit,min,max,default,last", "01,default,0,0,0,0,0,0"}; saveStrings("data/nodes/"+active_node+"/"+auxiliar.ID+".csv", new_node); //crear el archivo de datos para el parámetro por defecto que se crea con el dispositivo String [] header={"time,01", "del,0"}; saveStrings("data/"+fecha+"/"+active_node+"/"+auxiliar.ID+".csv", header); } }//if edit node else{ 20 //delete node if(edit_ID.word=="" && active_ID!=0 ){ table.removeRow(active_ID); saveTable (table,"data/nodes/"+active_node+"/devices.csv"); active_ID=0; } else if(edit_ID.word!="") { //modificar tabla del nodo table.setString(active_ID, 0, auxiliar.ID);//ID table.setString(active_ID, 1, auxiliar.name);//name table.setInt(active_ID, 2, auxiliar.pos_x);//X table.setInt(active_ID, 3, auxiliar.pos_y);//Y if(active_ID!=0) table.setInt(active_ID, 4, auxiliar.role); //ROLE saveTable (table,"data/nodes/"+active_node+"/devices.csv"); //cargar y modificar la tabla de dispositivos del nodo //primera linea corespondiente a la radio del nodo //si se cambia el nombre no podrá cargar el archivo del dispositivo, por eso cargamos el String active_device table.clearRows(); table=loadTable("data/nodes/"+active_node+"/"+active_device+".csv" ,"header"); table.setString(0, 0, auxiliar.ID);//ID table.setString(0, 1, auxiliar.name);//name //no se modifica la posición del nodo en para el menu 1 //table.setInt(0,"X", auxiliar.pos_x);//no se modifica la posición del nodo en para el menu 1 //table.setInt(0,"Y", auxiliar.pos_y); saveTable (table,"data/nodes/"+active_node+"/"+auxiliar.ID+"/devices.csv"); //volver a cargar los nodos de la red, se podrían utilizar dos tablas table.clearRows(); table=loadTable("data/nodes/"+active_node+"/devices.csv","header") ; } } } //modificar parametro else{ if(botones[2].state==true){ //modificar la tabla del nodo activo TableRow newRow = table.addRow(); newRow.setString(0, hex(table.getRowCount(), 2));//ID de la fila a la que corresponde newRow.setString(1, aux[1]);//name newRow.setInt(2, int(aux[2]));//type newRow.setString(3, aux[3]);//unit newRow.setInt(4, int(aux[4]));//min newRow.setInt(5, int(aux[5]));//max newRow.setInt(6, int(aux[6]));//default newRow.setString(7, "");//last 21 saveTable (table,"data/nodes/"+active_node+"/"+active_device+".csv"); //crear columna en la tabla graph y guardar la nueva tabla /*puede tardar dependiendo del numero de puntos que haya, aunque en general se hará al momento de configurar el dispositivo por lo que no habrá que considerar muchos puntos, posiblemente*/ String [] head=loadStrings("data/"+fecha+"/"+active_node+"/"+active_device+" .csv"); head[0]="time"; head[1]="del"; for(int i=0; i<table.getRowCount(); i++){ head[0]+=","+table.getString(i, 0); head[1]+=",0"; } saveStrings("data/"+fecha+"/"+active_node+"/"+active_device+".csv", head); } else { //delete parametro /*if(edit_ID.word=="" && active_ID!=0 ){ table.removeRow(active_ID); saveTable (table,"data/nodes/"+active_node+"/"+active_device+".csv"); active_ID=0; //no se borraran los datos captados, al ser borrado de la tabla no se creará la columna en los siguientes archivos } un parametro no se podrá eliminar,´sólo editar su definicion. porque esto está relacionado por como se reciben los datos desde el Arduino. Para eliminar el parámetro habrá que modificar la programación del MCU y eliminar la fila directamente desde el archivo devices, si procede Esto es como medida de seguridad */ //else if(edit_ID.word!="") { //modificar tabla del nodo //table.setString(active_ID, 0, aux[0]);//ID del dispositivo definida por su posicion en la tabla table.setString(active_ID, 1, aux[1]);//name table.setInt(active_ID, 2, int(aux[2]));//type table.setString(active_ID, 3, aux[3]);//unit table.setInt(active_ID, 4, int(aux[4]));//min table.setInt(active_ID, 5, int(aux[5]));//max table.setInt(active_ID, 6, int(aux[6]));//default saveTable (table,"data/nodes/"+active_node+"/"+active_device+".csv"); //si la ID editada es diferente de la existente, habrá que modificar la tabla de datos /*String [] m1=match(active_parameter, aux[0]); if(m1==null);{ String [] head=loadStrings("data/"+fecha+"/"+active_node+"/"+active_device+" .csv"); head[0]="time"; 22 for(int i=0; i<table.getRowCount(); i++){ head[0]+=","+table.getString(i, 0); } saveStrings("data/"+fecha+"/"+active_node+"/"+active_device+".csv", head); }*/ /*si se modifica la ID de un parámetro, habrá que cargar la tabla de datos actual, guardarla con otra nombre y crear un nuevo archivo de datos con los nuevos nombres a aplicar en que se almacenarán los datos a partir de ahora. */ /*editar la ID de los prametros de un sensor se realizará al realizar la configuracion del dispositivo esto significa que todavía no se encontrará activo o estará en proceso de activarse y comenzar a funcionar. Por ello, se puede abrir el archivo de datos en un String para modificar la cabecera*/ //} } } } //A continuación se definen todos las clases diseñadas específicamente para el programa //Linea de texto class TextLine{ String name; String word=""; int [] position=new int[2]; boolean active=false; int h=20; int size; TextLine (String tempname, int X, int Y, int tempW){ name=tempname; position[0]=X; position[1]=Y; size=tempW; } void type(){ if(key==8){ word=""; } else if(key==10)active=false; else { if(textWidth(word)<(size-textWidth(key))){ word+=char(key); //word=word.toUpperCase(); } } } void draw_text(){ stroke(0); 23 rectMode(CORNER); strokeWeight(1); fill(255); rect(position[0],position[1],size,h); fill(0); textSize(h*3/5); textAlign(LEFT, CENTER); text(word, (position[0]+h/4),(position[1]+h/2)); textSize(h*3/5); text(name, position[0], position[1]-h/4); } boolean over(){ if(mouseX>position[0] && mouseX<(position[0]+size) && mouseY>position[1] && mouseY<(position[1]+h))return true; else return false; } } //Opciones disyuntivas class Options{ String[] names=new String[2]; int [] position= new int[2]; boolean current=false; int size=12; int distance=size*6; Options(String op1, String op2, int X, int Y){ names[0]=op1; names[1]=op2; position[0]=X; position[1]=Y; } void draw_options(){ stroke(0); fill(255); ellipse(position[0], position[1], size,size); ellipse(position[0]+distance, position[1], size,size); fill(0); if(current==false)ellipse(position[0], position[1], size/2,size/2); else ellipse(position[0]+distance, position[1], size/2,size/2); textSize(size); textAlign(LEFT, CENTER); text(names[0], position[0]+size, position[1]); text(names[1], position[0]+size+distance, position[1]); } void over(){ if (dist(mouseX, mouseY, position[0], position[1]) < size*0.5){ current=false; } else if (dist(mouseX, mouseY, position[0]+distance, position[1]) < size*0.5){ 24 current=true; } } } //Botón de nivel de parámetro para comandos de actuación class DragButton{ int ID; int[] position= new int[2]; float[] range= new float[2]; float current; float value;//adaptar a cada sensor String name; String unit; int size=20; DragButton(int tempID, int tempX, int tempY, int min, int max, int tempC, String tempName, String tempU){ ID=tempID; position[0]=tempX; position[1]=tempY; range[0]=min; range[1]=max; value=tempC; current=int(map(value,range[0],range[1],position[0],position[0]+si ze*10));//adaptar a cada sensor name=tempName; unit=tempU; } void action(){ if(mouseX<position[0])current=position[0]; else if(mouseX>(position[0]+size*10))current=position[0]+size*10; else current=mouseX; if(range[1]-range[0]==1){ if(mouseX>(position[0]+size*5))value=range[1]; else value=range[0]; } else value=map(current,position[0],position[0]+size*10,range[0],range[1 ]); } void draw_drag(){ ellipseMode(CENTER); strokeWeight(4); stroke(0); line(position[0],position[1],position[0]+size*10,position[1]); strokeWeight(1); fill(150); ellipse(current,position[1], size,size); fill(0); textSize(size*3/5); text(name+": "+str(int(value))+" "+unit, position[0], position[1]-size); 25 } boolean over(){ float d = dist(mouseX, mouseY, current, position[1]);//análisis de posición del raton if (d < size*0.5) return true;//size es el diametro del circulo else return false; } } //Gráficos de datos class Graphic{ int[] position=new int[2]; int[] size=new int[2]; float[] limits_y=new float[2]; String[] limits_x=new String[2]; boolean fix=true; int N=50; int column; Graphic(int tempX, int tempY, int tempW, int tempH){ position[0]=tempX; position[1]=tempY; size[0]=tempW; size[1]=tempH; } void prepare_data(){//cada vez que cambie fix se ejecutará esta función float y; //si hay mas filas al numero indicado eliminar las sobrantes if (fix==true && gdata.getRowCount()>N) while(gdata.getRowCount()>N)gdata.removeRow(0); //determinar un maximo y minimo para empezar limits_y[0]=gdata.getFloat(0, column); limits_y[1]=gdata.getFloat(0, column); //determinar limites en y for(int i=1; i<gdata.getRowCount(); i++){ if(gdata.getFloat(i, column)<limits_y[0])limits_y[0]=gdata.getFloat(i, column); else if(gdata.getFloat(i, column)>limits_y[1])limits_y[1]=gdata.getFloat(i, column); } //determinar limites en X limits_x[0]=gdata.getString(0,0); limits_x[1]=gdata.getString(gdata.getRowCount()-1,0); for(int i=0; i<gdata.getRowCount(); i++){ y=map(gdata.getFloat(i, column), limits_y[0], limits_y[1], size[1]*0.025, size[1]*0.975); gdata.setFloat(i, "Y", y); } } void new_point(String time, float val, int col){ float y; //agregar nueva fila TableRow newRow = gdata.addRow(); 26 newRow.setString(0, time); newRow.setFloat(1, val); //si se supera el máximo de filas permitido if(fix=true && gdata.getRowCount()>N){ y=gdata.getFloat(0, col); gdata.removeRow(0); //determinar los extremos de la nueva tabla //la longitud de la tabla no será considerable, por lo que el tiempo no será excesivo (200 puntos max) pero se ha limitado igualmente if(int(y)==limits_y[0] || int(y)==limits_y[1] || val<limits_y[0] || val>limits_y[1]){ float min=gdata.getFloat(0, col); float max=gdata.getFloat(0, col); for(int i=1; i<gdata.getRowCount(); i++){ if(gdata.getFloat(i, col)<min)min=gdata.getFloat(i, col); else if(gdata.getFloat(i, col)>max)max=gdata.getFloat(i, col); } //si los extremos son diferentes a los existentes, establecer los nuevos y recalcular las "Y" de los otros puntos if(min!=limits_y[0] || max!=limits_y[1]){ limits_y[0]=min; limits_y[1]=max; for(int i=0; i<gdata.getRowCount()-1; i++){ y=map(gdata.getFloat(i, col), limits_y[0], limits_y[1], size[1]*0.025, size[1]*0.975); gdata.setFloat(i, "Y", y); } } } limits_x[0]=gdata.getString(0,0); } // si no hay un máximo de filas definido else{ //si el nuevo punto supera los extremos if(val<limits_y[0] || val>limits_y[1]){ //establecer los nuevos limites if (val<limits_y[0]) limits_y[0]=val; else limits_y[1]=val; //recalcular las "Y" de los otros puntos for(int i=0; i<gdata.getRowCount()-1; i++){// la última fila corresponde al punto nuevo que se realiza debajo y=map(gdata.getFloat(i, col), limits_y[0], limits_y[1], size[1]*0.025, size[1]*0.975); gdata.setFloat(i,"Y",y); } } } //calcular la Y del nuevo punto, tanto si han variado los extremos o no y=map(val, limits_y[0], limits_y[1], size[1]*0.025, size[1]*0.975); gdata.setFloat(gdata.getRowCount()-1, "Y", y); //nuevo límite en x if(gdata.getRowCount()==1)limits_x[0]=time;//si es el primer punto de la tabla porque el punto del fue eliminado 27 limits_x[1]=time; } void draw_graph(){ float origin=0; //dibujar los ejes y lineas rectMode(CENTER); fill(0); stroke(255); strokeWeight(2); rect(position[0],position[1],size[0],size[1]); translate(position[0]-size[0]/2,position[1]+size[1]/2); strokeWeight(1); stroke(128); line(0, -size[1]*0.025, size[0], -size[1]*0.025); line(0, -size[1]*0.25, size[0], -size[1]*0.25); line(0, -size[1]*0.5, size[0], -size[1]*0.5); line(0, -size[1]*0.75, size[0], -size[1]*0.75); line(0, -size[1]*0.975, size[0], -size[1]*0.975); //si hay valores positivos y negativos, dibujar el eje x para referencia if(limits_y[0]*limits_y[1]<0){ origin=-map(0, limits_y[0], limits_y[1], size[1]*0.025, size[1]*0.975); line(0, origin,size[0], origin); } //color y tamaño de los puntos stroke(204,102,0); strokeWeight(4); //calculo de posicion y dibujo de los puntos for(int i=0; i<gdata.getRowCount(); i++){ float x=size[0]*(i+1)/(gdata.getRowCount()+1); point(x, -gdata.getFloat(i,"Y")); } if(limits_y[0]==limits_y[1])line(0, -size[1]*0.5, size[0], size[1]*0.5); strokeWeight(1);//reset strokeWeight textSize(12); fill(0); textAlign(RIGHT, CENTER); text(str(limits_y[0]), -5, -size[1]*0.025); text(str(limits_y[1]), -5, -size[1]*0.975); text(str((limits_y[1]+limits_y[0])*0.5), -5, -size[1]*0.5); if(limits_y[0]*limits_y[1]<0) text("0", -5, origin); textAlign(CENTER, CENTER); text(limits_x[0],0,20); text(limits_x[1],size[0],20); //retornar origen translate(-position[0]+size[0]/2,-position[1]-size[1]/2); } } 28 //Elemento para operar las listas de parametros de los dispositivos class Parameter{ int[] position=new int[2];//posición base para dibujar la tabla int row=0; String ID="00"; String name="name"; int type=0; String unit="[]"; int min=0; int max=0; int def=0; String last; Parameter (int temp_X, int temp_Y){ position[0]=temp_X; position[1]=temp_Y; } void action (int i){ active_parameter=table.getString(i,0);//set fila i de la tabla como parámetro activo title2=table.getString(i,1)+" ["+table.getString(i,3)+"]"; //cargar columna de datos del sensor en el archivo de medidas que se cargo cuando se entró el menu 2 para dibujar el gráfico gdata=loadTable("data/"+fecha+"/"+active_node+"/"+active_device+". csv", "header"); //eliminar línea por defecto String [] m1=match(gdata.getString(0, 0),"del"); if(m1!=null)gdata.removeRow(0); gdata.addColumn("Y"); show.column=i+1;//time=columna 0 if (gdata.getRowCount()>0)show.prepare_data(); if(cmd==true){ get_line(i); command.range[0]=min; command.range[1]=max; command.value=def; command.name=name; command.unit=unit; command.action(); } } void get_line(int i){ row=i; ID=table.getString(i,0);//ID name=table.getString(i,1);//name type=table.getInt(i,2);//type unit=table.getString(i,3);//unit min=table.getInt(i,4);//min max=table.getInt(i,5);//max def=table.getInt(i,6);//default last=table.getString(i,7);//last } void draw_header(){ 29 stroke(0); textAlign(CENTER); textSize(12); text("ID", position[0], position[1]); //ID text("Name", position[0]+40, position[1]); //name text("Type", position[0]+80, position[1]); //type text("Unit", position[0]+120, position[1]);//unit text("MIN", position[0]+160, position[1]);//min text("MAX", position[0]+200, position[1]);//max text("Default", position[0]+240, position[1]);//default textAlign(LEFT); text("Last", position[0]+280, position[1]);//last line(position[0]30,position[1]+5,position[0]+410,position[1]+5); } void edit_header(){ stroke(0); fill(255); for (int i=1; i<7; i++){//se modificara´n sólo 7 propiedades ellipse(position[0]+40*i, position[1]-20, 10, 10); if(i==active_prop){ fill(0); ellipse(position[0]+40*i, position[1]-20,5,5); fill(255); } } } void draw_line(){//dibujar la linea correspondiente de la tabla int h=position[1]+20*(1+row); fill(0); textAlign(CENTER, CENTER); text(ID,position[0],h);//ID text(name,position[0]+40,h);//name text(type,position[0]+80,h);//type text(unit,position[0]+120,h);//unit text(min,position[0]+160,h);//min text(max,position[0]+200,h);//max text(def,position[0]+240,h);//default textAlign(LEFT, CENTER); text(last,position[0]+280,h);//last fill(255); stroke(0); ellipse(position[0]-20,h,10,10); if(table.getString(row,0)==active_parameter){ fill(0); ellipse(position[0]-20,h,5,5); } } boolean over(int i){ float d = dist(mouseX, mouseY, position[0]-20, position[1]+20*(1+i));//análisis de posición del raton if (d < 10*0.5) return true;//size es el diametro del circulo else return false; } 30 boolean over_prop(int i){ float d = dist(mouseX, mouseY, position[0]+40*i, position[1]20);//análisis de posición del raton if (d < 10*0.5) return true;//size es el diametro del circulo else return false; } } //Elemento para operar las etiquetas y botones de los nodos y los dispositivos class ElementButton{ String ID; //ID del dispositivo String name; //descripción del dispositivo int size=48; //tamaño del boton int pos_x; //posicion en x del boton int pos_y; //posicion en y del boton int role; //rol del dispositivo E,R,C (comunicacion), S,A (sensor y actuador) color aspecto=color(100,100,100);; ElementButton(String tempID, String tempname, int tempX, int tempY, int tempR){ ID=tempID; name=tempname; pos_x=tempX; pos_y=tempY; role=tempR; } void get_device(int i){ ID=table.getString(i,0); //columna ID name=table.getString(i,1); //columna name pos_x=table.getInt(i,2); //columna X pos_y=table.getInt(i,3); //columna Y //coumna role role=table.getInt(i,4); } void action(){ active_menu++; if(active_menu==1){//cargar dispositivos en el nodo active_node=ID; title=name; table=loadTable("data/nodes/"+ID+"/devices.csv","header"); } else if (active_menu==2){ active_device=ID; if(role==4)cmd=true;//activar el command mode para actuadores title=name; //cargar tabla de parámetros table=loadTable("data/nodes/"+active_node+"/"+ID+".csv","header"); active_parameter=table.getString(0,0);//indicación para marcar el primer parámetro de la lista list.action(0); /* //cargar tabla de datos para el parámetro por defecto 1 31 gdata=loadTable("data/"+fecha+"/"+active_node+"/"+ID+".csv", "header"); //eliminar línea por defecto String [] m1=match(gdata.getString(0, 0),"del"); if(m1!=null)gdata.removeRow(0); gdata.addColumn("Y"); show.column=1; if (gdata.getRowCount()>0)show.prepare_data();*/ } } void draw_label(){// no hacen falta parametros rectMode(CENTER); int text_size=size/4; int over=int(over()); float L=1.25*size; String symbol; switch (role){ case 0: aspecto=color(182,83,83); symbol="C"; //Coordinator break; case 1: aspecto=color(63,121,60); symbol="R"; //Router break; case 2: aspecto=color(80,80,120); symbol="E"; //End device break; case 3: aspecto=color(0,112,192); symbol="S"; //Sensor break; case 4: aspecto=color(255,192,0); symbol="A"; //Actuator break; default:symbol="0"; //otro break; } stroke(0); fill(255); rect(pos_x-(0.5*size), pos_y, 2*L, L); stroke(255); fill(aspecto,255-(80*over)); ellipse(pos_x,pos_y,size, size); fill(255); textSize(text_size*3); textAlign(CENTER, CENTER); text(symbol,pos_x,pos_y); fill(0); textAlign(LEFT, CENTER); textSize(text_size); text(ID, pos_x-(1.3*L), pos_y-(0.3*L)); text (name,pos_x-(1.3*L),pos_y); } boolean over(){ float d = dist(mouseX, mouseY, pos_x, pos_y);//análisis de posición del raton 32 if (d < size*0.5) return true;//size es el diametro del circulo else return false; } } //Botones generales de la aplicación class MenuButtons{ String name; //descripción del dispositivo int size=24; //tamaño del boton int pos_x; //posicion en x del boton int pos_y; //posicion en y del boton int role; boolean state=false; color aspecto=color(15,36,62); MenuButtons(int tempX, int tempY, int tempR){ role=tempR; pos_x=tempX; pos_y=tempY; switch (tempR){ case 0: name="Exit"; //salir break; case 1: name="Back"; //volver break; case 2: name="Add new"; //add device break; case 3: name="Edit selected"; //Edit break; case 4: name="Help"; //help break; default: name=""; break; } } void action(){ switch (role){ case 0: exit(); break; case 1: active_menu--; if(active_menu==0){ table.clearRows(); table=loadTable("data/Network.csv","header"); } else if(active_menu==1){ cmd=false; table.clearRows(); table=loadTable("data/nodes/"+active_node+"/devices.csv","header") ; } break; case 2: state=true; break; case 3: state=true; 33 break; case 4: open("C:/Users/marcelo/Documents/Processing/sketch_140821b/data/he lp.txt"); break; default: //active_menu=0; break; } } void draw_label(){// no hacen falta parametros int text_size=size/4; int over=int(over()); float L=1.25*size; String symbol; switch (role){ case 0: symbol="X"; break; case 1: symbol="<"; break; case 2: symbol="+"; break; case 3: symbol=">"; break; case 4: symbol="?"; break; default: symbol=""; break; } stroke(255); fill(aspecto,255-(80*over)); ellipse(pos_x,pos_y,size, size); //Salir //volver fill(255); textSize(text_size*3); textAlign(CENTER, CENTER); text(symbol,pos_x, pos_y); fill(0); textAlign(LEFT, CENTER); textSize(text_size*2); text(name,pos_x+size, pos_y); } boolean over(){ float d = dist(mouseX, mouseY, pos_x, pos_y);//análisis de posición del raton if (d < size*0.5) return true;//size es el diametro del circulo else return false; } } //A continuacion aparecen las funciones auxiliares que utiliza la aplicación //función para generar los archivos de datos de los dispositivos 34 void Filesgen(){ for(int i=0; i<table.getRowCount(); i++){ active_node=table.getString(i, 0); gdata=loadTable("data/nodes/"+active_node+"/devices.csv","header") ; for(int j=1; j<gdata.getRowCount(); j++){//ignorar la radio active_device=gdata.getString(j, 0); rxdata=loadTable("data/nodes/"+active_node+"/"+active_device+".csv ","header"); /*aparte de la cabecer se requiere de una primera línea, de lo contrario la tabla no se abre igualmente esta primera línea se eliminará al cargar la tabla*/ String [] header={"time", "del"}; for(int k=0; k<rxdata.getRowCount(); k++){ header[0]+=","+rxdata.getString(k, 0); header[1]+=",0"; } saveStrings("data/"+fecha+"/"+active_node+"/"+active_device+".csv", header); rxdata.clearRows(); } } gdata.clearRows(); } //función para convertir datos floats a formato de 5 bytes void ftohex (float number, byte [] result){ int aux; if(number<0){ result[0]=1; number=abs(number);//eliminar el signo } else result[0]=0; aux=int(number);//extraemos parte entera result[2]=byte(aux); result[1]=byte(aux >>8); //extraer la parte decimal number=number-aux; //A será inferior al número máximo para 16 bits 2^16=65536 //tambien el decimal debe ser mayor a 0, sino se quedaria en el bucle tener en cuenta los bits!!! while(number<6553.6 && number>0)number=number*10; aux=int(number); result[4]=byte(aux); result[3]=byte(aux >>8); } //funciones para obtener del sistema la fecha y la hora. String Date(){ String[] Date=new String[3]; Date[2] =nf(day(), 2); Date[1] =nf(month(), 2); Date[0]= nf(year(), 4); 35 return(Date[0]+ '_'+Date[1]+ '_'+Date[2]); } String Time(){ String[] Time=new String[3]; Time[0] =nf(second(), 2); Time[1] =nf(minute(), 2); Time[2]= nf(hour(), 2); return(Time[2]+ ':'+Time[1]+ ':'+Time[0]); } 36