#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, char* title, 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, 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, char* title, 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, char* title, 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, char* title, 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, "Counter Form\r\n" "\r\n" "
\r\n" "\r\n" "\r\n" "\r\n"); buffer.length += strlen(bp); add_select_begin(&buffer, "Direction", "direction"); add_option(&buffer, "Up", "up", pp->direction == COUNT_UP); add_option(&buffer, "Down", "down", !pp->direction == COUNT_UP); add_select_end(&buffer); add_int(&buffer, "Scan", "scan", pp->poll_period); add_int(&buffer, "Sample", "sample", pp->sample_period); add_int(&buffer, "Report", "report", pp->report_period); add_counter(&buffer, "Initial", "initial", pp->initial_count); add_counter(&buffer, "Terminal", "terminal", pp->terminal_count); add_select_begin(&buffer, "Terminal Event", "te_check"); add_option(&buffer, "None", "none", pp->terminal_check_type == 0); add_option(&buffer, "Counter", "counter", pp->terminal_check_type == 1); add_option(&buffer, "Timer", "timer", pp->terminal_check_type == 2); add_select_end(&buffer); add_double(&buffer, "Range Low", "range_low", pp->range_low); add_double(&buffer, "Range High", "range_high", pp->range_high); add_select_begin(&buffer, "Range Event", "re_check"); add_option(&buffer, "Disabled", "disabled", pp->range_check_enable == false); add_option(&buffer, "Enabled", "enabled", pp->range_check_enable == true); add_select_end(&buffer); add_select_begin(&buffer, "Range Gate", "range_gate"); add_option(&buffer, "Disabled", "disabled", pp->range_gate_enable == false); add_option(&buffer, "Enabled", "enabled", pp->range_gate_enable == true); add_select_end(&buffer); add_select_begin(&buffer, "Range Mode", "range_mode"); add_option(&buffer, "Report", "0", pp->range_mode == 0); add_option(&buffer, "Sample", "1", pp->range_mode == 1); add_option(&buffer, "Poll", "2", pp->range_mode == 2); add_select_end(&buffer); add_select_begin(&buffer, "Source", "source"); add_option(&buffer, "External", "0", pp->source == 0); add_option(&buffer, "Clock_1 20MHz", "1", pp->source == 1); add_option(&buffer, "Clock_2 100KHz", "2", pp->source == 2); add_option(&buffer, "Clock_3 80MHz", "3", pp->source == 3); add_select_end(&buffer); add_select_begin(&buffer, "Filter", "filter"); add_option(&buffer, "None", "0", pp->filter == 0); add_option(&buffer, "Filter_1 SyncT3", "1", pp->filter == 1); add_option(&buffer, "Filter_2 5000nS", "2", pp->filter == 2); add_option(&buffer, "Filter_3 1000nS", "3", pp->filter == 3); add_option(&buffer, "Filter_4 500nS", "4", pp->filter == 4); add_option(&buffer, "Filter_5 100nS", "5", pp->filter == 5); add_option(&buffer, "Filter_6 25nS", "6", pp->filter == 6); add_select_end(&buffer); add_select_begin(&buffer, "Output Line", "output"); add_option(&buffer, "Disabled", "disabled", pp->output_line == 0); add_option(&buffer, "Enabled", "enabled", pp->output_line == 1); add_option(&buffer, "Inverted", "inverted", pp->output_line == 2); add_select_end(&buffer); add_select_begin(&buffer, "Sync", "sync"); add_option(&buffer, "Internal", "internal", pp->sync == false); add_option(&buffer, "External", "external", pp->sync == true); add_select_end(&buffer); 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, 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_real(BUFFER* buffer, 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, 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, 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; SAMPLE* sp; 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; sp = &device->report; buffer.length = 0; bp = &buffer.body[buffer.length]; snprintf(bp, sizeof(buffer.body) - buffer.length, "Counter 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, "Start Time", &device->start_time, false); show_time(&buffer, "Current Time", &device->current_time, false); { struct timeval tv = device->stop_time; if (device->state == device_running || device->state == device_paused) { tv = device->current_time; } time_sub(&tv, &device->start_time); show_time(&buffer, "Elapsed", &tv, true); } show_time(&buffer, "Runtime", &device->accumulated, true); show_time(&buffer, "Report Time", &sp->timestamp, false); show_counter(&buffer, "Counter", sp->counter_value); show_int(&buffer, "Num Polls", sp->num_polls); show_int(&buffer, "Poll Number", sp->poll_counter); show_int(&buffer, "Sample Number", sp->sample_counter); show_real(&buffer, "Rate", sp->counter_rate); show_real(&buffer, "Min", sp->minimum_rate); show_real(&buffer, "Ave", sp->average_rate); show_real(&buffer, "Max", sp->maximum_rate); if (pp->range_check_enable) { char* result; if (device->range_error == 1) result = "LOW"; else if (device->range_error == 2) result = "HIGH"; else result = "OK"; show_text(&buffer, "Range Check", result); } else show_text(&buffer, "Range Check", "Disabled"); add_text(&buffer, "
FieldValue
\r\n"); add_text(&buffer, "\r\n"); add_text(&buffer, "\r\n"); add_text(&buffer, "\r\n"); add_text(&buffer, "\r\n"); add_text(&buffer, "\r\n"); add_text(&buffer, "
\r\n" "Form\r\n" "
\r\n" "Start\r\n" "
\r\n" "Stop\r\n" "
\r\n" "Pause\r\n" "
\r\n" "Continue\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, "START") == 0) error = device_start(device); else if (strcasecmp(command, "STOP") == 0) error = device_stop(device); else if (strcasecmp(command, "PAUSE") == 0) error = device_pause(device); else if (strcasecmp(command, "RESUME") == 0) error = device_resume(device); else 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 (!sics) { device_send(device, n); return; } if (error == 0) sock_ok(n); else sock_err(n); }