#include "display.h" #include "utility.h" #include "params.h" #include "sock.h" #include "device.h" #include #define MIN_REFRESH 5 #define MAX_REFRESH 30 /** * Add a drop down selection box in a two-column table row * * \param buffer where the output is appended * \param title text to go in column one * \param name name of the selection object * * The browser will return name=value for the selection, using the name from * the selection box and the value from the option selected. */ static void add_select_begin(BUFFER* buffer, const char* title, const char* name) { char* bp = &buffer->body[buffer->length]; snprintf(bp, sizeof(buffer->body) - buffer->length, "%s\r\n" "\r\n"); buffer->length += strlen(bp); } /** * Add arbitrary text to a buffer and adjust the length * * \param buffer where the text is appended */ static void add_text(BUFFER* buffer, const char* text) { char* bp = &buffer->body[buffer->length]; snprintf(bp, sizeof(buffer->body) - buffer->length, "%s", text); buffer->length += strlen(bp); } /** * Add a two-column row for an integer * * \param buffer where the text is appended * \param title is displayed in column one * \param name is returned by the browser * \param value is displayed in column two and returned by the browser * * The title is displayed in column one and the integer in column two. The * name=value is returned by the browser. */ static void add_int(BUFFER* buffer, const char* title, const char* name, int value) { char* bp = &buffer->body[buffer->length]; snprintf(bp, sizeof(buffer->body) - buffer->length, "%s" "" "\r\n", title, name, value); buffer->length += strlen(bp); } /** * Add a two-column row for a double floating point * * \param buffer where the text is appended * \param title is displayed in column one * \param name is returned by the browser * \param value is displayed in column two and returned by the browser * * The title is displayed in column one and the double in column two. The * name=value is returned by the browser. */ static void add_double(BUFFER* buffer, const char* title, const char* name, double value) { char* bp = &buffer->body[buffer->length]; snprintf(bp, sizeof(buffer->body) - buffer->length, "%s" "" "\r\n", title, name, value); buffer->length += strlen(bp); } /** * Add a two-column row for a counter value * * \param buffer where the text is appended * \param title is displayed in column one * \param name is returned by the browser * \param value is displayed in column two and returned by the browser * * The title is displayed in column one and the counter value in column two. * The name=value is returned by the browser. */ static void add_counter(BUFFER* buffer, const char* title, const char* name, uint64 value) { char* bp = &buffer->body[buffer->length]; snprintf(bp, sizeof(buffer->body) - buffer->length, "%s" "" "\r\n", title, name, value); buffer->length += strlen(bp); } /** * Display the counter control form on socket * * \param n index of socket * * The counter control form has fields to display and change counter control * parameters. It contains buttons to update the counter parameters or reset * them to the last displayed values. */ void put_form(int n) { dbg_printf(0, "put_form\n"); BUFFER html; BUFFER buffer; DEVICE* device = (DEVICE*) sock_device(n); PARAMETERS* pp = &device->params; char *bp; buffer.length = 0; bp = &buffer.body[buffer.length]; snprintf(bp, sizeof(buffer.body) - buffer.length, "Device Form\r\n" "\r\n" "
\r\n" "\r\n" "\r\n" "\r\n"); buffer.length += strlen(bp); add_int(&buffer, "Scan", "scan", pp->poll_period); add_int(&buffer, "Sample", "sample", pp->sample_period); add_int(&buffer, "Report", "report", pp->report_period); bp = &buffer.body[buffer.length]; snprintf(bp, sizeof(buffer.body) - buffer.length, "" "
FieldValue
\r\n" "\r\n" "" "\r\n" "
\r\n" "
\r\n" "\r\n" "\r\n" ); buffer.length += strlen(bp); bp = &buffer.body[buffer.length]; html.length = 0; bp = &html.body[html.length]; snprintf(bp, sizeof(html.body) - html.length, "HTTP/1.1 200 OK\r\n" "Content-Type: text/html\r\n" "Content-Length: %d\r\n" "\r\n", buffer.length); strcat(html.body, buffer.body); html.length = strlen(html.body); sock_send(n, &html); } static void show_text(BUFFER* buffer, const char* title, const char* value) { char* bp = &buffer->body[buffer->length]; snprintf(bp, sizeof(buffer->body) - buffer->length, "%s" "%s" "\r\n", title, value); buffer->length += strlen(bp); } static void show_int(BUFFER* buffer, const char* title, int value) { char* bp = &buffer->body[buffer->length]; snprintf(bp, sizeof(buffer->body) - buffer->length, "%s" "%d" "\r\n", title, value); buffer->length += strlen(bp); } static void show_hex(BUFFER* buffer, const char* title, int value) { char* bp = &buffer->body[buffer->length]; snprintf(bp, sizeof(buffer->body) - buffer->length, "%s" "0x%06X" "\r\n", title, value & 0xffffff); buffer->length += strlen(bp); } static void show_real(BUFFER* buffer, const char* title, double value) { char* bp = &buffer->body[buffer->length]; snprintf(bp, sizeof(buffer->body) - buffer->length, "%s" "%.2f" "\r\n", title, value); buffer->length += strlen(bp); } static void show_counter(BUFFER* buffer, const char* title, unsigned long long value) { char* bp = &buffer->body[buffer->length]; snprintf(bp, sizeof(buffer->body) - buffer->length, "%s" "%llu" "\r\n", title, value); buffer->length += strlen(bp); } static void show_time(BUFFER* buffer, const char* title, struct timeval* value, bool days_flag) { char time_str[40]; time_str[0] = '\0'; if (days_flag) { snprintf(time_str, sizeof(time_str), "%ld ", value->tv_sec / (24 * 3600)); } strcat(time_str, make_timestamp(value)); show_text(buffer, title, time_str); } /** * Display the counter display page on socket * * \param n index of socket * * The counter display page shows the state of the counter and contains * buttons which allow commands to be issued to the counter. It will be * periodically refreshed. */ void put_page(int n) { dbg_printf(0, "put_page\n"); BUFFER html; BUFFER buffer; DEVICE* device = (DEVICE*) sock_device(n); PARAMETERS* pp = &device->params; char *bp; int refresh; #if 1 { refresh = (int) device_time_to_next_report(device) + 1; } #else make_report(device); refresh = (pp->poll_period * pp->sample_period * pp->report_period + 500) / 1000; #endif if (refresh < MIN_REFRESH) refresh = MIN_REFRESH; if (refresh > MAX_REFRESH) refresh = MAX_REFRESH; buffer.length = 0; bp = &buffer.body[buffer.length]; snprintf(bp, sizeof(buffer.body) - buffer.length, "Device Page\r\n" "\r\n" "\r\n"); add_text(&buffer, "
\r\n" "\r\n" "\r\n" "\r\n"); buffer.length += strlen(bp); show_text(&buffer, "State", device->state == device_stopped ? "STOPPED" : device->state == device_running ? "RUNNING" : device->state == device_paused ? "PAUSED" : "IDLE"); show_text(&buffer, "Direction", pp->direction == COUNT_UP ? "UP" : "DOWN"); show_int(&buffer, "Scan", pp->poll_period); show_int(&buffer, "Sample", pp->sample_period); show_int(&buffer, "Report", pp->report_period); show_time(&buffer, "Current Time", &device->current_time, false); show_hex(&buffer, "Value", device->value); show_int(&buffer, "Poll Counter", device->poll_counter); add_text(&buffer, "
FieldValue
\r\n"); add_text(&buffer, "\r\n"); add_text(&buffer, "
\r\n" "Form\r\n" "
\r\n" "\r\n" "\r\n"); html.length = 0; bp = &html.body[html.length]; snprintf(bp, sizeof(html.body) - html.length, "HTTP/1.1 200 OK\r\n" "Content-Type: text/html\r\n" "Content-Length: %d\r\n" "Refresh: %d\r\n" "\r\n", buffer.length, refresh); strcat(html.body, buffer.body); html.length = strlen(html.body); sock_send(n, &html); } /** * Process the control form in the buffer on the socket * * \param n index of socket * \param bp buffer containing form input * * The form is sent by a browser and contains name=value pairs which are used * to set the counter parameters. */ void process_form(int n, BUFFER* bp) { dbg_printf(0, "process_form\n"); DEVICE* device = (DEVICE*) sock_device(n); int i, j; int state; char name[80]; char value[80]; char hex_str[3]; unsigned int hex_val; char* tp; state = 0; j = 0; for (i = 0; i < bp->length; ++i) { tp = &bp->body[i]; if (state == 0) { if (*tp == '=') { name[j++] = '\0'; j = 0; state = 1; } else if (j < 80 - 1) name[j++] = tolower(*tp); } else if (state == 1) { if (*tp == '&') { value[j++] = '\0'; j = 0; state = 9; } else if (*tp == '%') { hex_str[0] = '\0'; state = 2; } else if (j < 80 - 1) value[j++] = tolower(*tp); } else if (state == 2) { hex_str[0] = tolower(*tp); state = 3; } else if (state == 3) { hex_str[1] = tolower(*tp); hex_str[2] = '\0'; hex_val = strtoul(hex_str, NULL, 16); if (hex_val < ' ' || hex_val > '~') hex_val = '?'; if (j < 80 - 1) value[j++] = tolower(hex_val); state = 1; } if (state == 9) { dbg_printf(0, "name=\"%s\", value=\"%s\"\n", name, value); if (param_set(&device->params, name, value)) ; else if (strcmp(name, "xxx")) { } state = 0; } } } /** * Cause the browser to refresh the page * * \param n index of socket * * Used after a command button to redirect to the display page. Uses * HTTP 303 REDIRECT to get the browser to display the page. */ void put_page_refresh(int n) { dbg_printf(0, "put_page_refresh\n"); BUFFER html; BUFFER buffer; char* bp; DEVICE* device = (DEVICE*) sock_device(n); int refresh; #if 1 { refresh = (int) device_time_to_next_report(device) + 1; } #else PARAMETERS* pp = &device->params; make_report(device); refresh = (pp->poll_period * pp->sample_period * pp->report_period + 500) / 1000; #endif if (refresh < MIN_REFRESH) refresh = MIN_REFRESH; if (refresh > MAX_REFRESH) refresh = MAX_REFRESH; buffer.length = 0; add_text(&buffer, "\r\n" "\r\n" "Title\r\n" "\r\n" "\r\n"); add_text(&buffer, "\r\n" "\r\n"); html.length = 0; bp = &html.body[html.length]; snprintf(bp, sizeof(html.body) - html.length, "HTTP/1.1 303 REDIRECT\r\n" "Location: page\r\n" "Content-Type: text/html\r\n" "Content-Length: %d\r\n" "\r\n", buffer.length); strcat(html.body, buffer.body); html.length = strlen(html.body); sock_send(n, &html); } /** * Cause the browser to refresh the form * * \param n index of socket * * Used after a command button to redirect to the control form. Uses * HTTP 303 REDIRECT to get the browser to display the form. */ void put_form_refresh(int n) { dbg_printf(0, "put_form_refresh\n"); DEVICE* device = (DEVICE*) sock_device(n); BUFFER html; BUFFER buffer; char* bp; int refresh; #if 1 { refresh = (int) device_time_to_next_report(device) + 1; } #else PARAMETERS* pp = &device->params; make_report(device); refresh = (pp->poll_period * pp->sample_period * pp->report_period + 500) / 1000; #endif if (refresh < MIN_REFRESH) refresh = MIN_REFRESH; if (refresh > MAX_REFRESH) refresh = MAX_REFRESH; buffer.length = 0; add_text(&buffer, "\r\n" "\r\n" "Title\r\n" "\r\n" "\r\n"); add_text(&buffer, "\r\n" "\r\n"); html.length = 0; bp = &html.body[html.length]; snprintf(bp, sizeof(html.body) - html.length, "HTTP/1.1 303 REDIRECT\r\n" "Location: form\r\n" "Content-Type: text/html\r\n" "Content-Length: %d\r\n" "\r\n", buffer.length); strcat(html.body, buffer.body); html.length = strlen(html.body); sock_send(n, &html); } /** * Process the command in the buffer on the socket * * \param n index of socket * \param bp buffer containing command * * Handles non-browser commands like SICS commands. */ void process_command(int n, BUFFER* bp) { dbg_printf(0, "process_command(%d, %s)\n", n, bp->body); DEVICE* device = (DEVICE*) sock_device(n); bool sics = false; int error = 1; char command[80]; char param[80]; int len = 0; char* tp = bp->body; while (isspace(*tp)) ++tp; len = 0; while (*tp && !isspace(*tp)) { if (len < 80 - 1) command[len++] = *tp; ++tp; } command[len] = '\0'; if (strcasecmp(command, "SICS") == 0) { sics = true; while (isspace(*tp)) ++tp; len = 0; while (*tp && !isspace(*tp)) { if (len < 80 - 1) command[len++] = *tp; ++tp; } command[len] = '\0'; } while (isspace(*tp)) ++tp; if (strcasecmp(command, "REPORT") == 0) { int match = 1; error = 0; if (sics) match = 2; len = 0; while (*tp && !isspace(*tp)) { if (len < 80 - 1) param[len++] = *tp; ++tp; } param[len] = '\0'; if (strcasecmp(param, "OFF") == 0) match = 0; sock_set_match(n, match); } else if (strcasecmp(command, "SET") == 0) { /* set parameter */ dbg_printf(0, "SET %s\n", tp); if (param_set_cmd(&device->params, tp)) error = 0; } else if (strcasecmp(command, "GET") == 0) { /* get parameter */ param_get_cmd(&device->params, tp, n); return; } else if (strcasecmp(command, "READ") == 0) { device_read(device, n); return; } else if (strcasecmp(command, "WRITE") == 0) { device_write(device, n, tp); return; } else if (!sics) { device_send(device, n); return; } if (error == 0) sock_ok(n); else sock_err(n); }