|  | 
| 5 | 5 |  */ | 
| 6 | 6 | 
 | 
| 7 | 7 | #include "ts_client.h" | 
|  | 8 | +#include "ts_serial.h" | 
| 8 | 9 | 
 | 
| 9 | 10 | #include <stdio.h> | 
| 10 | 11 | #include <stdlib.h> | 
| 11 | 12 | 
 | 
| 12 | 13 | #include "esp_http_server.h" | 
| 13 | 14 | #include "esp_err.h" | 
| 14 | 15 | #include "esp_log.h" | 
|  | 16 | +#include "cJSON.h" | 
| 15 | 17 | 
 | 
| 16 | 18 | static const char *TAG = "ts_client"; | 
|  | 19 | +static TSDevice *devices[10]; | 
| 17 | 20 | 
 | 
| 18 |  | -char *ts_resp_data(char *resp) | 
|  | 21 | + | 
|  | 22 | +void ts_scan_devices() | 
| 19 | 23 | { | 
| 20 |  | -    if (resp[0] == ':') { | 
| 21 |  | -        char *pos = strstr(resp, ". "); | 
| 22 |  | -        if (pos != NULL) { | 
| 23 |  | -            return pos + 2; | 
|  | 24 | +    //scan serial connection | 
|  | 25 | +    devices[0] = (TSDevice *) heap_caps_malloc(sizeof(TSDevice), MALLOC_CAP_8BIT); | 
|  | 26 | +    if (ts_serial_scan_device_info(devices[0]) != 0) { | 
|  | 27 | +        free(devices[0]); | 
|  | 28 | +        devices[0] = NULL; | 
|  | 29 | +    }; | 
|  | 30 | + | 
|  | 31 | +    //TODO scan CAN connection | 
|  | 32 | +} | 
|  | 33 | + | 
|  | 34 | +char *ts_get_device_list() | 
|  | 35 | +{ | 
|  | 36 | +    cJSON *obj = cJSON_CreateObject(); | 
|  | 37 | +    int i = 0; | 
|  | 38 | +    while (devices[i] != NULL) { | 
|  | 39 | +        cJSON *id = cJSON_CreateString(devices[i]->ts_device_id); | 
|  | 40 | +        cJSON_AddItemToObject(obj, devices[i]->ts_name, id); | 
|  | 41 | +        i++; | 
|  | 42 | +    } | 
|  | 43 | +    char *names_string = NULL; | 
|  | 44 | +    if (i > 0) { | 
|  | 45 | +        names_string = cJSON_Print(obj); | 
|  | 46 | +        cJSON_Delete(obj); | 
|  | 47 | +    } else { | 
|  | 48 | +        // hackky solution so that free() can always be called on names_string | 
|  | 49 | +        const char msg[] = "No devices Connected"; | 
|  | 50 | +        names_string = (char *) malloc(sizeof(msg)+1); | 
|  | 51 | +        strncpy(names_string, msg, sizeof(msg)+1); | 
|  | 52 | +    } | 
|  | 53 | +    return names_string; | 
|  | 54 | +} | 
|  | 55 | + | 
|  | 56 | +TSDevice *ts_get_device(char *device_id) | 
|  | 57 | +{ | 
|  | 58 | +    int i = 0; | 
|  | 59 | +    while (devices[i] != NULL) { | 
|  | 60 | +        if (strcmp(devices[i]->ts_device_id, device_id) == 0) { | 
|  | 61 | +            return devices[i]; | 
| 24 | 62 |         } | 
|  | 63 | +        i++; | 
| 25 | 64 |     } | 
| 26 | 65 |     return NULL; | 
| 27 | 66 | } | 
| 28 | 67 | 
 | 
| 29 |  | -int ts_resp_status(char *resp) | 
|  | 68 | +// wrapper for strlen() to check for NULL | 
|  | 69 | +static int strlen_null(char *r) | 
| 30 | 70 | { | 
| 31 |  | -    unsigned int status_code = 0; | 
| 32 |  | -    int ret = sscanf(resp, ":%X ", &status_code); | 
| 33 |  | -    if (ret > 0) { | 
| 34 |  | -        return status_code; | 
|  | 71 | +    if (r != NULL) { | 
|  | 72 | +        return(strlen(r)); | 
|  | 73 | +    } else { | 
|  | 74 | +        return 0; | 
| 35 | 75 |     } | 
| 36 |  | -    else { | 
| 37 |  | -        return -1; | 
|  | 76 | +} | 
|  | 77 | + | 
|  | 78 | +char *exec_or_create(char *node) | 
|  | 79 | +{ | 
|  | 80 | +    if (strstr(node, "auth") != NULL || strstr(node, "exec") != NULL) { | 
|  | 81 | +        return "!"; | 
|  | 82 | +    } else { | 
|  | 83 | +        return "+"; | 
| 38 | 84 |     } | 
| 39 | 85 | } | 
| 40 | 86 | 
 | 
| 41 |  | -int ts_req_hdr_from_http(char *buf, size_t buf_size, int method, const char *uri) | 
|  | 87 | +void ts_parse_uri(const char *uri, TSUriElems *params) | 
| 42 | 88 | { | 
| 43 |  | -    ESP_LOGI(TAG, "URI: %s, method: %d", uri, method); | 
|  | 89 | +    if (uri == NULL || uri[0] == '\0') { | 
|  | 90 | +        ESP_LOGE(TAG, "Got invalid uri"); | 
|  | 91 | +        return; | 
|  | 92 | +    } | 
|  | 93 | +    params->ts_list_subnodes = uri[strlen(uri)] == '/' ? 0 : 1; | 
| 44 | 94 | 
 | 
| 45 |  | -    switch (method) { | 
| 46 |  | -        case HTTP_GET: | 
| 47 |  | -            buf[0] = '?'; | 
|  | 95 | +    // copy uri so we can safely modify | 
|  | 96 | +    char *temp_uri = (char *) heap_caps_malloc((strlen(uri)+1), MALLOC_CAP_8BIT); | 
|  | 97 | +    if (temp_uri == NULL) { | 
|  | 98 | +        ESP_LOGE(TAG, "Unable to allocate memory for temp_uri"); | 
|  | 99 | +        return; | 
|  | 100 | +    } | 
|  | 101 | +    strcpy(temp_uri, uri); | 
|  | 102 | +    // extract device ID | 
|  | 103 | +    int i = 0; | 
|  | 104 | +    while (temp_uri[i] != '\0' && temp_uri[i] != '/') { | 
|  | 105 | +        i++; | 
|  | 106 | +    } | 
|  | 107 | +    // replace '/' so we have two null-terminated strings | 
|  | 108 | +    temp_uri[i] = '\0'; | 
|  | 109 | +    params->ts_device_id = temp_uri; | 
|  | 110 | +    // this points either to '\0' aka NULL or the rest of the string | 
|  | 111 | +    params->ts_target_node = temp_uri + i + 1; | 
|  | 112 | +    ESP_LOGD(TAG, "Device_id: %s", params->ts_device_id); | 
|  | 113 | +    ESP_LOGD(TAG, "Target Node: %s", params->ts_target_node); | 
|  | 114 | +    ESP_LOGD(TAG, "List the sub nodes: %s", params->ts_list_subnodes == 1 ? "yes" : "no"); | 
|  | 115 | +} | 
|  | 116 | + | 
|  | 117 | +char *ts_build_query(uint8_t ts_method, TSUriElems *params) | 
|  | 118 | +{ | 
|  | 119 | +    // calculate size to allocate buffer | 
|  | 120 | +    int nbytes = 3;  // method + termination + zero termination | 
|  | 121 | +    if (*(params->ts_target_node) == '\0' && params->ts_list_subnodes == 1) { | 
|  | 122 | +        nbytes++;    // '/' at the end of query | 
|  | 123 | +    } | 
|  | 124 | +    nbytes += strlen_null(params->ts_target_node); | 
|  | 125 | +    if (params->ts_payload != NULL) { | 
|  | 126 | +        // additional whitespace between uri and array/json | 
|  | 127 | +        nbytes += strlen_null(params->ts_payload) +1; | 
|  | 128 | +    } | 
|  | 129 | +    char *ts_query = (char *) heap_caps_malloc(nbytes, MALLOC_CAP_8BIT); | 
|  | 130 | + | 
|  | 131 | +    if (ts_query == NULL) { | 
|  | 132 | +        ESP_LOGE(TAG, "Unable to allocate memory for ts_query"); | 
|  | 133 | +        return NULL; | 
|  | 134 | +    } | 
|  | 135 | +    int pos = 0; | 
|  | 136 | +    switch (ts_method){ | 
|  | 137 | +        case TS_GET: | 
|  | 138 | +            ts_query[pos] = '?'; | 
| 48 | 139 |             break; | 
| 49 |  | -        case HTTP_POST: | 
| 50 |  | -            buf[0] = '!'; | 
|  | 140 | +        case TS_POST: | 
|  | 141 | +            ts_query[pos] = *(exec_or_create(params->ts_target_node)); | 
| 51 | 142 |             break; | 
| 52 |  | -        case HTTP_PATCH: | 
| 53 |  | -            buf[0] = '='; | 
|  | 143 | +        case TS_PATCH: | 
|  | 144 | +            ts_query[pos] = '='; | 
|  | 145 | +            break; | 
|  | 146 | +        case TS_DELETE: | 
|  | 147 | +            ts_query[pos] = '-'; | 
| 54 | 148 |             break; | 
| 55 | 149 |         default: | 
| 56 |  | -            buf[0] = '\0';   // empty string | 
|  | 150 | +            ts_query[pos] = '\0'; | 
|  | 151 | +            return ts_query;    // nothing else to do here | 
|  | 152 | +    } | 
|  | 153 | +    pos++; | 
|  | 154 | +    // corner case for getting device categories | 
|  | 155 | +    if (*(params->ts_target_node) == '\0' && params->ts_list_subnodes == 1) { | 
|  | 156 | +        ts_query[pos] = '/'; | 
|  | 157 | +        pos++; | 
|  | 158 | +    } else { | 
|  | 159 | +        strncpy(ts_query + pos, params->ts_target_node, strlen_null(params->ts_target_node)); | 
|  | 160 | +        pos += strlen_null(params->ts_target_node); | 
|  | 161 | +    } | 
|  | 162 | +    if (params->ts_payload != NULL) { | 
|  | 163 | +        ts_query[pos] = ' '; | 
|  | 164 | +        pos++; | 
|  | 165 | +        strncpy(ts_query + pos, params->ts_payload, strlen_null(params->ts_payload)); | 
|  | 166 | +        pos += strlen_null(params->ts_payload); | 
| 57 | 167 |     } | 
|  | 168 | +    //terminate query properly | 
|  | 169 | +    ts_query[pos] = '\n'; | 
|  | 170 | +    pos++; | 
|  | 171 | +    ts_query[pos] = '\0'; | 
|  | 172 | +    ESP_LOGD(TAG, "Build query String: %s !", ts_query); | 
|  | 173 | +    return ts_query; | 
|  | 174 | +} | 
| 58 | 175 | 
 | 
| 59 |  | -    int pos_function = strlen("/ts/serial/"); | 
| 60 |  | -    int len_function = strlen(uri) - pos_function; | 
| 61 |  | -    if (len_function > 0) { | 
| 62 |  | -        int ret = snprintf(buf + 1, buf_size - 2, "%s", uri + pos_function); | 
| 63 |  | -        if (ret > 0) { | 
| 64 |  | -            return ret + 1; | 
| 65 |  | -        } | 
| 66 |  | -        else { | 
| 67 |  | -            buf[1] = '\0';   // terminate properly | 
|  | 176 | +TSResponse *ts_execute(const char *uri, char *content, int http_method) | 
|  | 177 | +{ | 
|  | 178 | +    uint8_t ts_method; | 
|  | 179 | +    switch (http_method) { | 
|  | 180 | +    case HTTP_DELETE: | 
|  | 181 | +        ts_method = TS_DELETE; | 
|  | 182 | +        break; | 
|  | 183 | +    case HTTP_GET: | 
|  | 184 | +        ts_method = TS_GET; | 
|  | 185 | +        break; | 
|  | 186 | +    case HTTP_POST: | 
|  | 187 | +        ts_method = TS_POST; | 
|  | 188 | +        break; | 
|  | 189 | +    case HTTP_PATCH: | 
|  | 190 | +        ts_method = TS_PATCH; | 
|  | 191 | +        break; | 
|  | 192 | +    default: | 
|  | 193 | +        ts_method = TS_GET; | 
|  | 194 | +        break; | 
|  | 195 | +    } | 
|  | 196 | +    TSUriElems params; | 
|  | 197 | +    params.ts_payload = content; | 
|  | 198 | +    ts_parse_uri(uri, ¶ms); | 
|  | 199 | +    TSDevice *device = ts_get_device(params.ts_device_id); | 
|  | 200 | +    if (device == NULL) { | 
|  | 201 | +        ESP_LOGD(TAG, "No Device, freeing query string and device id"); | 
|  | 202 | +        heap_caps_free(params.ts_device_id); | 
|  | 203 | +        return NULL; | 
|  | 204 | +    } | 
|  | 205 | +    char *ts_query_string = ts_build_query(ts_method, ¶ms); | 
|  | 206 | + | 
|  | 207 | +    TSResponse *res = (TSResponse *) heap_caps_malloc(sizeof(TSResponse), MALLOC_CAP_8BIT); | 
|  | 208 | +    if (res == NULL) { | 
|  | 209 | +        ESP_LOGE(TAG, "Unable to allocate memory for ts response"); | 
|  | 210 | +        heap_caps_free(ts_query_string); | 
|  | 211 | +        heap_caps_free(params.ts_device_id); | 
|  | 212 | +        return NULL; | 
|  | 213 | +    } | 
|  | 214 | + | 
|  | 215 | +    res->block = device->send(ts_query_string, device->CAN_Address); | 
|  | 216 | +    if (res->block == NULL) { | 
|  | 217 | +        ESP_LOGD(TAG, "No Response, freeing query string and device id"); | 
|  | 218 | +        heap_caps_free(ts_query_string); | 
|  | 219 | +        heap_caps_free(params.ts_device_id); | 
|  | 220 | +        heap_caps_free(res); | 
|  | 221 | +        return NULL; | 
|  | 222 | +    } | 
|  | 223 | +    res->ts_status_code = ts_resp_status(res->block); | 
|  | 224 | +    res->data = ts_resp_data(res->block); | 
|  | 225 | + | 
|  | 226 | +    heap_caps_free(ts_query_string); | 
|  | 227 | +    heap_caps_free(params.ts_device_id); | 
|  | 228 | + | 
|  | 229 | +    return res; | 
|  | 230 | +} | 
|  | 231 | + | 
|  | 232 | +char *ts_resp_data(char *resp) | 
|  | 233 | +{ | 
|  | 234 | +    if (resp[0] == ':') { | 
|  | 235 | +        char *pos = strstr(resp, ". "); | 
|  | 236 | +        if (pos != NULL) { | 
|  | 237 | +            return pos + 2; | 
| 68 | 238 |         } | 
| 69 | 239 |     } | 
| 70 |  | -    return 0; | 
|  | 240 | +    return NULL; | 
|  | 241 | +} | 
|  | 242 | + | 
|  | 243 | +int ts_resp_status(char *resp) | 
|  | 244 | +{ | 
|  | 245 | +    unsigned int status_code = 0; | 
|  | 246 | +    int ret = sscanf(resp, ":%X ", &status_code); | 
|  | 247 | +    if (ret > 0) { | 
|  | 248 | +        return status_code; | 
|  | 249 | +    } | 
|  | 250 | +    else { | 
|  | 251 | +        return -1; | 
|  | 252 | +    } | 
| 71 | 253 | } | 
0 commit comments