c36dd8883011d9151771597a9432ec3b8b23c35c
[owTools.git] / src / main.cpp
1 // Copyright (c) 2017, Tobias Mueller tm(at)tm3d.de
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 //  * Redistributions of source code must retain the above copyright
9 //    notice, this list of conditions and the following disclaimer.
10 //  * Redistributions in binary form must reproduce the above copyright
11 //    notice, this list of conditions and the following disclaimer in the
12 //    documentation and/or other materials provided with the
13 //    distribution.
14 //  * All advertising materials mentioning features or use of this
15 //    software must display the following acknowledgement: This product
16 //    includes software developed by tm3d.de and its contributors.
17 //  * Neither the name of tm3d.de nor the names of its contributors may
18 //    be used to endorse or promote products derived from this software
19 //    without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
33
34 #include <mysql.h>
35 #include "mySensorDB.h"
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <stdint.h>
39 #include <sys/mman.h>
40 #include <fcntl.h>    /* For O_RDWR */
41 #include <unistd.h>
42 #include <pthread.h>
43 #include <sched.h>
44 #include <memory.h>
45 #include "owTools.h"
46 #include "hexfile.h"
47 #include <iostream>
48 #include <algorithm>
49 #include <fstream>
50 #include <sstream> 
51
52
53
54 #define RB "\e(0\x6a\e(B" // 188 Right Bottom corner
55 #define RT "\e(0\x6b\e(B" // 187 Right Top corner
56 #define LT "\e(0\x6c\e(B" // 201 Left Top cornet
57 #define LB "\e(0\x6d\e(B" // 200 Left Bottom corner
58 #define MC "\e(0\x6e\e(B" // 206 Midle Cross
59 #define HL "\e(0\x71\e(B" // 205 Horizontal Line
60 #define LC "\e(0\x74\e(B" // 204 Left Cross
61 #define RC "\e(0\x75\e(B" // 185 Right Cross
62 #define BC "\e(0\x76\e(B" // 202 Bottom Cross
63 #define TC "\e(0\x77\e(B" // 203 Top Cross
64 #define VL "\e(0\x78\e(B" // 186 Vertical Line
65 #define SP " "            // space string
66         
67
68 void printhelp() {
69 printf("owTools - Programm for reading und  controlling 1-Wire Devices from www.tm3d.de\n\n");
70 printf("run:  owTools -a [COMn|USBn|GPIOn] [options]\n\n");
71 printf("    COMn -> Adapter DS9097 and compatible (e.g. LinkUSB)\n");
72 printf("         n=1 -> Windows COM1 -> Linux /dev/ttyS1\n");
73 printf("    USBn -> Adapter DS2490/DS9490 \n");
74 printf("         n=1 -> first USB-Adapter \n");
75 printf("    GPIOn -> port of Raspberry PI (port Name not Pin number)\n\n");
76 printf("options:\n");
77 printf("   -i interactive mode\n");
78 printf("          select a device and get information about it\n");
79 printf("   -c read and print all continuous\n");
80 printf("      -p time in sec between readings\n");
81 printf("      -d [config file] put all Data in mysql Database descripted in Config file\n");
82 printf("         Config file: \n");
83 printf("            [server]\n");
84 printf("            [port] -> 3306\n");
85 printf("            [user] \n");
86 printf("            [password]\n");
87 printf("            [database]\n");
88 printf("            [prefix] -> a prefix of all tables \n\n");
89 printf("      -r Search everytime for new Devices\n");
90 printf("   -f hexfile  Flash new\n");
91 printf("   -n change id   \n");
92 printf("      -g get from server\n");
93 printf("   -w [1|2] show Warnings (1) or all Infos (2)\n");
94
95
96
97 }
98
99
100 typedef struct {
101         std::string flag;
102         std::string arg;
103 } arg_t ;
104
105
106 //length of shown chars of an UTF8 string ... sometimes not the same as byte count
107 std::size_t utf8_length(std::string const &s) {
108   std::size_t len = 0;
109   std::string::const_iterator begin = s.begin(), end = s.end();
110   while (begin != end) {
111     unsigned char c = *begin;
112     int n=1;
113     if      ((c & 0x80) == 0)    n = 1;
114     else if ((c & 0xE0) == 0xC0) n = 2;
115     else if ((c & 0xF0) == 0xE0) n = 3;
116     else if ((c & 0xF8) == 0xF0) n = 4;
117     
118     len += 1;
119     begin += n;
120   }
121   return len;
122 }
123
124
125 std::vector<arg_t> comlist;
126
127 std::string getArg(std::string flag) {
128         for(arg_t a:comlist) {
129                 if (a.flag==flag) return a.arg;
130         }
131         return ""; 
132 }
133
134
135
136 int getArgi(std::string flag) {
137         return atoi(getArg(flag).c_str());
138 }
139
140
141 int database=0;
142 mySensorDB *sdb=NULL;
143
144
145 snum_t getArgsnum(std::string flag) {
146         snum_t snum;
147         snum.num=0;
148         return snum;
149 }
150
151 int findCPU(std::string cpu) {
152         std::ifstream fileInput;
153         std::string line;
154         fileInput.open("/proc/cpuinfo");
155         if(fileInput.is_open()) {       
156                 for(unsigned int curLine = 0; getline(fileInput, line); curLine++) {
157                         if (line.find(cpu) != std::string::npos) {
158                                 fileInput.close();
159                                 return 1;
160                         }
161                 }
162                 fileInput.close();
163                 
164         }
165         return 0;
166 }
167
168
169
170
171 owInterface *owi= NULL;
172
173 void setLogMode() {
174         if (getArg("w")=="2")
175                         owi->log->setLogLevel(0);
176         else if (getArg("w")=="2") owi->log->setLogLevel(0);
177         else owi->log->setLogLevel(OWLOG_ERROR);
178
179 }
180
181
182 void continuous(std::vector<owDevice*> *devices,int intervall,int headline,int searchrom) {
183         int first=1;
184         while (1) {
185                 if (searchrom) {
186                         owi->log->clear();
187                         owi->Find();
188                         if (owi->isDevicesChanged()||(first==1)) {
189                                 first=0;
190                                 if (headline) {
191                                         printf("                     \t");
192                                         for (owDevice* dev :*devices) {
193                                                 if (dev->configstate!=OWDCS_NONE) {
194                                                         for (size_t i=0;i<4;i++) {
195                                                                 if (dev->config->getPropertyID(i)!=0)
196                                                                         printf("\033[1;34m%02X.%02X%02X\033[0m\t",dev->getNum().byte[7],dev->getNum().byte[1],dev->getNum().byte[0]);
197                                                         }
198                                                 }
199                                         }
200                                         printf("\ntime                 \t");
201                                         for (owDevice* dev :*devices) {
202                                                 if (dev->configstate!=OWDCS_NONE) {
203                                                         for (size_t i=0;i<4;i++) {
204                                                                 if (dev->config->getPropertyID(i)!=0)
205                                                                         printf("\033[0;36m%s\033[0m\t",dev->config->getQuantity(i).substr(0,7).c_str());
206                                                         }
207                                                 }
208                                         }
209                                         printf("\n                     \t");
210                                         for (owDevice* dev :*devices) {
211                                                 if (dev->configstate!=OWDCS_NONE) {
212                                                         for (size_t i=0;i<4;i++) {
213                                                                 if (dev->config->getPropertyID(i)!=0) {
214                                                                         size_t l=utf8_length(dev->config->getUnit(i));
215                                                                         std::string ls="           ";
216                                                                         printf("\033[3;34m[%s]%s\033[0m\t",dev->config->getUnit(i).c_str(),ls.substr(0,5-l).c_str());
217                                                                 }
218                                                         }
219                                                 }
220                                         }
221                                         
222                                 }       
223                                 printf("\n");
224
225                                 if (database) {
226                                         //owi->log->setLogLevel(0);
227                                         for (owDevice* dev : *devices) {
228                                                 sdb->createDeviceTable(dev);
229                                         }
230                                 }
231                         }
232                         
233                 }
234                 time_t t=time(NULL);
235                 int st=(int)t;
236                 struct tm tm = *localtime(&t);
237                 printf("%d-%02d-%02d %02d:%02d:%02d\t", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
238                 for (owDevice* dev :*devices) {
239                         for(int k=0;k<owi->maxrepeat;k++){
240                                 dev->convertAll();
241                                 if (owi->log->last()<OWLOG_ERROR) break;
242                                 owi->log->clear();
243                         }
244                         if (owi->log->last()<OWLOG_ERROR) {
245                                 if (dev->configstate!=OWDCS_NONE) {
246                                         for (size_t i=0;i<4;i++) {                                      
247                                                 if (dev->config->getPropertyID(i)!=0) {
248                                                         if (dev->values[i]<=-10) 
249                                                                 printf("%0.2f \t",dev->values[i]);
250                                                         else if (dev->values[i]<0) 
251                                                                 printf("%0.3f \t",dev->values[i]);
252                                                         else if (dev->values[i]<10) 
253                                                                 printf("%0.4f \t",dev->values[i]);
254                                                         else if (dev->values[i]<100)    
255                                                                 printf("%0.3f \t",dev->values[i]);
256                                                         else if (dev->values[i]<1000)   
257                                                                 printf("%0.2f \t",dev->values[i]);
258                                                         else
259                                                                 printf("%0.1f \t",dev->values[i]);
260                                                         fflush(stdout);
261                                                 }
262                                         }
263                                 }
264                                 if (database) {
265                                         sdb->insertValues(dev);
266                                 }
267                         } else {
268                                 owi->log->setLogLevel(OWLOG_INFO);
269                                 owi->log->set(OWLOG_ERROR,"Too many errors, mybee conection is dead.");
270                                 return;
271                         }
272                         
273                 
274                 }
275                 printf("\n");
276                 while (((int)time(NULL))<(st+intervall)) sleep(1);
277         }
278 }
279
280 void device_menu(owDevice* d) {
281         for(int i=0;i<70;i++) printf(HL);printf("\n");
282         printf("Selected Device: ");
283         snum_t snum=d->getNum();
284         printf("\033[1;34m%016llX\033[0m",(unsigned long long)snum.num);
285         printf("  \n");
286         printf("Chip Info: ");
287         std::vector<std::string> info=d->getFamilyInfo();
288         for (std::string s : info) printf(" %s",s.c_str());
289         printf("\n");
290         printf("Values info: ");
291         int tm3d=d->readConfig();
292         for (size_t i=0;i<4;i++) {
293                 printf("%s in %s",d->config->getQuantity(i).c_str(),d->config->getUnit(i).c_str());
294                 if (i<4) printf("; "); 
295         }
296         if (tm3d) printf(" (tm3d.de)"); else printf(" (default)"); 
297         printf("\n");
298         d->convertAll();
299         for (size_t i=0;i<d->values.size();i++) {
300                 printf("\033[1;33m%0.4f %s\033[0m  ",d->values[i],d->config->getUnit(i).c_str());
301         }
302         printf("\n");
303         for(int i=0;i<70;i++) printf(HL);printf("\n");
304         printf("0) Exit\n");
305         printf("1) Run continuous\n");
306         std::string inp;
307         while (1) {
308                 printf("Select an option: ");std::getline (std::cin,inp);
309                 int dnr=atoi(inp.c_str());
310                 if (dnr<=0) return;
311                 if (dnr==1) {
312                         std::vector<owDevice*> v;
313                         v.push_back(d);
314                         continuous(&v,1,1,0);
315                 } 
316 //                      if (dnr<=i) break; 
317         //              printf("\033[4;33mSelect a number between 0 and %i\033[0m\n",i);
318         }
319         
320         
321 }
322
323 int selectDevice() {
324         owi->Find();
325         int i=0;
326         for (owDevice* dev :owi->devices) {
327                 i++;
328                 snum_t snum=dev->getNum();
329                 printf("%i) ",i);
330                 printf("\033[1;34m%016llX\033[0m",(unsigned long long)snum.num);
331
332                 printf("  ");
333                 for (int j=0;j<4;j++) printf("%s ",dev->config->getQuantity(j).c_str());
334                 printf("\n");
335         }
336         printf("0) Exit Programm\n");
337         std::string inp;
338         int dnr=0;
339         while (1) {
340                 printf("Select device with number: ");std::getline (std::cin,inp);
341                 dnr=atoi(inp.c_str());
342                 if (dnr<=0) return 0;
343                 if (dnr<=i) break; 
344                 printf("\033[4;33mSelect a number between 0 and %i\033[0m\n",i);
345         }
346         return dnr;
347
348 }
349
350 int questionYesNo(std::string text) {
351         std::string inp;
352         printf("%s [Y/n] ",text.c_str());std::getline (std::cin,inp);
353         if (inp=="Y") return 1; else return 0;
354         
355 }
356
357 void interactive(owInterface *owi) {
358         while (1) {
359                 int i=selectDevice();
360                 if (i==0 ) return;
361                 device_menu(owi->devices[i-1]);
362         }
363 }
364
365 int main(int argc, char *argv[]) {
366         int i;
367         arg_t a;
368         //printf("Kommando: %s:\n", argv[0]);
369         while (--argc) {
370                 if (**++argv != '-') {
371         //printf("   Arg. = %s\n", *argv);
372         if (comlist.empty()) {
373                 a.flag=".";
374                 a.arg=*argv;
375                 comlist.push_back(a);
376         } else {
377                 if ((*(comlist.end()-1)).arg!="1") {
378                         a.flag=".";
379                         a.arg=*argv;
380                         comlist.push_back(a);
381                 } else {
382                         (*(comlist.end()-1)).arg=*argv;
383                 }
384         }
385                 } else {
386                         for( i=1; (*argv)[i] != '\0'; i++) {
387                                 //printf("   Flag = (%c)\n", (*argv)[i]);
388                                 a.flag=(*argv)[i];
389                                 a.arg="1";
390                                 comlist.push_back(a);
391                         }
392                 }
393     }          
394
395         //for(arg_t a:comlist) printf("%s->%s\n",a.flag.c_str(),a.arg.c_str());
396         if (getArg("h")=="1") {
397                 printhelp();
398                 return 0;
399         }
400         std::string adapter=getArg("a");
401         if (adapter.empty()) {
402                 printhelp();
403                 return 0;
404         }       
405         std::transform(adapter.begin(), adapter.end(),adapter.begin(), ::toupper);
406
407         std::string s;
408         
409         if(adapter.find("COM")!=std::string::npos) {
410                 owi=new owCOMInterface();
411                 int port=atoi(adapter.substr(adapter.find("COM")+3).c_str());
412                 printf("Open /dev/ttyS%i\n",port);
413                 owi->InitAdapter(port);
414         } else 
415         if(adapter.find("ARDUINO")!=std::string::npos) {
416                 owi=new owARDUINOInterface();
417                 int port=atoi(adapter.substr(adapter.find("ARDUINO")+7).c_str());
418                 printf("Open /dev/ttyS%i\n",port);
419                 owi->InitAdapter(port);
420         } else 
421         if(adapter.find("USB")!=std::string::npos) {
422                 owi=new owUSBInterface();
423                 int port=atoi(adapter.substr(adapter.find("USB")+3).c_str());
424                 printf("Open the %i. USB Adapter\n",port);
425                 int err;
426                 if ((err=owi->InitAdapter(port))<0) {
427                         if (err==-1) printf("No Adapter found!\n  On Linux, try: sudo owTools....\n  On Windows: Install libusb0 driver\n");
428                         if (err==-2) printf("Maybe Adapter is used ... (try sudo)\nOn Linux: If the kernel modul is loaded type: sudo rmmod ds2490\n");
429                         exit(0);
430                 }
431         } else 
432         if(adapter.find("GPIO")!=std::string::npos) {
433                 if (findCPU("BCM2708")) {
434                         printf("\033[1;33mRaspberry Pi 1 is dedected. 1-wire on the GPIO port can have many errors in the transfer.\033[0m\n");
435                         owi=new owPiGPioInterface();
436                         int port=atoi(adapter.substr(adapter.find("GPIO")+4).c_str());
437                         printf("Open GPIO %i\n",port);
438                 } else if (findCPU("BCM2709")) {
439                         owi=new owPiGPioInterface();
440                         int port=atoi(adapter.substr(adapter.find("GPIO")+4).c_str());
441                         printf("Open GPIO %i\n",port);
442                         owi->InitAdapter(port);
443                 } else {
444                         printf("\033[1;31mGPIO works with Raspberry PI only \033[0m\n");
445                 }
446         }  
447         if (owi==NULL) {
448                 printf("No 1-Wiremaster found\n");
449                 return 0;
450         }
451
452         setLogMode();
453         
454         
455
456         if (getArg("i")=="1") {
457                 interactive(owi);       
458         } else 
459         if (getArg("c")=="1") {
460                 int reload=(getArg("r")=="1");
461                 owi->Find();
462                 printf("\n");
463                 //owi->log->setLogLevel(3);
464                 std::string s;
465                 int pause;
466                 if ((s=getArg("p")) !="") {
467                         pause=atoi(s.c_str());
468                 } else pause=30;
469                 if  ((s=getArg("d")) !="") {
470                         reload=1;
471                         printf("Use Database\n");
472                         sdb=new mySensorDB(s,owi->log);
473                         if (sdb->log->last()>OWLOG_WARNING) {
474                                 exit(1);
475                         }
476                         sdb->connect();
477                         if (sdb->log->last()>OWLOG_WARNING) {
478                                 exit(1);
479                         }
480                         sdb->createSensorTable();
481                         if (sdb->log->last()>OWLOG_WARNING) {
482                                 exit(1);
483                         }                               
484                         database=1;
485                 }       
486                         
487                 
488                 continuous(&(owi->devices),pause,1,reload);     
489         } else 
490         if ((s=getArg("f"))!="") {
491                 printf("Flash %s in selected Device\n",s.c_str());
492                 int sel=selectDevice();
493                 if (sel==0) exit(0);
494                 int resetid=questionYesNo("Would you reset to default ID? (This is necessary if another device is used!)");
495                 owi->log->setLogLevel(OWLOG_INFO);              
496                 owi->flashHEXFile(s,owi->device_nums[sel-1],resetid,1);
497                 setLogMode();
498                 owi->Clean();
499                 owi->Find();
500                 for (owDevice* dev :owi->devices) {
501                         snum_t snum=dev->getNum();
502                         printf("\033[1;34m%016llX\033[0m",(unsigned long long)snum.num);
503
504                         printf("\n");
505                 }
506                 printf("%i Devices found\n",(int)owi->devices.size());  
507                                 
508         } else 
509         if ((s=getArg("v"))!="") {
510                 printf("Reset VOC\n");
511                 int sel=selectDevice();
512                 if (sel==0) exit(0);
513                 owDevice* dev=owi->devices[sel-1];
514                 std::vector<uint8_t> cl;
515                 cl.push_back(0x4E);
516                 cl.push_back(0x1F);
517                 cl.push_back(0x1F);
518                 cl.push_back(0x1F);
519                 dev->Communicate(&cl,4,0);
520         } else 
521         
522         if ((s=getArg("n"))!="") {
523                 printf("Change id of selected Device\n");
524                 int sel=selectDevice();
525                 if (sel==0) exit(0);
526                 sel-=1;
527                 
528                 snum_t snum=owi->devices[sel]->getNum();
529                 if ((getArg("g"))=="1") {
530                         printf("get ID from Server\n");
531                         char s[255];
532                         sprintf(s,"wget -q http://www.tm3d.de/shop99/owid.php?fam=%02X -O id.txt",snum.byte[0]);
533                         int err;
534                         if ((err=system(s))==0) {
535                                 printf("OK!\n");
536                                 std::string rs;
537                                 std::stringstream str;
538                                 std::ifstream fs;
539                                 fs.open ("id.txt" );
540                                 snum_t isnum;
541                                 int i=0; int br=0;
542                                 if(fs.is_open()) {
543                                 while(fs.peek() != EOF) {
544                                         char c= (char) fs.get();
545                                         if (br==0) {
546                                                 if (c=='x') br=1;
547                                         } else {
548                                                 if ((c==',')|(c=='}')) {
549                                                         isnum.byte[i]=strtol(s, NULL, 16);
550                                                         //printf("%x\n",strtol(s, NULL, 16));
551                                                         i++;
552                                                         br=0;
553                                                         s[0]=0;
554                                                 } else {
555                                                         s[br-1]=c;
556                                                         s[br]=0;
557                                                         br++;
558                                                 }
559                                         }
560                                         if (c=='}') break;
561                                 }
562                                 
563                                 fs.close();
564                                 }                                         
565                                 snum.num=isnum.num;
566                         } else (printf("ERROR %i\n",err));
567                 } else {
568                         snum.num+=256;
569                 }
570
571
572                 printf("New ID: ");
573                 printf("\033[1;34m%016llX\033[0m",(unsigned long long)snum.num);
574
575                 printf("\n");
576                 
577                 owi->devices[sel]->changeID(snum);              
578         
579                 owi->Clean();
580                 owi->Find();
581                 for (owDevice* dev :owi->devices) {
582                         snum_t snum=dev->getNum();
583                         printf("\033[1;34m%016llX\033[0m",(unsigned long long)snum.num);
584
585                         printf("\n");
586                 }
587                 printf("%i Devices found\n",(int)owi->devices.size());  
588                                 
589         } else {
590                 owi->Find();
591                 for (owDevice* dev :owi->devices) {
592                         printf("_______________________________________________________________________\n");
593                         snum_t snum=dev->getNum();
594                         printf("\033[1;34m%016llX\033[0m ",(unsigned long long)snum.num);
595                         switch (dev->configstate) {
596                                 case OWDCS_16:printf("old Configcode from www.tm3d.de");break;
597                                 case OWDCS_24:printf("Configcode from www.tm3d.de");break;
598                                 case OWDCS_DEFAULT:printf("Default Configcode set by this Software");break;
599                                 case OWDCS_NONE:printf("No Configcode");break;
600                         }
601                         printf( " - (");
602                         for(int i=0;i<3;i++)
603                                 printf(" %s",dev->getFamilyInfo()[i].c_str());
604                         printf(" )\n");
605                         if (dev->configstate!=OWDCS_NONE) {
606                                 std::vector<std::vector<std::string>> ia;
607                                 std::vector<size_t> maxcol;
608                                 dev->convertAll();
609                                 for(int i=0;i<4;i++) {
610                                         std::vector<std::string> colm;
611                                         size_t max=0;
612                                         colm.push_back(dev->config->getSensorChip(i));
613                                         if (max<utf8_length(colm[0])) max=utf8_length(colm[0]);
614                                         colm.push_back(dev->config->getQuantity(i));
615                                         if (max<utf8_length(colm[1])) max=utf8_length(colm[1]);
616                                         colm.push_back(dev->config->getUnit(i));
617                                         if (max<utf8_length(colm[2])) max=utf8_length(colm[2]);
618                                         if (dev->config->getPropertyID(i)==0) {
619                                                 colm.push_back("");
620                                         } else {
621                                                 char hs[50];
622                                                 sprintf(hs,"%0.3f",dev->values[i]);
623                                                 colm.push_back(hs);
624                                         }
625                                         if (max<utf8_length(colm[3])) max=utf8_length(colm[3]);
626                                         ia.push_back(colm);
627                                         maxcol.push_back(max);
628                                 }
629                                 std::string space="                                                          ";
630                                 for(int i=0;i<4;i++) {
631                                         for(int j=0;j<4;j++) {
632                                                 printf("%s%s",ia[j][i].c_str(),space.substr(0,maxcol[j]-utf8_length(ia[j][i])+2).c_str());
633                                         }
634                                         printf("\n");
635                                 }
636                         }
637                 }
638                 printf("%i Devices found\n",(int)owi->devices.size());
639         }
640         return 0;
641    
642 }