
Nella prima parte di questo articolo ho illustrato come creare le tabelle del DB dove registrare i dati provenienti dal tracker GPS, le principali funzionalità dello script e la possibilità di “agganciare” questo socket direttamente al software Open Source: OpenGTS.
Quest’ultima caratteristica, la ritengo particolarmente interessante in quanto permette di evitare di scriversi tutta la parte che riguarda la visualizzazione dei punti inviati dal tracker, sulle mappe.
Chiave | Valori possibili | Descrizione |
---|---|---|
VERBOSE | true|false | Se impostato a true fornisce un output dettagliato degli errori |
MOVING_THRESHOLD | .05 | Soglia minima in Km per la registrazione del dato [.05 = 50 metri] |
OPENGTS | true|false | Se impostato a true invia query per le tabelle OpenGTS |
IP_ADDR | xxx.xxx.xxx.xxx | Indirizzo IP in ascolto [0 = tutti gli indirizzi] |
TCP_PORT | 0..65535 | La porta TCP da utilizzare |
DBHOST | localhost | Indirizzo del DB MySQL |
DBUSER | dbuser | utente del DB MySQL |
DBPASS | dbpassword | password dell’utente del DB MySQL |
DBNAME | gpsd | nome del DB MySQL |
POLL_TIME | 20|30|60|300|600 | Tempo di polling del tracker in secondi |
SPEED_CONV | 1.609344|1.852 | Conversione da Miglia (terrestri|marine) a Km |
DFLT_MSG | ‘tracker’ | Messaggio di default del tracker |
SOCK_RCV_TIMEOUT | 120 | Timeout in secondi per il socket in ricezione |
Ed ecco il codice PHP del loop del socket (L’intero script è disponibile qui):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | #!/usr/bin/env php < ?php /*********************************************************************** * SETTINGS * ********************************************************************/ define("VERBOSE", true); define("MOVING_THRESHOLD",.04); //.05 = 50 metres define("OPENGTS",false); /* HOST */ define("IP_ADDR","0"); // "0" = listen all ip define("TCP_PORT",5050); /* DATABASE */ define("DBHOST","localhost"); define("DBUSER","dbuser"); define("DBPASS","dbpassword"); define("DBNAME","gpsd"); /* TRACKER */ define("POLL_TIME",60); // SET POLL TIME 20,30,60,300,600 default:60 secs define("SPEED_CONV",1.852); //From NM to Km define("DFLT_MSG","tracker"); /* SOCKET */ define("SOCK_RCV_TIMEOUT",120); // Socket receive timeout in seconds /*********************************************************************** * END SETTINGS * ********************************************************************/ // Do not edit here----------------------------------------------------- error_reporting(E_ALL); // Do not exit while waiting for connect... set_time_limit(0); // Turn implicit flush on ob_implicit_flush(); $dblink = dbConnect(); $address = IP_ADDR; $port = TCP_PORT; $allowedIMEI = getAllowedImei($dblink); $sendPollTime = false; // Create the socket and bind it to the host and port, with infinite loop. if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) < 0) { writeLog("socket_create() failed: ".socket_strerror($sock),true); } if (($ret = socket_bind($sock, $address, $port)) < 0) { writeLog("socket_bind() failed: ".socket_strerror($ret),true); } if (($ret = socket_listen($sock, 5)) < 0) { writeLog("socket_listen() failed: ".socket_strerror($ret),true); } do { if (!mysql_ping ($dblink)) { //check if mysql connection is active mysql_close($dblink); $dblink = dbConnect(); //if not, connect! } try { if (FALSE === ($msgsock = socket_accept($sock))) { throw new Exception("socket_accept() failed: " . socket_strerror(socket_last_error($msgsock))); } socket_set_option($msgsock, SOL_SOCKET, SO_RCVTIMEO, array('sec' => SOCK_RCV_TIMEOUT,'usec' => 0)); writeLog("CONNECT"); if (FALSE === ($buf = socket_read($msgsock, 2048))) { throw new Exception("socket_read() failed: " . socket_strerror(socket_last_error($msgsock))); } writeLog("RECEIVED: ".$buf); $actualIMEI = ""; $outData = array(); $buf = trim($buf); // clean up input string $dirtyMode = (substr($buf, 0, 2) == "##") ? false : true; $actualIMEI = (!$dirtyMode) ? substr($buf, 8, 15) : substr($buf, 5, 15); // returns IMEI if (!in_array($actualIMEI, $allowedIMEI)){ throw new Exception("Received: $actualIMEI from $buf, IMEI not allowed"); } if (!$dirtyMode){ $output = "LOAD". "\n"; writeLog("SEND: LOAD"); // Send intructions socket_write($msgsock, $output, strlen($output)); if (FALSE === ($buf = socket_read($msgsock, 2048))) { throw new Exception("socket_read() failed: " . socket_strerror(socket_last_error($msgsock))); } $buf = trim($buf); writeLog("RECEIVED: ".$buf); if (empty($buf)){ throw new Exception("Received: nothing, disconnect"); } if (($sendPollTime === false)) { $output = "ON". "\n"; writeLog("SEND: ON"); socket_write($msgsock, $output, strlen($output)); $output = "**,imei:".$actualIMEI."," . pollTimeString(POLL_TIME)."\n"; socket_write($msgsock, $output, strlen($output)); writeLog("SEND: ".$output); if (FALSE === ($buf = socket_read($msgsock, 2048))) { $sendPollTime = false; throw new Exception("socket_read() failed: " . socket_strerror(socket_last_error($msgsock))); } $buf = empty($buf) ? "NO DATA" : trim($buf); $sendPollTime = true; } } $outData = explode ( "," , $buf ); writeLog("DATA: ".$buf); if(count($outData)>=5){ $outDecodedData = decodeData($outData); if ($outDecodedData['DATA_FL'] == "F" || $outDecodedData['MSG'] !== DFLT_MSG){ if (OPENGTS) { $res = updatePosOpenGTS($outDecodedData); } else{ $res = updatePos($outDecodedData); } } } } catch (Exception $e) { writeLog(" ".$e->getMessage(),true); } socket_close($msgsock); writeLog("SOCKET CLOSE"); } while (true); socket_close($sock); dbClose($dblink); // ... continua ?> |
Come si può notare il socket $sock
è in costante ascolto, e quando arriva una richiesta di connessione viene creato il socket $msgsock
che si prende il carico dell’intera comunicazione. Se il tracker sta trasmettendo in “single point”, i primi due caratteri sono: ##
, in questo caso viene inviato il comando di polling forzando l’apparecchio a trasmettere in “multi point” con l’intervallo desiderato.
Vediamo adesso la funzione che si occupa della decodifica del messaggio che contiene i dati veri e propri. Questo è formato da 12 campi separati da virgola. Nei commenti iniziali è riportata la loro composizione, il formato e i possibili valori.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | function decodeData($arr){ /* ********************************************************************* * 0 = imei:000000000000000 [imei] * 1 = tracker [Msg: help me / low battery / stockade / * dt /move / speed / tracker] * 2 = 0809231929 [acquisition time: YYMMDDhhmm +8GMT cn] * 3 = 13554900601 [adminphone?] * 4 = F [Data: F - full / L - low] * 5 = 112909.397 [Time (HHMMSS.SSS)] * 6 = A [A = available?] * 7 = 2234.4669 [Latitude (DDMM.MMMM)] * 8 = N [Lat direction: N / S] * 9 = 11354.3287 [Longitude (DDDMM.MMMM)] * 10 = E [Lon direction: E / O] * 11 = 0.11 [speed Mph] ***********************************************************************/ $out = array(); $out['IMEI'] = substr($arr[0], 5, 15); $out['MSG'] = trim($arr[1]); $out['ACQUISITION_TIME'] = substr($arr[2], 0, 2)."-". substr($arr[2], 2, 2)."-".substr($arr[2], 4, 2). " ".substr($arr[2],6,2).":".substr($arr[2],8,2); $out['ADMINPHONE'] = trim($arr[3]); $out['DATA_FL'] = trim($arr[4]); if ($out['DATA_FL'] === "F"){ $out['TIME'] = substr($arr[5], 0, 2).":" . substr($arr[5], 2, 2).":" . sprintf("%2d",round(floatval(substr($arr[5], 4, 6)))); $out['AVAILABLE'] = $arr[6]==="A" ? 1 : 0; $out['LAT'] = floatval(substr($arr[7], 0, 2)) + floatval(substr($arr[7], 2, 7)) / 60; $out['LAT'] = $arr[8]==="N" ? $out['LAT'] : -$out['LAT']; $out['LON'] = floatval(substr($arr[9], 0, 3)) + floatval(substr($arr[9], 3, 7)) / 60; $out['LON'] = $arr[10]==="E" ? $out['LON'] : -$out['LON']; $out['SPEED'] = floatval($arr[11]) * SPEED_CONV; } else { $out['TIME'] = "00:00:00"; $out['AVAILABLE'] = 0; $out['LAT'] = (float) 0; $out['LON'] = (float) 0; $out['SPEED'] = (float) 0; } return $out; } |
Rimane un dubbio sul campo 3, che quasi sicuramente dovrebbe essere il numero di telefono abilitato alla comunicazione SMS con il tracker e sull’unità di misura utilizzata per la velocità. I cinesi della Cobanch dicono che è Km, ma sperimentalmente è facile smentirli. Sempre sperimentalmente, ho potuto verificare con accettabile precisione che si tratta di NM (miglia nautico internazionale)
Nella terza parte di questo articolo vedremo come avviare lo script su un server Linux facendolo funzionare in background come “demone”.