#include #include #include #include #include #include #include "ShFreqImport.h" #include "Sonde.h" static int ppos; static int quotes; static char id[20]; static int idpos; static float lat, lon, freq; static char type[20]; static uint8_t inuse[1+99/8]; // MAXSONDE is 99 static char keyword[40]; static int keywordpos; static char value[40]; static int valuepos; static int importState; static float homelat, homelon; // Map SondeHub type string to Stype. -1 for not supported types. int ShFreqImport::stringToStype(const char *type) { if(type[2]=='4') return STYPE_RS41; if(type[2]=='9') return STYPE_RS92; if(type[1]=='1') return STYPE_M10; if(type[1]=='2') return STYPE_M20; if(type[0]=='D') return STYPE_DFM; if(type[2]=='3') return STYPE_MP3H; // TODO: check if '3' is correct return -1; // iMet is not supported } // in Display.cpp extern float calcLatLonDist(float lat1, float lon1, float lat2, float lon2); void ShFreqImport::setLabel(int idx, char *id, float lat, float lon) { snprintf(sonde.sondeList[idx].launchsite, 18, "@%s/%d", id, (int)(calcLatLonDist(homelat, homelon, lat, lon)/1000)); sonde.sondeList[idx].launchsite[17] = 0; } void ShFreqImport::usekeyvalue() { if(strcmp(keyword,"lat")==0) lat = atof(value); if(strcmp(keyword,"lon")==0) lon = atof(value); if(strcmp(keyword,"frequency")==0) freq = atof(value); if(strcmp(keyword,"type")==0) strcpy(type, value); } /* populate qrg.txt with frequency of near sonde */ void ShFreqImport::populate(char *id, float lat, float lon, float freq, const char *type) { //printf(" ID %s: %.5f, %.5f f=%.3f, type=%s \n", id, lat, lon, freq, type); // Skip if freq already exists int stype = stringToStype(type); if(stype<0) return; // unsupported type // check if frequency exists already // don't do anything if its a static entry // update label if its a dynamic SH entry int i; for(i=0; i= sonde.config.maxsonde) { Serial.println("populate: out of free slots"); return; } // no more free slots sonde.sondeList[ppos].active = 1; sonde.sondeList[ppos].freq = freq; sonde.sondeList[ppos].type = (SondeType)stype; setLabel(ppos, id, lat, lon); inuse[ppos/8] |= (1<<(ppos&7)); ppos++; } // clears all remaining automatically filled slots (no longer in SH data) void ShFreqImport::cleanup() { //Serial.println("Cleanup called ********"); for(int i=0; i>(i&7))&1) == 0) && *sonde.sondeList[i].launchsite=='@' ) { // Don't remove the currently active entry if(i==sonde.currentSonde) continue; Serial.printf("removing #%d\n", i); sonde.sondeList[i].launchsite[0] = 0; sonde.sondeList[i].active = 0; sonde.sondeList[i].freq = 400; } } } #define BUFLEN 128 #define VALLEN 20 int ShFreqImport::handleChar(char c) { Serial.print(c); switch(importState) { case START: // wait for initial '{' if(c=='{') { Serial.println("{ found"); lat = NAN; lon = NAN; freq = NAN; *type = 0; importState++; } break; case BEFOREID: // what for first '"' in { "A1234567" : { ... } }; or detect end if(c=='"') { idpos = 0; importState++; } if(c=='}') { importState = ENDREACHED; cleanup(); return 1; } break; case COPYID: // copy ID "A1234567" until second '"' is earched if(c=='"') { id[idpos] = 0; importState++; } else id[idpos++] = c; break; case AFTERID: // wait for '{' in '"A1234567": { ...' if(c=='{') importState++; break; case BEFOREKEY: if(c=='"') { keywordpos = 0; importState++; } break; case COPYKEY: if(c=='"') { importState++; keyword[keywordpos] = 0; /* printf("Key: >%s<\n", keyword);*/ } else keyword[keywordpos++] = c; break; case AFTERKEY: if(c==':') { valuepos = 0; quotes = 0; if(strcmp(keyword,"lat")==0 || strcmp(keyword, "lon")==0 || strcmp(keyword, "frequency")==0 ) importState = BEFORENUMVAL; else { if (strcmp(keyword, "type")==0) importState = BEFORESTRINGVAL; else importState = SKIPVAL; } } break; case BEFORENUMVAL: if( (c>='0'&&c<='9') || c=='-') { value[0] = c; valuepos=1; importState++; } break; case COPYNUMVAL: if( !(c>='0'&&c<='9') && c!='-' && c!='.' ) { value[valuepos]=0; importState=SKIPVAL; usekeyvalue(); if(c!=',' && c!='}') break; } else { value[valuepos++] = c; break; } // intenionall fall-through case SKIPVAL: // This is rather fragile, we *should* handle more escaping and so on but do not do so so far, only simple quotes if(c=='"') quotes = !quotes; if(quotes) break; if(c==',') importState = BEFOREKEY; if(c=='}') { // we have an ID and all key/value pairs, check if its good.... if( !isnan(lat) && !isnan(lon) && !isnan(freq) && type[0] ) { printf("SondeHub import: populate %s %f %f %f %s\n", id, lat, lon, freq, type); populate(id, lat, lon, freq, type); } else { printf("Skipping incomplete %s\n", id); } importState = ENDORNEXT; } break; case BEFORESTRINGVAL: if(c=='"') importState++; break; case COPYSTRINGVAL: if(c=='"') { importState=SKIPVAL; value[valuepos]=0; usekeyvalue(); } else value[valuepos++] = c; break; case ENDORNEXT: // next we have to see either a final "}', or a comma before the next id if(c==',') importState = BEFOREID; else if (c=='}') { importState = ENDREACHED; cleanup(); return 1; } break; case ENDREACHED: Serial.println("REPLY: END REACHED"); return 1; } return 0; } // lat lon in deg, dist in km, time in minutes int ShFreqImport::shImportSendRequest(WiFiClient *client, float lat, float lon, int dist, int time) { if(!client->connected()) { if(!client->connect(sonde.config.sondehub.host, 80)) { Serial.println("Connection FAILED"); return 1; } } Serial.println("Sending SondeHub import request"); char req[300]; snprintf(req, 200, "GET /sondes?lat=%f&lon=%f&distance=%d&last=%d HTTP/1.1\r\n" "Host: %s\r\n" "Accept: application/json\r\n" "Cache-Control: no-cache\r\n\r\n", lat, lon, dist*1000, time*60, sonde.config.sondehub.host); client->print(req); Serial.print(req); importState = START; homelat = lat; homelon = lon; memset(inuse, 0, sizeof(inuse)); ppos = 0; return 0; } // return 0 if more data should be read (later), 1 if finished (close connection...) int ShFreqImport::shImportHandleReply(WiFiClient *client) { if(!client->connected()) return 1; while(client->available()) { int res = handleChar(client->read()); if(res) return res; } return 0; }