A class to gather weather data from an NWS server.
NWSWeather.cpp
- Committer:
- WiredHome
- Date:
- 2019-02-23
- Revision:
- 20:29257dc4c7aa
- Parent:
- 19:00af774c9b72
File content as of revision 20:29257dc4c7aa:
// // This file contains the interface for gathering forecast from NWS. // // attention: This program is copyright (c) 2014 - 2019 by Smartware Computing, all // rights reserved. // // This software, and/or material is the property of Smartware Computing. All // use, disclosure, and/or reproduction not specifically authorized by Smartware // Computing is prohibited. // // This software may be freely used for non-commercial purposes, and any // derivative work shall carry the original copyright. The author of // a derivative work shall make clear that it is a derivative work. // // author David Smart, Smartware Computing // #include "NWSWeather.h" //#include "Utility.h" // private memory manager #ifndef UTILITY_H #define swMalloc malloc // use the standard #define swFree free #endif //#define DEBUG "NWS " // ... // INFO("Stuff to show %d", var); // new-line is automatically appended // #if (defined(DEBUG) && !defined(TARGET_LPC11U24)) #define INFO(x, ...) std::printf("[INF %s %3d] " x "\r\n", DEBUG, __LINE__, ##__VA_ARGS__); #define WARN(x, ...) std::printf("[WRN %s %3d] " x "\r\n", DEBUG, __LINE__, ##__VA_ARGS__); #define ERR(x, ...) std::printf("[ERR %s %3d] " x "\r\n", DEBUG, __LINE__, ##__VA_ARGS__); #else #define INFO(x, ...) #define WARN(x, ...) #define ERR(x, ...) #endif // 20151126 - Not sure when, but the NWS site stopped accepting requests unless a // User-Agent was provided. It doesn't seem to matter the contents of // the U-A string. static const char * hdrs[] = { "Connection", "keep-alive", "Accept", "text/html", "User-Agent", "SW_Client" }; #define HDR_PAIRS 3 NWSWeather::NWSWeather(XMLItem_T * pList, int count) : m_baseurl(0) { WeatherData = pList; WeatherItemCount = count; setAlternateURL(DEF_URL); } NWSWeather::~NWSWeather() { ClearWeatherRecords(); } NWSWeather::NWSReturnCode_T NWSWeather::setAlternateURL(const char * baseURL) { int n = strlen(baseURL)+1; if (m_baseurl) swFree(m_baseurl); m_baseurl = (char *)swMalloc(n); if (m_baseurl) strncpy(m_baseurl, baseURL, n); else { ERR("failed to swMalloc(%d)", n); return nomemory; } ClearWeatherRecords(); return noerror; } uint16_t NWSWeather::count(void) { return WeatherItemCount; } #if WEATHER_GOV == 1 NWSWeather::NWSReturnCode_T NWSWeather::get(const char * site, int responseSize) #elif OPEN_WEATHER == 1 NWSWeather::NWSReturnCode_T NWSWeather::get(const char * site, const char * userid, int responseSize) #endif { HTTPClient http; NWSReturnCode_T retCode = nomemory; char *url = NULL; char *message = (char *)swMalloc(responseSize); INFO("get(%s)", site); if (!message) ERR("failed to swMalloc(%d)", responseSize); while (message) { #if WEATHER_GOV == 1 //https://www.weather.gov/data/current_obs/<loc_code>.xml // ++++ int n = strlen(m_baseurl) + strlen(site) + 5; #elif OPEN_WEATHER == 1 //http://api.openweathermap.org/data/2.5/weather?mode=xml&units=imperial&id=<loc_code>&APPID=<user_id> // ++++ +++++++ int n = strlen(m_baseurl) + 4 + strlen(site) + 7 + strlen(userid) + 1; #endif url = (char *)swMalloc(n); if (url) { #if WEATHER_GOV == 1 sprintf(url, "%s%s%s.xml", m_baseurl, site); #elif OPEN_WEATHER == 1 sprintf(url, "%s&id=%s&APPID=%s", m_baseurl, site, userid); #endif INFO(" url: %s", url); http.setMaxRedirections(3); http.customHeaders(hdrs, HDR_PAIRS); int ret = http.get(url, message, responseSize); if (!ret) { INFO("ret is %d", ret); if (http.getHTTPResponseCode() >= 300 && http.getHTTPResponseCode() < 400) { INFO(" http.getHTTPResponseCode(): %d", http.getHTTPResponseCode()); retCode = noerror; // redirection that was not satisfied. break; } else { INFO("wx get %d bytes.\r\n", strlen(message)); ParseWeatherXML(message); INFO("wx parse complete.\r\n"); retCode = noerror; break; } } else { WARN("get returned %d, no response?", ret); retCode = (NWSReturnCode_T)ret; // return code from get also indicates type of error // noresponse; break; } } else { ERR("failed to swMalloc(%d)", n); retCode = nomemory; break; } } // while(...) but configured with break for only 1 pass. INFO(" ret is %d", retCode); if (url) swFree(url); if (message) swFree(message); INFO(" mem freed."); return retCode; } NWSWeather::NWSReturnCode_T NWSWeather::isUpdated(uint16_t i) { if (i < WeatherItemCount) { if (WeatherData[i].updated) return noerror; else return noupdate; } else { return badparameter; } } NWSWeather::NWSReturnCode_T NWSWeather::isUpdated(const char * name) { if (name) { for (int i=0; i < WeatherItemCount; i++) { if (strcmp(name, WeatherData[i].key) == 0) { if (WeatherData[i].updated) return noerror; else return noupdate; } } } return badparameter; } NWSWeather::NWSReturnCode_T NWSWeather::getParam(uint16_t i, char *name, Value_T *value, TypeOf_T *typeis) { if (i < WeatherItemCount) { *name = *WeatherData[i].key; *value = WeatherData[i].value; if (typeis) *typeis = WeatherData[i].typeis; return noerror; } else { return badparameter; } } NWSWeather::NWSReturnCode_T NWSWeather::getParam(const char *name, Value_T *value, TypeOf_T *typeis) { return getParam(name, NULL, value, typeis); } NWSWeather::NWSReturnCode_T NWSWeather::getParam(const char *name, const char * param, Value_T *value, TypeOf_T *typeis) { for (int i=0; i < WeatherItemCount; i++) { if (strcmp(name, WeatherData[i].key) == 0) { if (param == NULL || strcmp(param, WeatherData[i].param) == 0) { *value = WeatherData[i].value; if (typeis) *typeis = WeatherData[i].typeis; return noerror; } } } return badparameter; } void NWSWeather::ClearWeatherRecords(void) { int i; int count = WeatherItemCount; for (i=0; i<count; i++) { switch(WeatherData[i].typeis) { case isFloat: WeatherData[i].value.fValue = 0.0; WeatherData[i].updated = false; break; case isInt: WeatherData[i].value.iValue = 0; WeatherData[i].updated = false; break; case isString: if (WeatherData[i].value.sValue) { swFree(WeatherData[i].value.sValue); WeatherData[i].value.sValue = NULL; } WeatherData[i].updated = false; break; default: break; } } } void NWSWeather::PrintWeatherRecord(uint16_t i) { if (i < WeatherItemCount) { printf("%23s::%-10s = ", WeatherData[i].key, WeatherData[i].param); switch(WeatherData[i].typeis) { case isFloat: printf("%f", WeatherData[i].value.fValue); break; case isInt: printf("%d", WeatherData[i].value.iValue); break; case isString: printf("%s", WeatherData[i].value.sValue); break; default: break; } printf("\r\n"); } } void NWSWeather::PrintAllWeatherRecords(void) { for (int i=0; i<WeatherItemCount; i++) { PrintWeatherRecord(i); } } NWSWeather::NWSReturnCode_T NWSWeather::ExtractParam(int i, char * pValue, char * pEnd) { int n; INFO("ExtractParam(%d, ...)", i); switch(WeatherData[i].typeis) { case isFloat: WeatherData[i].value.fValue = (float)atof(pValue); WeatherData[i].updated = true; INFO("%f", WeatherData[i].value.fValue); break; case isInt: WeatherData[i].value.iValue = atoi(pValue); WeatherData[i].updated = true; INFO("%d", WeatherData[i].value.iValue); break; case isString: if (WeatherData[i].value.sValue) swFree(WeatherData[i].value.sValue); n = pEnd - pValue; WeatherData[i].value.sValue = (char *)swMalloc(n+1); if (WeatherData[i].value.sValue) { strncpy(WeatherData[i].value.sValue, pValue, n); WeatherData[i].value.sValue[n] = '\0'; WeatherData[i].updated = true; INFO(" info '%s'", WeatherData[i].value.sValue); } else { ERR("failed to swMalloc(%d)", n); return nomemory; } break; default: ERR("unknown type"); return badparameter; //break; } INFO("..."); return noerror; } NWSWeather::NWSReturnCode_T NWSWeather::ParseWeatherRecord(char * p) { #if 1 // Pattern Matching <thekey dontcare="something" value="thevalue"/> // | | |------| | | // | | | | +pEnd // | +pKeyE +pVal +pValE // +pKey // // <thekey>theValue</thekey> // | || | | // | || | +pEnd // | |+pVal +pValE // | +pKeyE // +pKey // char *pKey, *pEnd; char *pVal = NULL; char *pValE = NULL; p = strchr(p, '<'); while (p) { //INFO("ParseWeatherRecord \n%s\n", p); p++; // Advance past the '<' for (int i=0; i<WeatherItemCount; i++) { //INFO("\n check %s::%s", WeatherData[i].key, WeatherData[i].param); pKey = strstr(p, WeatherData[i].key); if (pKey && pKey == p && *(pKey-1) == '<') { //INFO(" key %s", pKey); if (WeatherData[i].param && WeatherData[i].param[0]) { // <thekey dontcare="something" value="thevalue"/> pEnd = strchr(pKey, '/'); if (pEnd) { pVal = strstr(pKey, WeatherData[i].param); if (pVal && pVal < pEnd) { pVal = strchr(pVal, '"'); if (pVal) { pVal++; //INFO(" val %s", pVal); pValE = strchr(pVal, '"'); ExtractParam(i, pVal, pValE); INFO("Key %s::%s", WeatherData[i].key, WeatherData[i].param); } } } } else { // <thekey>theValue</key> pVal = strchr(pKey, '>'); if (pVal) { pVal++; pValE = strchr(pVal, '<'); if (pValE && *(pValE+1) == '/') { ExtractParam(i, pVal, pValE); INFO("Key %s", WeatherData[i].key); } } } } } p = strchr(p, '<'); } #else // <tag_name>value</tag_name> // pKey pValue char *pKey, *pValue, *pX; int i; int n; int count = WeatherItemCount; bool parseIt = false; INFO("ParseWeatherRecord(%s)", p); #if WEATHER_GOV == 1 // Pattern Matching <key>value</key> // | +pValue // +pKey pKey = strchr(p, '<'); if (pKey++) { pValue = strchr(pKey+1, '>'); if (pValue) { p2 = strchr(pValue+1, '<'); if (p2) { *pValue++ = '\0'; *p2 = '\0'; parseIt = true; } } } #elif OPEN_WEATHER == 1 pKey = strchr(p, '<'); // Pattern Matching <key dontcare="something" value="thevalue" dontcare="something"/> // | +pValue // +pKey if (pKey++) { pValue = strchr(pKey+1, ' '); if (pValue) { *pValue++ = '\0'; pValue = strstr(pValue, "value=\""); if (pValue) { pValue += 7; pX = strchr(pValue, '"'); if (pX) { *pX = '\0'; parseIt = true; } } } } #endif if (parseIt) { for (i=0; i<count; i++) { if (strcmp(pKey, WeatherData[i].key) == 0) { switch(WeatherData[i].typeis) { case isFloat: WeatherData[i].value.fValue = atof(pValue); WeatherData[i].updated = true; break; case isInt: WeatherData[i].value.iValue = atoi(pValue); WeatherData[i].updated = true; break; case isString: if (WeatherData[i].value.sValue) swFree(WeatherData[i].value.sValue); n = strlen(pValue)+1; INFO(" pre- swMalloc(%d)", n); WeatherData[i].value.sValue = (char *)swMalloc(n); INFO(" post-swMalloc"); if (WeatherData[i].value.sValue) { strcpy(WeatherData[i].value.sValue, pValue); WeatherData[i].updated = true; } else { ERR("failed to swMalloc(%d)", n); break; } break; default: ERR("unknown type"); return badparameter; //break; } return noerror; } } } #endif return noparamfound; } void NWSWeather::ParseWeatherXML(char * message) { char * p = strchr(message, '<'); ClearWeatherRecords(); ParseWeatherRecord(p); }