David Smart / NWSWeather

NWSWeather.cpp

Committer:
WiredHome
Date:
2014-04-20
Revision:
4:94dfdc405640
Parent:
2:eae60b64066e
Child:
6:cf96f2787a4e

File content as of revision 4:94dfdc405640:

// 
// This file contains the interface for gathering forecast from NWS.
//
// attention: This program is copyright (c) 2014 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"

//#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


static NWSWeather::XMLItem_T WeatherData[] = {
    {"observation_time_rfc822", NWSWeather::isString},
    {"suggested_pickup", NWSWeather::isString},
    {"suggested_pickup_period", NWSWeather::isInt},
    {"location", NWSWeather::isString},
    {"weather", NWSWeather::isString},
    {"temp_f",  NWSWeather::isFloat},
    {"temp_c",  NWSWeather::isFloat},
    {"relative_humidity", NWSWeather::isInt},
    {"wind_degrees", NWSWeather::isInt},
    {"wind_mph", NWSWeather::isFloat},
    {"pressure_mb", NWSWeather::isFloat},
    {"pressure_in", NWSWeather::isFloat},
    {"dewpoint_f", NWSWeather::isFloat},
    {"dewpoint_c", NWSWeather::isFloat},
    {"windchill_f", NWSWeather::isFloat},
    {"windchill_c", NWSWeather::isFloat},
    {"visibility_mi", NWSWeather::isFloat},
    {"icon_url_base", NWSWeather::isString},
    {"icon_url_name", NWSWeather::isString},
    {"latitude", NWSWeather::isFloat},
    {"longitude", NWSWeather::isFloat},
};

#define MAXPARAMLEN 23

#define WeatherItemCount (sizeof(WeatherData)/sizeof(WeatherData[0]))

NWSWeather::NWSWeather(const char * baseURL)
{
    setAlternateURL(baseURL);
}

NWSWeather::~NWSWeather()
{
    ClearWeatherRecords();
}

NWSWeather::NWSReturnCode_T NWSWeather::setAlternateURL(const char * baseURL)
{
    if (m_baseurl)
        free(m_baseurl);
    m_baseurl = (char *)malloc(strlen(baseURL)+1);
    if (m_baseurl)
        strcpy(m_baseurl, baseURL);
    else {
        return nomemory;
    }
    ClearWeatherRecords();
    return noerror;
}

uint16_t NWSWeather::count(void)
{
    return WeatherItemCount;
}

NWSWeather::NWSReturnCode_T NWSWeather::get(const char * site, int responseSize)
{
    NWSReturnCode_T retCode = nomemory;
    char *url = NULL;
    char *message = (char *)malloc(responseSize);

    INFO("get(%s)", site);
    if (!message)
        ERR("no memory");
    while (message) {
        url = (char *)malloc(strlen(m_baseurl) + strlen(site) + 5);
        if (url) {
            strcpy(url, m_baseurl);
            strcat(url, site);
            strcat(url, ".xml");
            INFO("  url: %s", url);
            http.setMaxRedirections(3);
            int ret = http.get(url, message, responseSize);
            if (!ret) {
                INFO("ret is %d", ret);
                if (http.getHTTPResponseCode() >= 300 && http.getHTTPResponseCode() < 400) {
                    retCode = noerror;         // redirection that was not satisfied.
                    break;
                } else {
                    ParseWeatherXML(message);
                    retCode = noerror;
                    break;
                }
            } else {
                WARN("get returned %d, no response?", ret);
                retCode = noresponse;
                break;
            }
        } else {
            ERR("no memory");
            retCode = nomemory;
            break;
        }
    } // while(...) but configured with break for only 1 pass.
    INFO("  ret is %d", retCode);
    if (url)
        free(url);
    if (message)
        free(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].name) == 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].name;
        *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)
{
    for (int i=0; i < WeatherItemCount; i++) {
        if (strcmp(name, WeatherData[i].name) == 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) {
                    free(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) {
        switch(WeatherData[i].typeis) {
            case isFloat:
                printf("%23s = %f\r\n", WeatherData[i].name, WeatherData[i].value.fValue);
                break;
            case isInt:
                printf("%23s = %d\r\n", WeatherData[i].name, WeatherData[i].value.iValue);
                break;
            case isString:
                printf("%23s = %s\r\n", WeatherData[i].name, WeatherData[i].value.sValue);
                break;
            default:
                break;
        }
    }
}

void NWSWeather::PrintAllWeatherRecords(void)
{
    for (int i=0; i<WeatherItemCount; i++) {
        PrintWeatherRecord(i);
    }
}

NWSWeather::NWSReturnCode_T NWSWeather::ParseWeatherRecord(char * p)
{
    // <tag_name>value</tag_name>
    //  p1       p2   p3
    char *p1, *p2, *p3;
    int i;
    int count = WeatherItemCount;

    p1 = strchr(p, '<');
    if (p1++) {
        p2 = strchr(p1+1, '>');
        if (p2) {
            p3 = strchr(p2+1, '<');
            if (p3) {
                *p2++ = '\0';
                *p3 = '\0';
                for (i=0; i<count; i++) {
                    if (strcmp(p1, WeatherData[i].name) == 0) {
                        switch(WeatherData[i].typeis) {
                            case isFloat:
                                WeatherData[i].value.fValue = atof(p2);
                                WeatherData[i].updated = true;
                                break;
                            case isInt:
                                WeatherData[i].value.iValue = atoi(p2);
                                WeatherData[i].updated = true;
                                break;
                            case isString:
                                if (WeatherData[i].value.sValue)
                                    free(WeatherData[i].value.sValue);
                                WeatherData[i].value.sValue = (char *)malloc(strlen(p2)+1);
                                if (WeatherData[i].value.sValue) {
                                    strcpy(WeatherData[i].value.sValue, p2);
                                    WeatherData[i].updated = true;
                                }
                                break;
                            default:
                                return badparameter;
                                //break;
                        }
                        return noerror;
                    }
                }
            }
        }
    }
    return noparamfound;
}

void NWSWeather::ParseWeatherXML(char * message)
{
    char * p = message;

    ClearWeatherRecords();
    while (*p) {
        char * n = strchr(p, '\n');
        if (*n) {
            *n = '\0';
            ParseWeatherRecord(p);
            p = n + 1;
        } else {
            ParseWeatherRecord(p);
            break;
        }
    }
}